From 13579d28f14c539117dd36af2e72f0c2d9574187 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 04:24:32 +0200 Subject: [PATCH 01/24] activate backend module --- backend/config/web.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/config/web.php b/backend/config/web.php index fec4db012..376bee37a 100644 --- a/backend/config/web.php +++ b/backend/config/web.php @@ -14,6 +14,9 @@ '@npm' => '@vendor/npm-asset', ], 'modules' => [ + 'speedprogramming' => [ + 'class' => 'app\modules\speedprogramming\Module', + ], 'sales' => [ 'class' => 'app\modules\sales\Module', ], From 9534aed1a1d43dcbae09a9d9bd37fc97a60d857a Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 04:25:08 +0200 Subject: [PATCH 02/24] add migrations for initial tables --- ...1105_014128_create_speed_problem_table.php | 33 ++++++ ...105_014129_create_speed_solution_table.php | 106 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 backend/migrations/m241105_014128_create_speed_problem_table.php create mode 100644 backend/migrations/m241105_014129_create_speed_solution_table.php diff --git a/backend/migrations/m241105_014128_create_speed_problem_table.php b/backend/migrations/m241105_014128_create_speed_problem_table.php new file mode 100644 index 000000000..a711c2af2 --- /dev/null +++ b/backend/migrations/m241105_014128_create_speed_problem_table.php @@ -0,0 +1,33 @@ +createTable('{{%speed_problem}}', [ + 'id' => $this->primaryKey(), + 'name' => $this->string(), + 'description' => $this->text(), + 'challenge_image' => $this->string(), + 'validator_image' => $this->string(), + 'created_at' => $this->datetime(), + 'updated_at' => $this->datetime(), + ]); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropTable('{{%speed_problem}}'); + } +} diff --git a/backend/migrations/m241105_014129_create_speed_solution_table.php b/backend/migrations/m241105_014129_create_speed_solution_table.php new file mode 100644 index 000000000..d07bbf238 --- /dev/null +++ b/backend/migrations/m241105_014129_create_speed_solution_table.php @@ -0,0 +1,106 @@ +createTable('{{%speed_solution}}', [ + 'id' => $this->primaryKey(), + 'player_id' => $this->integer()->unsigned()->notNull(), + 'problem_id' => $this->integer()->notNull(), + 'language' => $this->string(), + 'sourcecode' => 'LONGBLOB', + 'status' => $this->string(), + 'points' => $this->integer()->defaultValue(0), + 'created_at' => $this->datetime(), + 'updated_at' => $this->datetime(), + ]); + + // creates index for column `player_id` + $this->createIndex( + '{{%idx-speed_solution-player_id}}', + '{{%speed_solution}}', + 'player_id' + ); + + // add foreign key for table `{{%player}}` + $this->addForeignKey( + '{{%fk-speed_solution-player_id}}', + '{{%speed_solution}}', + 'player_id', + '{{%player}}', + 'id', + 'CASCADE' + ); + + // creates index for column `problem_id` + $this->createIndex( + '{{%idx-speed_solution-problem_id}}', + '{{%speed_solution}}', + 'problem_id' + ); + + // add foreign key for table `{{%speed_problem}}` + $this->addForeignKey( + '{{%fk-speed_solution-problem_id}}', + '{{%speed_solution}}', + 'problem_id', + '{{%speed_problem}}', + 'id', + 'CASCADE' + ); + + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + // drops foreign key for table `{{%player}}` + $this->dropForeignKey( + '{{%fk-speed_solution-player_id}}', + '{{%speed_solution}}' + ); + + // drops index for column `player_id` + $this->dropIndex( + '{{%idx-speed_solution-player_id}}', + '{{%speed_solution}}' + ); + + // drops foreign key for table `{{%team}}` + $this->dropForeignKey( + '{{%fk-speed_solution-team_id}}', + '{{%speed_solution}}' + ); + + // drops index for column `team_id` + $this->dropIndex( + '{{%idx-speed_solution-team_id}}', + '{{%speed_solution}}' + ); + + // drops foreign key for table `{{%target}}` + $this->dropForeignKey( + '{{%fk-speed_solution-target_id}}', + '{{%speed_solution}}' + ); + + // drops index for column `target_id` + $this->dropIndex( + '{{%idx-speed_solution-target_id}}', + '{{%speed_solution}}' + ); + + $this->dropTable('{{%speed_solution}}'); + } +} From f573c74469071699aed25885a9c8cec49b2834d2 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 04:25:32 +0200 Subject: [PATCH 03/24] add to menu --- backend/views/layouts/main.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/backend/views/layouts/main.php b/backend/views/layouts/main.php index 38e9e23d3..cc2c4d9c0 100644 --- a/backend/views/layouts/main.php +++ b/backend/views/layouts/main.php @@ -86,7 +86,6 @@ ['label' => 'VPN Templates', 'url' => ['/content/vpn-template/index'], 'visible' => !Yii::$app->user->isGuest,], ] ], - [ 'label' => ' Sales', 'url' => ['/sales/default/index'], @@ -103,6 +102,17 @@ ['label' => 'Webhook', 'url' => ['/sales/stripe-webhook/index'], 'visible' => !Yii::$app->user->isGuest && Yii::$app->user->identity->isAdmin && array_key_exists('sales', \Yii::$app->modules) !== false,], ] ], + [ + 'label' => ' Speed', + 'url' => ['/speedprogramming/default/index'], + 'icon' => 'fas fa-money-check-alt', + 'active' => Yii::$app->controller->module->id == 'speedprogramming', + 'visible' => array_key_exists('speedprogramming', \Yii::$app->modules) !== false && !Yii::$app->user->isGuest && Yii::$app->user->identity->isAdmin, + 'items' => [ + ['label' => 'Problems', 'url' => ['/speedprogramming/speed-problem/index'], 'visible' => !Yii::$app->user->isGuest && Yii::$app->user->identity->isAdmin && array_key_exists('sales', \Yii::$app->modules) !== false,], + ['label' => 'Solutions', 'url' => ['/speedprogramming/default/index'], 'visible' => !Yii::$app->user->isGuest && Yii::$app->user->identity->isAdmin && array_key_exists('sales', \Yii::$app->modules) !== false,], + ] + ], [ 'label' => ' Activity', 'url' => ['/activity/default/index'], From 08570e4d5028c0ba2cf682b1c9bcbe00edf528a4 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 04:28:10 +0200 Subject: [PATCH 04/24] add initial speed programming module --- backend/modules/speedprogramming/Module.php | 24 +++ .../controllers/SpeedProblemController.php | 134 +++++++++++++++ .../speedprogramming/models/SpeedProblem.php | 76 +++++++++ .../models/SpeedProblemQuery.php | 34 ++++ .../models/SpeedProblemSearch.php | 73 +++++++++ .../speedprogramming/models/SpeedSolution.php | 155 ++++++++++++++++++ .../models/SpeedSolutionQuery.php | 34 ++++ .../models/SpeedSolutionSearch.php | 75 +++++++++ .../views/speed-problem/_form.php | 33 ++++ .../views/speed-problem/_search.php | 42 +++++ .../views/speed-problem/create.php | 20 +++ .../views/speed-problem/index.php | 51 ++++++ .../views/speed-problem/update.php | 23 +++ .../views/speed-problem/view.php | 42 +++++ 14 files changed, 816 insertions(+) create mode 100644 backend/modules/speedprogramming/Module.php create mode 100644 backend/modules/speedprogramming/controllers/SpeedProblemController.php create mode 100644 backend/modules/speedprogramming/models/SpeedProblem.php create mode 100644 backend/modules/speedprogramming/models/SpeedProblemQuery.php create mode 100644 backend/modules/speedprogramming/models/SpeedProblemSearch.php create mode 100644 backend/modules/speedprogramming/models/SpeedSolution.php create mode 100644 backend/modules/speedprogramming/models/SpeedSolutionQuery.php create mode 100644 backend/modules/speedprogramming/models/SpeedSolutionSearch.php create mode 100644 backend/modules/speedprogramming/views/speed-problem/_form.php create mode 100644 backend/modules/speedprogramming/views/speed-problem/_search.php create mode 100644 backend/modules/speedprogramming/views/speed-problem/create.php create mode 100644 backend/modules/speedprogramming/views/speed-problem/index.php create mode 100644 backend/modules/speedprogramming/views/speed-problem/update.php create mode 100644 backend/modules/speedprogramming/views/speed-problem/view.php diff --git a/backend/modules/speedprogramming/Module.php b/backend/modules/speedprogramming/Module.php new file mode 100644 index 000000000..0bcaabbd0 --- /dev/null +++ b/backend/modules/speedprogramming/Module.php @@ -0,0 +1,24 @@ + [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'delete' => ['POST'], + ], + ], + ] + ); + } + + /** + * Lists all SpeedProblem models. + * + * @return string + */ + public function actionIndex() + { + $searchModel = new SpeedProblemSearch(); + $dataProvider = $searchModel->search($this->request->queryParams); + + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } + + /** + * Displays a single SpeedProblem model. + * @param int $id ID + * @return string + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new SpeedProblem model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return string|\yii\web\Response + */ + public function actionCreate() + { + $model = new SpeedProblem(); + + if ($this->request->isPost) { + if ($model->load($this->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); + } + } else { + $model->loadDefaultValues(); + } + + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing SpeedProblem model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param int $id ID + * @return string|\yii\web\Response + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($this->request->isPost && $model->load($this->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); + } + + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing SpeedProblem model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param int $id ID + * @return \yii\web\Response + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the SpeedProblem model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param int $id ID + * @return SpeedProblem the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = SpeedProblem::findOne(['id' => $id])) !== null) { + return $model; + } + + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } +} diff --git a/backend/modules/speedprogramming/models/SpeedProblem.php b/backend/modules/speedprogramming/models/SpeedProblem.php new file mode 100644 index 000000000..c71ad9e5b --- /dev/null +++ b/backend/modules/speedprogramming/models/SpeedProblem.php @@ -0,0 +1,76 @@ + 255], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('app', 'ID'), + 'name' => Yii::t('app', 'Name'), + 'description' => Yii::t('app', 'Description'), + 'challenge_image' => Yii::t('app', 'Challenge Image'), + 'validator_image' => Yii::t('app', 'Validator Image'), + 'created_at' => Yii::t('app', 'Created At'), + 'updated_at' => Yii::t('app', 'Updated At'), + ]; + } + + /** + * Gets query for [[SpeedSolutions]]. + * + * @return \yii\db\ActiveQuery|SpeedSolutionQuery + */ + public function getSpeedSolutions() + { + return $this->hasMany(SpeedSolution::class, ['problem_id' => 'id']); + } + + /** + * {@inheritdoc} + * @return SpeedProblemQuery the active query used by this AR class. + */ + public static function find() + { + return new SpeedProblemQuery(get_called_class()); + } +} diff --git a/backend/modules/speedprogramming/models/SpeedProblemQuery.php b/backend/modules/speedprogramming/models/SpeedProblemQuery.php new file mode 100644 index 000000000..0fe19370b --- /dev/null +++ b/backend/modules/speedprogramming/models/SpeedProblemQuery.php @@ -0,0 +1,34 @@ +andWhere('[[status]]=1'); + }*/ + + /** + * {@inheritdoc} + * @return SpeedProblem[]|array + */ + public function all($db = null) + { + return parent::all($db); + } + + /** + * {@inheritdoc} + * @return SpeedProblem|array|null + */ + public function one($db = null) + { + return parent::one($db); + } +} diff --git a/backend/modules/speedprogramming/models/SpeedProblemSearch.php b/backend/modules/speedprogramming/models/SpeedProblemSearch.php new file mode 100644 index 000000000..a7df59dd9 --- /dev/null +++ b/backend/modules/speedprogramming/models/SpeedProblemSearch.php @@ -0,0 +1,73 @@ + $query, + ]); + + $this->load($params); + + if (!$this->validate()) { + // uncomment the following line if you do not want to return any records when validation fails + // $query->where('0=1'); + return $dataProvider; + } + + // grid filtering conditions + $query->andFilterWhere([ + 'id' => $this->id, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]); + + $query->andFilterWhere(['like', 'name', $this->name]) + ->andFilterWhere(['like', 'description', $this->description]) + ->andFilterWhere(['like', 'challenge_image', $this->challenge_image]) + ->andFilterWhere(['like', 'validator_image', $this->validator_image]); + + return $dataProvider; + } +} diff --git a/backend/modules/speedprogramming/models/SpeedSolution.php b/backend/modules/speedprogramming/models/SpeedSolution.php new file mode 100644 index 000000000..f4bba4f0f --- /dev/null +++ b/backend/modules/speedprogramming/models/SpeedSolution.php @@ -0,0 +1,155 @@ + TimestampBehavior::class, + 'createdAtAttribute' => 'created_at', + 'updatedAtAttribute' => 'updated_at', + 'value' => new Expression('NOW()'), + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function rules() + { + return [ + [['player_id','points'], 'filter','filter'=>'intval'], + [['player_id'], 'required'], + [['player_id', 'points'], 'integer'], + [['sourcecode'], 'string'], + [['created_at', 'updated_at'], 'safe'], + [['language', 'status'], 'string', 'max' => 255], + [['player_id'], 'exist', 'skipOnError' => true, 'targetClass' => Player::class, 'targetAttribute' => ['player_id' => 'id']], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'player_id' => 'Player ID', + 'language' => 'Language', + 'sourcecode' => 'Sourcecode', + 'status' => 'Status', + 'points' => 'Points', + 'created_at' => 'Created At', + 'updated_at' => 'Updated At', + ]; + } + + /** + * Gets query for [[Player]]. + * + * @return \yii\db\ActiveQuery|PlayerQuery + */ + public function getPlayer() + { + return $this->hasOne(Player::class, ['id' => 'player_id']); + } + + /** + * Gets query for [[Player]]. + * + * @return \yii\db\ActiveQuery|PlayerQuery + */ + public function getProblem() + { + return $this->hasOne(SpeedProblem::class, ['id' => 'problem_id']); + } + + /** + * {@inheritdoc} + * @return SpeedSolutionQuery the active query used by this AR class. + */ + public static function find() + { + return new SpeedSolutionQuery(get_called_class()); + } + + public function afterSave($insert, $changedAttributes) + { + parent::afterSave($insert, $changedAttributes); + if((isset($changedAttributes['status']) || isset($changedAttributes['points'])) + && (($this->status==='approved' && $this->points>0) || $this->status==='rejected')) + { + $stream=new Stream(); + $stream->player_id=$this->player_id; + $stream->model='solution'; + $stream->model_id=$this->target_id; + $stream->points=$this->points; + $stream->title='submission for '.$this->target->name.' got '.$this->status; + $stream->message=$stream->pubmessage=$stream->pubtitle=$stream->title; + return $stream->save(); + } + } + public static function getLanguages() + { + return [ + 'c'=>'C', + 'cpp'=>'C++', + 'cs'=>'C#', + 'py2'=>'Python 2.x', + 'py3'=>'Python 3.x', + 'php7'=>'PHP 7.x', + 'java'=>'Java' + ]; + } + public static function getStatuses() + { + return ['pending'=>'pending','approved'=>'approved','rejected'=>'rejected','invalid'=>'invalid']; + } + + public function approve() + { + $this->status='approved'; + return $this->save(); + } + public function reject() + { + $this->status='rejected'; + $this->points=0; + return $this->save(); + } + +} diff --git a/backend/modules/speedprogramming/models/SpeedSolutionQuery.php b/backend/modules/speedprogramming/models/SpeedSolutionQuery.php new file mode 100644 index 000000000..248891dac --- /dev/null +++ b/backend/modules/speedprogramming/models/SpeedSolutionQuery.php @@ -0,0 +1,34 @@ +andWhere('[[status]]=1'); + }*/ + + /** + * {@inheritdoc} + * @return SpeedSolution[]|array + */ + public function all($db = null) + { + return parent::all($db); + } + + /** + * {@inheritdoc} + * @return SpeedSolution|array|null + */ + public function one($db = null) + { + return parent::one($db); + } +} diff --git a/backend/modules/speedprogramming/models/SpeedSolutionSearch.php b/backend/modules/speedprogramming/models/SpeedSolutionSearch.php new file mode 100644 index 000000000..01b7f25ec --- /dev/null +++ b/backend/modules/speedprogramming/models/SpeedSolutionSearch.php @@ -0,0 +1,75 @@ + $query, + ]); + + $this->load($params); + + if (!$this->validate()) { + // uncomment the following line if you do not want to return any records when validation fails + // $query->where('0=1'); + return $dataProvider; + } + + // grid filtering conditions + $query->andFilterWhere([ + 'id' => $this->id, + 'player_id' => $this->player_id, + 'problem_id' => $this->problem_id, + 'points' => $this->points, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]); + + $query->andFilterWhere(['like', 'language', $this->language]) + ->andFilterWhere(['like', 'sourcecode', $this->sourcecode]) + ->andFilterWhere(['like', 'status', $this->status]); + + return $dataProvider; + } +} diff --git a/backend/modules/speedprogramming/views/speed-problem/_form.php b/backend/modules/speedprogramming/views/speed-problem/_form.php new file mode 100644 index 000000000..3219f39a2 --- /dev/null +++ b/backend/modules/speedprogramming/views/speed-problem/_form.php @@ -0,0 +1,33 @@ + + +
+ + + + field($model, 'name')->textInput(['maxlength' => true]) ?> + + field($model, 'description')->textarea(['rows' => 6]) ?> + + field($model, 'challenge_image')->textInput(['maxlength' => true]) ?> + + field($model, 'validator_image')->textInput(['maxlength' => true]) ?> + + field($model, 'created_at')->textInput() ?> + + field($model, 'updated_at')->textInput() ?> + +
+ 'btn btn-success']) ?> +
+ + + +
diff --git a/backend/modules/speedprogramming/views/speed-problem/_search.php b/backend/modules/speedprogramming/views/speed-problem/_search.php new file mode 100644 index 000000000..b2ec1f20b --- /dev/null +++ b/backend/modules/speedprogramming/views/speed-problem/_search.php @@ -0,0 +1,42 @@ + + + diff --git a/backend/modules/speedprogramming/views/speed-problem/create.php b/backend/modules/speedprogramming/views/speed-problem/create.php new file mode 100644 index 000000000..7e9d72e04 --- /dev/null +++ b/backend/modules/speedprogramming/views/speed-problem/create.php @@ -0,0 +1,20 @@ +title = Yii::t('app', 'Create Speed Problem'); +$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Speed Problems'), 'url' => ['index']]; +$this->params['breadcrumbs'][] = $this->title; +?> +
+ +

title) ?>

+ + render('_form', [ + 'model' => $model, + ]) ?> + +
diff --git a/backend/modules/speedprogramming/views/speed-problem/index.php b/backend/modules/speedprogramming/views/speed-problem/index.php new file mode 100644 index 000000000..43fc487e4 --- /dev/null +++ b/backend/modules/speedprogramming/views/speed-problem/index.php @@ -0,0 +1,51 @@ +title = Yii::t('app', 'Speed Problems'); +$this->params['breadcrumbs'][] = $this->title; +?> +
+ +

title) ?>

+ +

+ 'btn btn-success']) ?> +

+ + + render('_search', ['model' => $searchModel]); ?> + + $dataProvider, + 'filterModel' => $searchModel, + 'columns' => [ + ['class' => 'yii\grid\SerialColumn'], + + 'id', + 'name', + 'description:ntext', + 'challenge_image', + 'validator_image', + 'created_at', + 'updated_at', + [ + 'class' => ActionColumn::className(), + 'urlCreator' => function ($action, SpeedProblem $model, $key, $index, $column) { + return Url::toRoute([$action, 'id' => $model->id]); + } + ], + ], + ]); ?> + + + +
diff --git a/backend/modules/speedprogramming/views/speed-problem/update.php b/backend/modules/speedprogramming/views/speed-problem/update.php new file mode 100644 index 000000000..e53cd289a --- /dev/null +++ b/backend/modules/speedprogramming/views/speed-problem/update.php @@ -0,0 +1,23 @@ +title = Yii::t('app', 'Update Speed Problem: {name}', [ + 'name' => $model->name, +]); +$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Speed Problems'), 'url' => ['index']]; +$this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->id]]; +$this->params['breadcrumbs'][] = Yii::t('app', 'Update'); +?> +
+ +

title) ?>

+ + render('_form', [ + 'model' => $model, + ]) ?> + +
diff --git a/backend/modules/speedprogramming/views/speed-problem/view.php b/backend/modules/speedprogramming/views/speed-problem/view.php new file mode 100644 index 000000000..aeebc2007 --- /dev/null +++ b/backend/modules/speedprogramming/views/speed-problem/view.php @@ -0,0 +1,42 @@ +title = $model->name; +$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Speed Problems'), 'url' => ['index']]; +$this->params['breadcrumbs'][] = $this->title; +\yii\web\YiiAsset::register($this); +?> +
+ +

title) ?>

+ +

+ $model->id], ['class' => 'btn btn-primary']) ?> + $model->id], [ + 'class' => 'btn btn-danger', + 'data' => [ + 'confirm' => Yii::t('app', 'Are you sure you want to delete this item?'), + 'method' => 'post', + ], + ]) ?> +

+ + $model, + 'attributes' => [ + 'id', + 'name', + 'description:ntext', + 'challenge_image', + 'validator_image', + 'created_at', + 'updated_at', + ], + ]) ?> + +
From 3795775a1e06bd7349f9f5b934e1b079a3c9eb2f Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 04:35:42 +0200 Subject: [PATCH 05/24] add server to connect to (currently docker only) --- backend/migrations/m241105_014128_create_speed_problem_table.php | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/migrations/m241105_014128_create_speed_problem_table.php b/backend/migrations/m241105_014128_create_speed_problem_table.php index a711c2af2..7736e1bf3 100644 --- a/backend/migrations/m241105_014128_create_speed_problem_table.php +++ b/backend/migrations/m241105_014128_create_speed_problem_table.php @@ -16,6 +16,7 @@ public function safeUp() 'id' => $this->primaryKey(), 'name' => $this->string(), 'description' => $this->text(), + 'server' => $this->string(), 'challenge_image' => $this->string(), 'validator_image' => $this->string(), 'created_at' => $this->datetime(), From 0246c05e2e8bd087a6f2059869bfdbb000d8c665 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 04:37:11 +0200 Subject: [PATCH 06/24] initial solution validation --- .../controllers/DefaultController.php | 318 ++++++++++++++++++ .../controllers/SpeedProblemController.php | 224 ++++++------ .../speedprogramming/models/SpeedProblem.php | 102 +++--- .../models/SpeedProblemSearch.php | 101 +++--- .../speedprogramming/views/default/_form.php | 45 +++ .../views/default/_search.php | 47 +++ .../speedprogramming/views/default/create.php | 20 ++ .../speedprogramming/views/default/index.php | 55 +++ .../speedprogramming/views/default/update.php | 21 ++ .../speedprogramming/views/default/view.php | 74 ++++ .../views/speed-problem/_form.php | 2 + .../views/speed-problem/index.php | 1 + .../views/speed-problem/view.php | 1 + 13 files changed, 799 insertions(+), 212 deletions(-) create mode 100644 backend/modules/speedprogramming/controllers/DefaultController.php create mode 100644 backend/modules/speedprogramming/views/default/_form.php create mode 100644 backend/modules/speedprogramming/views/default/_search.php create mode 100644 backend/modules/speedprogramming/views/default/create.php create mode 100644 backend/modules/speedprogramming/views/default/index.php create mode 100644 backend/modules/speedprogramming/views/default/update.php create mode 100644 backend/modules/speedprogramming/views/default/view.php diff --git a/backend/modules/speedprogramming/controllers/DefaultController.php b/backend/modules/speedprogramming/controllers/DefaultController.php new file mode 100644 index 000000000..3eaeb62c8 --- /dev/null +++ b/backend/modules/speedprogramming/controllers/DefaultController.php @@ -0,0 +1,318 @@ + [ + 'class' => VerbFilter::class, + 'actions' => [ + 'delete' => ['POST'], + ], + ], + ]; + } + + /** + * Lists all SpeedSolution models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new SpeedSolutionSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } + + /** + * Displays a single SpeedSolution model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new SpeedSolution model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new SpeedSolution(); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); + } + + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing SpeedSolution model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); + } + + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Submits a solution to container for validation + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionValidate($id) + { + $model = $this->findModel($id); + try { + $client = DockerClientFactory::create([ + //'remote_socket' => 'unix:///var/run/docker.sock', + 'remote_socket' => $model->server, + 'ssl' => false, + ]); + $docker = Docker::create($client); + } catch (\Exception $e) { + Yii::$app->session->setFlash('error', 'Failed to connect to docker server.'); + return $this->redirect(['index']); + } + + $restartPolicy = new RestartPolicy(); + $hostConfig = new HostConfig(); + $hostConfig->setMemory(512 * 1024 * 1024); // Set memory limit to 512MB + $hostConfig->setRestartPolicy($restartPolicy); + + $containerConfig = new ContainersCreatePostBody(); + $endpointSettings = new EndpointSettings(); + $endpointIPAMConfig = new EndpointIPAMConfig(); + $endpointSettings->setIPAMConfig($endpointIPAMConfig); + $containerConfig->setImage($model->validator_image); // target->image + $containerConfig->setHostConfig($hostConfig); + $containerConfig->setAttachStdin(true); + $containerConfig->setAttachStdout(true); + $containerConfig->setAttachStderr(true); + $containerConfig->setCmd(['echo', 'I am running a command inside the validator']); + $targetVariables[] = sprintf("FETCH_URL=https://" . \Yii::$app->sys->offense_domain . "/uploads/player_%d-target_%d.%s", $model->player_id, $model->target_id); + $targetVariables[] = sprintf("VALIDATE_LANG=%s", $model->language); + $targetVariables[] = sprintf("VALIDATE_PLAYER=%s", $model->player_id); + $targetVariables[] = sprintf("VALIDATE_PROBLEM=%s", $model->problem_id); + $targetVariables[] = sprintf("VALIDATE_SUBMISSION=%s", $model->id); + $containerConfig->setEnv($targetVariables); // target->targetVariables + + $containerCreateResult = $docker->containerCreate($containerConfig, ['name' => $model->language . 'validation_id' . $model->id]); // target->name + $attachStream = $docker->containerAttach($containerCreateResult->getId(), [ + 'stream' => true, + 'stdin' => true, + 'stdout' => true, + 'stderr' => true + ]); + $docker->containerStart($containerCreateResult->getId()); + $attachStream->onStdout(function ($stdout) use (&$model) { + $model->modcomments .= date('Y-m-d H:i:s') . ' stdout: '; + $model->modcomments .= $stdout; + }); + $attachStream->onStderr(function ($stderr) use (&$model) { + $model->modcomments .= date('Y-m-d H:i:s') . ' stderr: '; + $model->modcomments .= $stderr; + }); + + $attachStream->wait(); + $docker->containerDelete($containerCreateResult->getId(), ['force' => true]); + if ($model->save()) { + Yii::$app->session->setFlash('success', 'Validation completed, check the modcomments for output.'); + } else + Yii::$app->session->setFlash('error', 'Validation failed check the modcomments for output.'); + return $this->redirect(['view', 'id' => $model->id]); + } + + /** + * Deletes an existing SpeedSolution model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + + /** + * Approves an existing SpeedSolution model. + * If approval is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionApprove($id) + { + $model = $this->findModel($id); + if ($model->approve()) { + Yii::$app->session->setFlash('success', 'Submission approved.'); + } else { + Yii::$app->session->setFlash('error', 'Failed to approve submission.'); + } + + return $this->redirect(['view', 'id' => $id]); + } + /** + * Rejects an existing SpeedSolution model. + * If rejection is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionReject($id) + { + $model = $this->findModel($id); + if ($model->reject()) { + Yii::$app->session->setFlash('success', 'Submission rejected succesfully.'); + } else { + Yii::$app->session->setFlash('error', 'Failed to reject submission.'); + } + return $this->redirect(['view', 'id' => $id]); + } + + + /** + * Finds the SpeedSolution model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return SpeedSolution the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = SpeedSolution::findOne($id)) !== null) { + return $model; + } + + throw new NotFoundHttpException('The requested page does not exist.'); + } + + private function getManager() + { + return self::getDocker(); + } + + public function testStartStream(): void + { + $createContainerResult = $this->createContainer(); + + $execConfig = new ContainersIdExecPostBody(); + $execConfig->setAttachStdout(true); + $execConfig->setAttachStderr(true); + $execConfig->setCmd(['echo', 'output']); + + $execCreateResult = $this->getManager()->containerExec($createContainerResult->getId(), $execConfig); + + $execStartConfig = new ExecIdStartPostBody(); + $execStartConfig->setDetach(false); + $execStartConfig->setTty(false); + + $stream = $this->getManager()->execStart($execCreateResult->getId(), $execStartConfig); + + $this->assertInstanceOf(DockerRawStream::class, $stream); + + $stdoutFull = ''; + $stream->onStdout(function ($stdout) use (&$stdoutFull): void { + $stdoutFull .= $stdout; + }); + $stream->wait(); + + $this->assertSame("output\n", $stdoutFull); + + self::getDocker()->containerKill($createContainerResult->getId(), [ + 'signal' => 'SIGKILL', + ]); + } + public function testExecFind(): void + { + $createContainerResult = $this->createContainer(); + + $execConfig = new ContainersIdExecPostBody(); + $execConfig->setCmd(['/bin/true']); + $execCreateResult = $this->getManager()->containerExec($createContainerResult->getId(), $execConfig); + + $execStartConfig = new ExecIdStartPostBody(); + $execStartConfig->setDetach(false); + $execStartConfig->setTty(false); + + $this->getManager()->execStart($execCreateResult->getId(), $execStartConfig); + + $execFindResult = $this->getManager()->execInspect($execCreateResult->getId()); + + $this->assertInstanceOf(ExecIdJsonGetResponse200::class, $execFindResult); + + self::getDocker()->containerKill($createContainerResult->getId(), [ + 'signal' => 'SIGKILL', + ]); + } + + private function createContainer() + { + $containerConfig = new ContainersCreatePostBody(); + $containerConfig->setImage('busybox:latest'); + $containerConfig->setCmd(['sh']); + $containerConfig->setOpenStdin(true); + $containerConfig->setLabels(new \ArrayObject(['docker-php-test' => 'true'])); + + $containerCreateResult = self::getDocker()->containerCreate($containerConfig); + self::getDocker()->containerStart($containerCreateResult->getId()); + + return $containerCreateResult; + } +} diff --git a/backend/modules/speedprogramming/controllers/SpeedProblemController.php b/backend/modules/speedprogramming/controllers/SpeedProblemController.php index d33300ed3..eaf5973e1 100644 --- a/backend/modules/speedprogramming/controllers/SpeedProblemController.php +++ b/backend/modules/speedprogramming/controllers/SpeedProblemController.php @@ -13,122 +13,122 @@ */ class SpeedProblemController extends BaseController { - /** - * @inheritDoc - */ - public function behaviors() - { - return array_merge( - parent::behaviors(), - [ - 'verbs' => [ - 'class' => VerbFilter::className(), - 'actions' => [ - 'delete' => ['POST'], - ], - ], - ] - ); + /** + * @inheritDoc + */ + public function behaviors() + { + return array_merge( + parent::behaviors(), + [ + 'verbs' => [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'delete' => ['POST'], + ], + ], + ] + ); + } + + /** + * Lists all SpeedProblem models. + * + * @return string + */ + public function actionIndex() + { + $searchModel = new SpeedProblemSearch(); + $dataProvider = $searchModel->search($this->request->queryParams); + + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } + + /** + * Displays a single SpeedProblem model. + * @param int $id ID + * @return string + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new SpeedProblem model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return string|\yii\web\Response + */ + public function actionCreate() + { + $model = new SpeedProblem(); + + if ($this->request->isPost) { + if ($model->load($this->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); + } + } else { + $model->loadDefaultValues(); } - /** - * Lists all SpeedProblem models. - * - * @return string - */ - public function actionIndex() - { - $searchModel = new SpeedProblemSearch(); - $dataProvider = $searchModel->search($this->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing SpeedProblem model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param int $id ID + * @return string|\yii\web\Response + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($this->request->isPost && $model->load($this->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Displays a single SpeedProblem model. - * @param int $id ID - * @return string - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing SpeedProblem model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param int $id ID + * @return \yii\web\Response + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the SpeedProblem model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param int $id ID + * @return SpeedProblem the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = SpeedProblem::findOne(['id' => $id])) !== null) { + return $model; } - /** - * Creates a new SpeedProblem model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return string|\yii\web\Response - */ - public function actionCreate() - { - $model = new SpeedProblem(); - - if ($this->request->isPost) { - if ($model->load($this->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - } else { - $model->loadDefaultValues(); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } - - /** - * Updates an existing SpeedProblem model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param int $id ID - * @return string|\yii\web\Response - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model = $this->findModel($id); - - if ($this->request->isPost && $model->load($this->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); - } - - /** - * Deletes an existing SpeedProblem model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param int $id ID - * @return \yii\web\Response - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); - } - - /** - * Finds the SpeedProblem model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param int $id ID - * @return SpeedProblem the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if (($model = SpeedProblem::findOne(['id' => $id])) !== null) { - return $model; - } - - throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); - } + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } } diff --git a/backend/modules/speedprogramming/models/SpeedProblem.php b/backend/modules/speedprogramming/models/SpeedProblem.php index c71ad9e5b..2c7f0f17c 100644 --- a/backend/modules/speedprogramming/models/SpeedProblem.php +++ b/backend/modules/speedprogramming/models/SpeedProblem.php @@ -12,6 +12,7 @@ * @property string|null $description * @property string|null $challenge_image * @property string|null $validator_image + * @property string|null $server * @property string|null $created_at * @property string|null $updated_at * @@ -19,58 +20,59 @@ */ class SpeedProblem extends \yii\db\ActiveRecord { - /** - * {@inheritdoc} - */ - public static function tableName() - { - return 'speed_problem'; - } + /** + * {@inheritdoc} + */ + public static function tableName() + { + return 'speed_problem'; + } - /** - * {@inheritdoc} - */ - public function rules() - { - return [ - [['description'], 'string'], - [['created_at', 'updated_at'], 'safe'], - [['name', 'challenge_image', 'validator_image'], 'string', 'max' => 255], - ]; - } + /** + * {@inheritdoc} + */ + public function rules() + { + return [ + [['description'], 'string'], + [['created_at', 'updated_at'], 'safe'], + [['name', 'challenge_image', 'validator_image', 'server'], 'string', 'max' => 255], + ]; + } - /** - * {@inheritdoc} - */ - public function attributeLabels() - { - return [ - 'id' => Yii::t('app', 'ID'), - 'name' => Yii::t('app', 'Name'), - 'description' => Yii::t('app', 'Description'), - 'challenge_image' => Yii::t('app', 'Challenge Image'), - 'validator_image' => Yii::t('app', 'Validator Image'), - 'created_at' => Yii::t('app', 'Created At'), - 'updated_at' => Yii::t('app', 'Updated At'), - ]; - } + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('app', 'ID'), + 'name' => Yii::t('app', 'Name'), + 'description' => Yii::t('app', 'Description'), + 'challenge_image' => Yii::t('app', 'Challenge Image'), + 'validator_image' => Yii::t('app', 'Validator Image'), + 'server' => Yii::t('app', 'Server'), + 'created_at' => Yii::t('app', 'Created At'), + 'updated_at' => Yii::t('app', 'Updated At'), + ]; + } - /** - * Gets query for [[SpeedSolutions]]. - * - * @return \yii\db\ActiveQuery|SpeedSolutionQuery - */ - public function getSpeedSolutions() - { - return $this->hasMany(SpeedSolution::class, ['problem_id' => 'id']); - } + /** + * Gets query for [[SpeedSolutions]]. + * + * @return \yii\db\ActiveQuery|SpeedSolutionQuery + */ + public function getSpeedSolutions() + { + return $this->hasMany(SpeedSolution::class, ['problem_id' => 'id']); + } - /** - * {@inheritdoc} - * @return SpeedProblemQuery the active query used by this AR class. - */ - public static function find() - { - return new SpeedProblemQuery(get_called_class()); - } + /** + * {@inheritdoc} + * @return SpeedProblemQuery the active query used by this AR class. + */ + public static function find() + { + return new SpeedProblemQuery(get_called_class()); + } } diff --git a/backend/modules/speedprogramming/models/SpeedProblemSearch.php b/backend/modules/speedprogramming/models/SpeedProblemSearch.php index a7df59dd9..6e1ad410a 100644 --- a/backend/modules/speedprogramming/models/SpeedProblemSearch.php +++ b/backend/modules/speedprogramming/models/SpeedProblemSearch.php @@ -11,63 +11,64 @@ */ class SpeedProblemSearch extends SpeedProblem { - /** - * {@inheritdoc} - */ - public function rules() - { - return [ - [['id'], 'integer'], - [['name', 'description', 'challenge_image', 'validator_image', 'created_at', 'updated_at'], 'safe'], - ]; - } + /** + * {@inheritdoc} + */ + public function rules() + { + return [ + [['id'], 'integer'], + [['name', 'description', 'challenge_image', 'validator_image', 'server', 'created_at', 'updated_at'], 'safe'], + ]; + } - /** - * {@inheritdoc} - */ - public function scenarios() - { - // bypass scenarios() implementation in the parent class - return Model::scenarios(); - } + /** + * {@inheritdoc} + */ + public function scenarios() + { + // bypass scenarios() implementation in the parent class + return Model::scenarios(); + } - /** - * Creates data provider instance with search query applied - * - * @param array $params - * - * @return ActiveDataProvider - */ - public function search($params) - { - $query = SpeedProblem::find(); + /** + * Creates data provider instance with search query applied + * + * @param array $params + * + * @return ActiveDataProvider + */ + public function search($params) + { + $query = SpeedProblem::find(); - // add conditions that should always apply here + // add conditions that should always apply here - $dataProvider = new ActiveDataProvider([ - 'query' => $query, - ]); + $dataProvider = new ActiveDataProvider([ + 'query' => $query, + ]); - $this->load($params); + $this->load($params); - if (!$this->validate()) { - // uncomment the following line if you do not want to return any records when validation fails - // $query->where('0=1'); - return $dataProvider; - } + if (!$this->validate()) { + // uncomment the following line if you do not want to return any records when validation fails + // $query->where('0=1'); + return $dataProvider; + } - // grid filtering conditions - $query->andFilterWhere([ - 'id' => $this->id, - 'created_at' => $this->created_at, - 'updated_at' => $this->updated_at, - ]); + // grid filtering conditions + $query->andFilterWhere([ + 'id' => $this->id, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]); - $query->andFilterWhere(['like', 'name', $this->name]) - ->andFilterWhere(['like', 'description', $this->description]) - ->andFilterWhere(['like', 'challenge_image', $this->challenge_image]) - ->andFilterWhere(['like', 'validator_image', $this->validator_image]); + $query->andFilterWhere(['like', 'name', $this->name]) + ->andFilterWhere(['like', 'description', $this->description]) + ->andFilterWhere(['like', 'server', $this->server]) + ->andFilterWhere(['like', 'challenge_image', $this->challenge_image]) + ->andFilterWhere(['like', 'validator_image', $this->validator_image]); - return $dataProvider; - } + return $dataProvider; + } } diff --git a/backend/modules/speedprogramming/views/default/_form.php b/backend/modules/speedprogramming/views/default/_form.php new file mode 100644 index 000000000..c5984d718 --- /dev/null +++ b/backend/modules/speedprogramming/views/default/_form.php @@ -0,0 +1,45 @@ + + +
+ + +
+
+ field($model, 'player_id')->dropDownList(ArrayHelper::map(Player::find()->orderBy(['username'=>SORT_ASC])->all(), 'id', 'username'), ['prompt'=>'Player'])->Label('Player')->hint('Player who authored this solutions.')?> +
+
+ field($model, 'team_id')->dropDownList(ArrayHelper::map(Team::find()->orderBy(['id'=>SORT_ASC])->all(), 'id', 'name'), ['prompt'=>'Team'])->Label('Team')->hint('Select the team for the solution (optional)')?> +
+
+ field($model, 'target_id')->dropDownList(ArrayHelper::map(Target::find()->orderBy(['id'=>SORT_ASC])->all(), 'id', 'name'), ['prompt'=>'Challenge'])->Label('Challenge')->hint('Challenge that this solution belongs.')?> +
+
field($model, 'status')->dropDownList($model->statuses, ['prompt'=>'Status'])->Label('Status')->hint('Select submission status')?>
+
field($model, 'language')->dropDownList($model->languages, ['prompt'=>'Language'])->Label('Language')->hint('Select submission language')?>
+
field($model, 'points')->textInput() ?>
+
+ + field($model, 'sourcecode')->textarea(['rows'=>10]) ?> + field($model, 'modcomments')->textarea(['rows' => 10]) ?> + + + + +
+ 'btn btn-success']) ?> +
+ + + +
diff --git a/backend/modules/speedprogramming/views/default/_search.php b/backend/modules/speedprogramming/views/default/_search.php new file mode 100644 index 000000000..c8ee1015b --- /dev/null +++ b/backend/modules/speedprogramming/views/default/_search.php @@ -0,0 +1,47 @@ + + + diff --git a/backend/modules/speedprogramming/views/default/create.php b/backend/modules/speedprogramming/views/default/create.php new file mode 100644 index 000000000..128663a9b --- /dev/null +++ b/backend/modules/speedprogramming/views/default/create.php @@ -0,0 +1,20 @@ +title = 'Create Speed Solution'; +$this->params['breadcrumbs'][] = ['label' => 'Speed Solutions', 'url' => ['index']]; +$this->params['breadcrumbs'][] = $this->title; +?> +
+ +

title) ?>

+ + render('_form', [ + 'model' => $model, + ]) ?> + +
diff --git a/backend/modules/speedprogramming/views/default/index.php b/backend/modules/speedprogramming/views/default/index.php new file mode 100644 index 000000000..9641d66ae --- /dev/null +++ b/backend/modules/speedprogramming/views/default/index.php @@ -0,0 +1,55 @@ +title = 'Speed Solutions'; +$this->params['breadcrumbs'][] = $this->title; +?> +
+ +

title) ?>

+ +

+ 'btn btn-success']) ?> +

+ + render('_search', ['model' => $searchModel]); ?> + + $dataProvider, + 'filterModel' => $searchModel, + 'columns' => [ + ['class' => 'yii\grid\SerialColumn'], + + 'id', + 'player_id', + 'problem_id', + [ + 'attribute'=>'player.username', + 'label'=>'Player' + ], + [ + 'attribute' => 'language', + 'filter' => SpeedSolution::getLanguages(), + ], + [ + 'attribute' => 'status', + 'filter' => SpeedSolution::getStatuses(), + ], + //'sourcecode', + 'points', + //'modcomments:ntext', + //'created_at', + //'updated_at', + + ['class' => 'yii\grid\ActionColumn','template' => '{view} {update}'], + ], + ]); ?> + + +
diff --git a/backend/modules/speedprogramming/views/default/update.php b/backend/modules/speedprogramming/views/default/update.php new file mode 100644 index 000000000..a77443dc6 --- /dev/null +++ b/backend/modules/speedprogramming/views/default/update.php @@ -0,0 +1,21 @@ +title = 'Update Speed Solution: ' . $model->id; +$this->params['breadcrumbs'][] = ['label' => 'Speed Solutions', 'url' => ['index']]; +$this->params['breadcrumbs'][] = ['label' => $model->id, 'url' => ['view', 'id' => $model->id]]; +$this->params['breadcrumbs'][] = 'Update'; +?> +
+ +

title) ?>

+ + render('_form', [ + 'model' => $model, + ]) ?> + +
diff --git a/backend/modules/speedprogramming/views/default/view.php b/backend/modules/speedprogramming/views/default/view.php new file mode 100644 index 000000000..66606523d --- /dev/null +++ b/backend/modules/speedprogramming/views/default/view.php @@ -0,0 +1,74 @@ +title = $model->id; +$this->params['breadcrumbs'][] = ['label' => 'Speed Solutions', 'url' => ['index']]; +$this->params['breadcrumbs'][] = $this->title; +\yii\web\YiiAsset::register($this); +?> +
+ +

title) ?>

+ +

+ + player_id . '-target_'. $model->target_id. '.'.$model->language, ['class' => 'btn btn-warning']) ?> + + $model->id], ['class' => 'btn btn-primary']) ?> + status==='pending'):?> + $model->id], [ + 'class' => 'btn btn-success', + 'data' => [ + 'confirm' => 'Are you sure you want to approve this submission with default points?', + 'method' => 'post', + ], + ]) ?> + $model->id], [ + 'class' => 'btn btn-danger', + 'data' => [ + 'confirm' => 'Are you sure you want to reject this submission?', + 'method' => 'post', + ], + ]) ?> + +

+ + $model, + 'attributes' => [ + 'id', + [ + 'attribute'=>'player.username', + 'label'=>'Player', + 'value'=>function($model){ return sprintf("(id: %d) %s %s",$model->player_id,$model->player->username,$model->team ? $model->team->name:''); } + ], + [ + 'attribute'=>'target.name', + 'label'=>'Challenge', + 'value'=>function($model){ return sprintf("(id: %d / difficulty: %d) %s",$model->target_id,$model->target->difficulty,$model->target->name); } + ], + [ + 'attribute' => 'language', + ], + [ + 'attribute' => 'status', + ], + //'sourcecode', + 'points', + [ + 'attribute'=>'sourcecode', + 'format'=>'raw', + 'value'=>function($model){return '
'.Html::encode(wordwrap($model->sourcecode,90)).'
';} + ], + 'modcomments:ntext', + 'created_at', + 'updated_at', + ], + ]) ?> + +
diff --git a/backend/modules/speedprogramming/views/speed-problem/_form.php b/backend/modules/speedprogramming/views/speed-problem/_form.php index 3219f39a2..eb885d05e 100644 --- a/backend/modules/speedprogramming/views/speed-problem/_form.php +++ b/backend/modules/speedprogramming/views/speed-problem/_form.php @@ -20,6 +20,8 @@ field($model, 'validator_image')->textInput(['maxlength' => true]) ?> + field($model, 'server')->textInput(['maxlength' => true]) ?> + field($model, 'created_at')->textInput() ?> field($model, 'updated_at')->textInput() ?> diff --git a/backend/modules/speedprogramming/views/speed-problem/index.php b/backend/modules/speedprogramming/views/speed-problem/index.php index 43fc487e4..abcad01b5 100644 --- a/backend/modules/speedprogramming/views/speed-problem/index.php +++ b/backend/modules/speedprogramming/views/speed-problem/index.php @@ -35,6 +35,7 @@ 'description:ntext', 'challenge_image', 'validator_image', + 'server', 'created_at', 'updated_at', [ diff --git a/backend/modules/speedprogramming/views/speed-problem/view.php b/backend/modules/speedprogramming/views/speed-problem/view.php index aeebc2007..558e9711f 100644 --- a/backend/modules/speedprogramming/views/speed-problem/view.php +++ b/backend/modules/speedprogramming/views/speed-problem/view.php @@ -34,6 +34,7 @@ 'description:ntext', 'challenge_image', 'validator_image', + 'server', 'created_at', 'updated_at', ], From cd7491d4fb72d618b078a2ccbf71833a125bac6f Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 04:38:38 +0200 Subject: [PATCH 07/24] classname to class --- .../speedprogramming/controllers/SpeedProblemController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/modules/speedprogramming/controllers/SpeedProblemController.php b/backend/modules/speedprogramming/controllers/SpeedProblemController.php index eaf5973e1..6847f4616 100644 --- a/backend/modules/speedprogramming/controllers/SpeedProblemController.php +++ b/backend/modules/speedprogramming/controllers/SpeedProblemController.php @@ -22,7 +22,7 @@ public function behaviors() parent::behaviors(), [ 'verbs' => [ - 'class' => VerbFilter::className(), + 'class' => VerbFilter::class, 'actions' => [ 'delete' => ['POST'], ], From 00e61ba194074171d08a1a8c81b65801922c6e51 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 04:43:40 +0200 Subject: [PATCH 08/24] initial frontend speed programming module --- frontend/modules/speedprogramming/Module.php | 27 ++++++++++++++ .../speedprogramming/models/SpeedForm.php | 37 +++++++++++++++++++ .../models/SpeedSolutionQuery.php | 34 +++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 frontend/modules/speedprogramming/Module.php create mode 100644 frontend/modules/speedprogramming/models/SpeedForm.php create mode 100644 frontend/modules/speedprogramming/models/SpeedSolutionQuery.php diff --git a/frontend/modules/speedprogramming/Module.php b/frontend/modules/speedprogramming/Module.php new file mode 100644 index 000000000..35cb2b272 --- /dev/null +++ b/frontend/modules/speedprogramming/Module.php @@ -0,0 +1,27 @@ + ['c', 'cpp', 'c++','php','php7','py','py2','py3','java'], 'maxSize' => 1024 * 1024 * 1,'minSize'=>128,'tooSmall'=>'Solution too small'], + [['language', 'file'], 'required'], + ['language', 'string'], + ['language', 'in', 'strict'=>true, 'range' => ['cs','c','cpp','py2','py3','php7','java']] + ]; + } + + public function getAvailableLanguages() + { + return [ + 'c'=>'C', + 'cpp'=>'C++', + 'cs'=>'C#', + 'py2'=>'Python 2.x', + 'py3'=>'Python 3.x', + 'php7'=>'PHP 7.x', + 'java'=>'Java' + ]; + } +} diff --git a/frontend/modules/speedprogramming/models/SpeedSolutionQuery.php b/frontend/modules/speedprogramming/models/SpeedSolutionQuery.php new file mode 100644 index 000000000..d2d3c9d62 --- /dev/null +++ b/frontend/modules/speedprogramming/models/SpeedSolutionQuery.php @@ -0,0 +1,34 @@ +andWhere('[[status]]=1'); + }*/ + + /** + * {@inheritdoc} + * @return SpeedSolution[]|array + */ + public function all($db = null) + { + return parent::all($db); + } + + /** + * {@inheritdoc} + * @return SpeedSolution|array|null + */ + public function one($db = null) + { + return parent::one($db); + } +} From 82186105e69c5d3a905837c0c2c8c726071ad491 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 04:45:32 +0200 Subject: [PATCH 09/24] sample validator image for speed programming --- ansible/Dockerfiles/validator/Dockerfile | 17 +++++++++++++++++ ansible/Dockerfiles/validator/README.md | 1 + ansible/Dockerfiles/validator/entrypoint.sh | 13 +++++++++++++ .../Dockerfiles/validator/scripts/c_validator | 6 ++++++ .../Dockerfiles/validator/scripts/cpp_validator | 6 ++++++ .../validator/scripts/java_validator | 4 ++++ .../validator/scripts/php7_validator | 6 ++++++ .../Dockerfiles/validator/scripts/py2_validator | 3 +++ .../Dockerfiles/validator/scripts/py3_validator | 3 +++ 9 files changed, 59 insertions(+) create mode 100644 ansible/Dockerfiles/validator/Dockerfile create mode 100644 ansible/Dockerfiles/validator/README.md create mode 100644 ansible/Dockerfiles/validator/entrypoint.sh create mode 100644 ansible/Dockerfiles/validator/scripts/c_validator create mode 100644 ansible/Dockerfiles/validator/scripts/cpp_validator create mode 100644 ansible/Dockerfiles/validator/scripts/java_validator create mode 100644 ansible/Dockerfiles/validator/scripts/php7_validator create mode 100644 ansible/Dockerfiles/validator/scripts/py2_validator create mode 100644 ansible/Dockerfiles/validator/scripts/py3_validator diff --git a/ansible/Dockerfiles/validator/Dockerfile b/ansible/Dockerfiles/validator/Dockerfile new file mode 100644 index 000000000..03b0a0325 --- /dev/null +++ b/ansible/Dockerfiles/validator/Dockerfile @@ -0,0 +1,17 @@ +FROM buildpack-deps:buster-curl +LABEL maintainer="echothrust solutions " +LABEL description="Speed Programming Validation Container" + + +ENV DEBIAN_FRONTEND noninteractive +COPY --chown=root:root scripts /usr/local/validators +COPY --chown=root:root entrypoint.sh / +WORKDIR /echoctf +RUN set -ex \ + && apt-get update \ + && apt-get install --no-install-recommends -y default-jre default-jdk build-essential wget python3 gcc git gzip socat netcat-openbsd python2 \ + && chmod 0500 /entrypoint.sh /usr/local/validators/* + + +ENTRYPOINT ["/entrypoint.sh"] +CMD ["bash"] diff --git a/ansible/Dockerfiles/validator/README.md b/ansible/Dockerfiles/validator/README.md new file mode 100644 index 000000000..018200547 --- /dev/null +++ b/ansible/Dockerfiles/validator/README.md @@ -0,0 +1 @@ +# Validator image example diff --git a/ansible/Dockerfiles/validator/entrypoint.sh b/ansible/Dockerfiles/validator/entrypoint.sh new file mode 100644 index 000000000..3e19aca81 --- /dev/null +++ b/ansible/Dockerfiles/validator/entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/bash +if [ "$FETCH_URL"x == "x" ] || [ "$VALIDATE_LANG"x == "x" ]; then + echo "Error: FETCH_URL or VALIDATE_LANG env variable not set"; + exit 1 +fi + +wget -O /echoctf/script_to_validate."${VALIDATE_LANG}" "${FETCH_URL}" + + +if [ -x /usr/local/validators/${VALIDATE_LANG}_validator ]; then + /usr/local/validators/${VALIDATE_LANG}_validator +fi +$@ diff --git a/ansible/Dockerfiles/validator/scripts/c_validator b/ansible/Dockerfiles/validator/scripts/c_validator new file mode 100644 index 000000000..4fc7ea36e --- /dev/null +++ b/ansible/Dockerfiles/validator/scripts/c_validator @@ -0,0 +1,6 @@ +#!/bin/bash + +echo "Validating c..." +gcc /echoctf/script_to_validate.c -o /echoctf/submission + +timeout 1 bash -c "echo \"TEST1\"|/echoctf/submission" diff --git a/ansible/Dockerfiles/validator/scripts/cpp_validator b/ansible/Dockerfiles/validator/scripts/cpp_validator new file mode 100644 index 000000000..6fc5ad493 --- /dev/null +++ b/ansible/Dockerfiles/validator/scripts/cpp_validator @@ -0,0 +1,6 @@ +#!/bin/bash + +echo "Validating cpp..." +g++ /echoctf/script_to_validate.cpp -o /echoctf/submission + +timeout 1 bash -c "echo \"TEST1\"|/echoctf/submission" diff --git a/ansible/Dockerfiles/validator/scripts/java_validator b/ansible/Dockerfiles/validator/scripts/java_validator new file mode 100644 index 000000000..b9647f000 --- /dev/null +++ b/ansible/Dockerfiles/validator/scripts/java_validator @@ -0,0 +1,4 @@ +#!/bin/bash + +echo "Validating java..." +timeout 1 bash -c "echo \"TEST1\"|java /echoctf/script_to_validate.java" diff --git a/ansible/Dockerfiles/validator/scripts/php7_validator b/ansible/Dockerfiles/validator/scripts/php7_validator new file mode 100644 index 000000000..e2e04c363 --- /dev/null +++ b/ansible/Dockerfiles/validator/scripts/php7_validator @@ -0,0 +1,6 @@ +#!/bin/bash + +echo "Validating php7..." + + +echo "TEST1"|timeout 1 php7 /echoctf/script_to_validate.php7 diff --git a/ansible/Dockerfiles/validator/scripts/py2_validator b/ansible/Dockerfiles/validator/scripts/py2_validator new file mode 100644 index 000000000..48c93d962 --- /dev/null +++ b/ansible/Dockerfiles/validator/scripts/py2_validator @@ -0,0 +1,3 @@ +#!/usr/bin/python2 + +print "Validating py2..." diff --git a/ansible/Dockerfiles/validator/scripts/py3_validator b/ansible/Dockerfiles/validator/scripts/py3_validator new file mode 100644 index 000000000..336eb072d --- /dev/null +++ b/ansible/Dockerfiles/validator/scripts/py3_validator @@ -0,0 +1,3 @@ +#!/usr/bin/python3 + +print "Validating py3..." From 67207b74e2dfd86879a89baa0862ebe74c12ade9 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 12:58:44 +0200 Subject: [PATCH 10/24] add stream handle for solution --- backend/modules/activity/models/Stream.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/modules/activity/models/Stream.php b/backend/modules/activity/models/Stream.php index e755fa3c5..27ee67f18 100644 --- a/backend/modules/activity/models/Stream.php +++ b/backend/modules/activity/models/Stream.php @@ -22,6 +22,7 @@ class Stream extends StreamAR const MODEL_ICONS=[ 'headshot'=>'', 'challenge'=>'', + 'solution'=>'', 'treasure'=>'', 'finding'=>'', 'question'=>'', @@ -62,6 +63,11 @@ public function getSuffix() return ""; } + public function getSolutionMessage() + { + return sprintf("%s %s%s", $this->prefix, $this->title, $this->suffix); + } + public function getBadgeMessage() { return sprintf("%s got the badge [%s]%s", $this->prefix, Badge::findOne(['id'=>$this->model_id])->name, $this->suffix); From 591f0d7736b8f5ba6afd002e86b0ce4d9a472f69 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 12:59:34 +0200 Subject: [PATCH 11/24] remove old team and target id's --- .../speedprogramming/models/SpeedSolution.php | 4 ++-- .../speedprogramming/views/default/_form.php | 13 ++++++++----- .../speedprogramming/views/default/_search.php | 4 +--- .../modules/speedprogramming/views/default/view.php | 7 +++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/backend/modules/speedprogramming/models/SpeedSolution.php b/backend/modules/speedprogramming/models/SpeedSolution.php index f4bba4f0f..0ab3c1df0 100644 --- a/backend/modules/speedprogramming/models/SpeedSolution.php +++ b/backend/modules/speedprogramming/models/SpeedSolution.php @@ -116,9 +116,9 @@ public function afterSave($insert, $changedAttributes) $stream=new Stream(); $stream->player_id=$this->player_id; $stream->model='solution'; - $stream->model_id=$this->target_id; + $stream->model_id=$this->problem_id; $stream->points=$this->points; - $stream->title='submission for '.$this->target->name.' got '.$this->status; + $stream->title='submission for '.$this->problem->name.' got '.$this->status; $stream->message=$stream->pubmessage=$stream->pubtitle=$stream->title; return $stream->save(); } diff --git a/backend/modules/speedprogramming/views/default/_form.php b/backend/modules/speedprogramming/views/default/_form.php index c5984d718..0d8a87b7f 100644 --- a/backend/modules/speedprogramming/views/default/_form.php +++ b/backend/modules/speedprogramming/views/default/_form.php @@ -5,7 +5,8 @@ use yii\helpers\ArrayHelper; use app\modules\frontend\models\Player; use app\modules\frontend\models\Team; -use app\modules\gameplay\models\Target; +use app\modules\speedprogramming\models\SpeedProblem; +use app\widgets\sleifer\autocompleteAjax\AutocompleteAjax; /* @var $this yii\web\View */ /* @var $model app\models\SpeedSolution */ @@ -17,13 +18,16 @@
- field($model, 'player_id')->dropDownList(ArrayHelper::map(Player::find()->orderBy(['username'=>SORT_ASC])->all(), 'id', 'username'), ['prompt'=>'Player'])->Label('Player')->hint('Player who authored this solutions.')?> + field($model, 'player_id')->widget(AutocompleteAjax::class, [ + 'multiple' => false, + 'url' => ['/frontend/player/ajax-search'], + 'options' => ['placeholder' => 'Find player by email, username, id or profile.'] + ])->Label('Player')->hint('Choose the player that will own this solution') ?>
- field($model, 'team_id')->dropDownList(ArrayHelper::map(Team::find()->orderBy(['id'=>SORT_ASC])->all(), 'id', 'name'), ['prompt'=>'Team'])->Label('Team')->hint('Select the team for the solution (optional)')?> + field($model, 'problem_id')->dropDownList(ArrayHelper::map(SpeedProblem::find()->orderBy(['id'=>SORT_ASC])->all(), 'id', 'name'), ['prompt'=>'Problem'])->Label('Problem')->hint('Problem that this solution belongs.')?>
- field($model, 'target_id')->dropDownList(ArrayHelper::map(Target::find()->orderBy(['id'=>SORT_ASC])->all(), 'id', 'name'), ['prompt'=>'Challenge'])->Label('Challenge')->hint('Challenge that this solution belongs.')?>
field($model, 'status')->dropDownList($model->statuses, ['prompt'=>'Status'])->Label('Status')->hint('Select submission status')?>
field($model, 'language')->dropDownList($model->languages, ['prompt'=>'Language'])->Label('Language')->hint('Select submission language')?>
@@ -31,7 +35,6 @@
field($model, 'sourcecode')->textarea(['rows'=>10]) ?> - field($model, 'modcomments')->textarea(['rows' => 10]) ?> diff --git a/backend/modules/speedprogramming/views/default/_search.php b/backend/modules/speedprogramming/views/default/_search.php index c8ee1015b..99fd29d3a 100644 --- a/backend/modules/speedprogramming/views/default/_search.php +++ b/backend/modules/speedprogramming/views/default/_search.php @@ -19,9 +19,7 @@ field($model, 'player_id') ?> - field($model, 'team_id') ?> - - field($model, 'target_id') ?> + field($model, 'problem_id') ?> field($model, 'language') ?> diff --git a/backend/modules/speedprogramming/views/default/view.php b/backend/modules/speedprogramming/views/default/view.php index 66606523d..d7061a40f 100644 --- a/backend/modules/speedprogramming/views/default/view.php +++ b/backend/modules/speedprogramming/views/default/view.php @@ -17,7 +17,7 @@

- player_id . '-target_'. $model->target_id. '.'.$model->language, ['class' => 'btn btn-warning']) ?> + player_id . '-target_'. $model->problem_id. '.'.$model->language, ['class' => 'btn btn-warning']) ?> $model->id], ['class' => 'btn btn-primary']) ?> status==='pending'):?> @@ -45,12 +45,12 @@ [ 'attribute'=>'player.username', 'label'=>'Player', - 'value'=>function($model){ return sprintf("(id: %d) %s %s",$model->player_id,$model->player->username,$model->team ? $model->team->name:''); } + 'value'=>function($model){ return sprintf("(id: %d) %s",$model->player_id,$model->player->username); } ], [ 'attribute'=>'target.name', 'label'=>'Challenge', - 'value'=>function($model){ return sprintf("(id: %d / difficulty: %d) %s",$model->target_id,$model->target->difficulty,$model->target->name); } + 'value'=>function($model){ return sprintf("(id: %d / difficulty: %d) %s",$model->problem_id,$model->problem->difficulty,$model->problem->name); } ], [ 'attribute' => 'language', @@ -65,7 +65,6 @@ 'format'=>'raw', 'value'=>function($model){return '

'.Html::encode(wordwrap($model->sourcecode,90)).'
';} ], - 'modcomments:ntext', 'created_at', 'updated_at', ], From 9ddcbf87087f47bbdf17f93387f1025c51149a40 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 13:04:13 +0200 Subject: [PATCH 12/24] use problem id --- .../modules/speedprogramming/controllers/DefaultController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/modules/speedprogramming/controllers/DefaultController.php b/backend/modules/speedprogramming/controllers/DefaultController.php index 3eaeb62c8..d109e5a0f 100644 --- a/backend/modules/speedprogramming/controllers/DefaultController.php +++ b/backend/modules/speedprogramming/controllers/DefaultController.php @@ -142,7 +142,7 @@ public function actionValidate($id) $containerConfig->setAttachStdout(true); $containerConfig->setAttachStderr(true); $containerConfig->setCmd(['echo', 'I am running a command inside the validator']); - $targetVariables[] = sprintf("FETCH_URL=https://" . \Yii::$app->sys->offense_domain . "/uploads/player_%d-target_%d.%s", $model->player_id, $model->target_id); + $targetVariables[] = sprintf("FETCH_URL=https://" . \Yii::$app->sys->offense_domain . "/uploads/player_%d-target_%d.%s", $model->player_id, $model->problem_id); $targetVariables[] = sprintf("VALIDATE_LANG=%s", $model->language); $targetVariables[] = sprintf("VALIDATE_PLAYER=%s", $model->player_id); $targetVariables[] = sprintf("VALIDATE_PROBLEM=%s", $model->problem_id); From 78f6c90e97325f898a8993542606458ec816a5cd Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 13:10:27 +0200 Subject: [PATCH 13/24] add knob to disable operation --- backend/views/layouts/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/views/layouts/main.php b/backend/views/layouts/main.php index cc2c4d9c0..e9da66591 100644 --- a/backend/views/layouts/main.php +++ b/backend/views/layouts/main.php @@ -107,7 +107,7 @@ 'url' => ['/speedprogramming/default/index'], 'icon' => 'fas fa-money-check-alt', 'active' => Yii::$app->controller->module->id == 'speedprogramming', - 'visible' => array_key_exists('speedprogramming', \Yii::$app->modules) !== false && !Yii::$app->user->isGuest && Yii::$app->user->identity->isAdmin, + 'visible' => array_key_exists('speedprogramming', \Yii::$app->modules) !== false && !\Yii::$app->sys->module_speedprogramming_disabled && !Yii::$app->user->isGuest && Yii::$app->user->identity->isAdmin, 'items' => [ ['label' => 'Problems', 'url' => ['/speedprogramming/speed-problem/index'], 'visible' => !Yii::$app->user->isGuest && Yii::$app->user->identity->isAdmin && array_key_exists('sales', \Yii::$app->modules) !== false,], ['label' => 'Solutions', 'url' => ['/speedprogramming/default/index'], 'visible' => !Yii::$app->user->isGuest && Yii::$app->user->identity->isAdmin && array_key_exists('sales', \Yii::$app->modules) !== false,], From 1779eb50d879f0b6294df8ba99f598624c50e0c3 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 13:15:08 +0200 Subject: [PATCH 14/24] update migration with extra table columns --- .../migrations/m241105_014128_create_speed_problem_table.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/migrations/m241105_014128_create_speed_problem_table.php b/backend/migrations/m241105_014128_create_speed_problem_table.php index 7736e1bf3..9ca380e7f 100644 --- a/backend/migrations/m241105_014128_create_speed_problem_table.php +++ b/backend/migrations/m241105_014128_create_speed_problem_table.php @@ -16,6 +16,9 @@ public function safeUp() 'id' => $this->primaryKey(), 'name' => $this->string(), 'description' => $this->text(), + 'active' => $this->boolean()->notNull()->defaultValue(1), + 'difficulty' => $this->smallInteger()->notNull()->defaultValue(0), + 'category' => $this->string(64), 'server' => $this->string(), 'challenge_image' => $this->string(), 'validator_image' => $this->string(), From 1376fb2201215c6f2a7294554ab1306916e3c393 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 13:15:37 +0200 Subject: [PATCH 15/24] add migration for url routes --- ...11349_add_speed_programming_url_routes.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 backend/migrations-init/m241105_111349_add_speed_programming_url_routes.php diff --git a/backend/migrations-init/m241105_111349_add_speed_programming_url_routes.php b/backend/migrations-init/m241105_111349_add_speed_programming_url_routes.php new file mode 100644 index 000000000..8c8b6e868 --- /dev/null +++ b/backend/migrations-init/m241105_111349_add_speed_programming_url_routes.php @@ -0,0 +1,29 @@ +upsert('url_route', ['source' => 'speed', 'destination' => 'speedprogramming/default/index'], true); + $this->upsert('url_route', ['source' => 'speed/', 'destination' => 'speedprogramming/default/view'], true); + $this->upsert('url_route', ['source' => 'speed//answer', 'destination' => 'speedprogramming/default/answer'], true); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->delete('url_route', ['source' => 'speed',]); + $this->delete('url_route', ['source' => 'speed/']); + $this->delete('url_route', ['source' => 'speed//answer']); + } +} From c0d5882e8719ca7ff258e159b7bd376beec14db0 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 13:15:46 +0200 Subject: [PATCH 16/24] document the new sysconfig --- docs/Sysconfig-Keys.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Sysconfig-Keys.md b/docs/Sysconfig-Keys.md index 079045d28..d9e4cfd32 100644 --- a/docs/Sysconfig-Keys.md +++ b/docs/Sysconfig-Keys.md @@ -40,6 +40,7 @@ * `player_point_rankings`: Whether or not player point rankings will be visible on leaderboard * `player_monthly_rankings`: Whether or not player monthly rankings will be visible on leaderboard * `module_smartcity_disabled`: Whether or not the smartcity module is disabled (hides the menu from backend) +* `module_speedprogramming_disabled`: Whether or not the Speed Programming module is disabled (hides the menu from backend also) ## String and numeric key/val pairs * `event_name` A name for your event From dd4d6898d4c2c30de749b9854713bd7d5e3660e6 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 13:15:58 +0200 Subject: [PATCH 17/24] fix solution icon --- frontend/models/Stream.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/models/Stream.php b/frontend/models/Stream.php index 358078530..f3d0f6b93 100644 --- a/frontend/models/Stream.php +++ b/frontend/models/Stream.php @@ -22,7 +22,7 @@ class Stream extends StreamAR const MODEL_ICONS=[ 'headshot'=>'', 'challenge'=>'', - 'solution'=>'', + 'solution'=>'', 'treasure'=>'', 'finding'=>'', 'question'=>'', From 641e33b1f6e98d8060681c7536e4958d7b098ddf Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 13:16:47 +0200 Subject: [PATCH 18/24] add initial controller --- .../controllers/DefaultController.php | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 frontend/modules/speedprogramming/controllers/DefaultController.php diff --git a/frontend/modules/speedprogramming/controllers/DefaultController.php b/frontend/modules/speedprogramming/controllers/DefaultController.php new file mode 100644 index 000000000..f3ddcbf6f --- /dev/null +++ b/frontend/modules/speedprogramming/controllers/DefaultController.php @@ -0,0 +1,184 @@ + [ + 'class' => AccessControl::class, + 'only' => ['index', 'speed', 'view',], + 'rules' => [ + 'eventEnd' => [ + 'actions' => ['speed'], + ], + 'eventStart' => [ + 'actions' => ['speed'], + ], + + 'eventStartEnd' => [ + 'actions' => ['index', 'view', 'speed'], + ], + 'teamsAccess' => [ + 'actions' => ['index', 'speed'], + ], + 'disabledRoute' => [ + 'actions' => ['view', 'index', 'speed'], + ], + [ + 'allow' => false, + 'matchCallback'=>function($event){ + if(Yii::$app->sys->module_speedprogramming_disabled) { + return true; + } + return false; + } + ], + [ + 'allow' => true, + 'actions' => ['speed'], + 'roles' => ['@'], + 'verbs' => ['post'], + ], + [ + 'actions' => ['index'], + 'allow' => true, + 'roles' => ['@'] + ], + [ + 'actions' => ['view'], + 'allow' => true, + ], + ], + ], + ]); + } + + + /** + * Renders a Target model details view + * @return string + */ + public function actionIndex() + { + $dataProvider = new ActiveDataProvider([ + 'query' => SpeedProblem::find(), + 'pagination' => [ + 'pageSizeParam' => 'speed-perpage', + 'pageParam' => 'speed-page', + ] + ]); + + return $this->render('index', [ + 'dataProvider' => $dataProvider + ]); + } + + + /** + * Renders a Target model details view + * @return string + */ + public function actionView(int $id) + { + $speed = $this->findModel($id); + if (!$speed->active) + $this->redirect(['/speed']); + + $speedForm = new SpeedForm(); + + return $this->render('view', [ + 'problem' => $speed, + 'speedForm' => $speedForm, + ]); + } + + /** + * Finds the Target model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return SpeedProblem the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = \app\modules\speedprogramming\models\SpeedProblem::findOne($id)) !== null) { + return $model; + } + + throw new NotFoundHttpException('The requested problem does not exist.'); + } + + protected function findProfile($id) + { + if (($model = \app\models\Profile::findOne($id)) !== null) { + return $model; + } + + throw new NotFoundHttpException('The requested profile does not exist.'); + } + + + + public function actionAnswer($id) + { + $problem=$this->findModel($id); + if (SpeedSolution::findOne(['player_id' => Yii::$app->user->id, 'problem_id' => $id]) !== null) { + return $this->redirect(['/speedprogramming/default/index']); + } + + $model = new SpeedForm(); + + if (Yii::$app->request->isPost && $model->load(Yii::$app->request->post())) { + $model->file = UploadedFile::getInstance($model, 'file'); + if ($model->file && $model->validate()) { + $connection = Yii::$app->db; + $transaction = $connection->beginTransaction(); + + try { + $model->file->saveAs('uploads/player_' . Yii::$app->user->id . '-target_' . $id . '.' . $model->language); + $ss = new SpeedSolution(); + $ss->language = $model->language; + $ss->player_id = Yii::$app->user->id; + $ss->problem_id = $id; + $ss->sourcecode = file_get_contents('uploads/player_' . Yii::$app->user->id . '-target_' . $id . '.' . $model->language); + $ss->status = 'pending'; + $stream = new Stream(); + $stream->player_id = Yii::$app->user->id; + $stream->model = 'solution'; + $stream->model_id = $id; + $stream->points = 0; + $stream->title = 'Submitted a solution for ' . $problem->name . ' for approval'; + $stream->message = $stream->pubmessage = $stream->pubtitle = $stream->title; + if ($ss->save() && $stream->save(false)) + $transaction->commit(); + Yii::$app->session->setFlash('success', 'Submission accepted, please wait while our judges look into it.'); + return $this->redirect(['/speedprogramming/default/view', 'id' => $id]); + } catch (\Exception $e) { + $transaction->rollback(); + die(var_dump($e->getMessage())); + Yii::$app->session->setFlash('error', 'Failed to accept your submission.'); + return $this->redirect(['/speedprogramming/default/view', 'id' => $id]); + } + } + } + return $this->render('_speed_form', ['model' => $model]); + } +} From 8d46560ddb775122fd3cc934cb26bcd56bc59f1a Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 13:17:27 +0200 Subject: [PATCH 19/24] add frontend models --- .../speedprogramming/models/SpeedForm.php | 2 +- .../speedprogramming/models/SpeedProblem.php | 82 +++++++++++++ .../models/SpeedProblemQuery.php | 34 ++++++ .../models/SpeedProblemSearch.php | 74 ++++++++++++ .../speedprogramming/models/SpeedSolution.php | 112 ++++++++++++++++++ .../models/SpeedSolutionQuery.php | 2 +- 6 files changed, 304 insertions(+), 2 deletions(-) create mode 100644 frontend/modules/speedprogramming/models/SpeedProblem.php create mode 100644 frontend/modules/speedprogramming/models/SpeedProblemQuery.php create mode 100644 frontend/modules/speedprogramming/models/SpeedProblemSearch.php create mode 100644 frontend/modules/speedprogramming/models/SpeedSolution.php diff --git a/frontend/modules/speedprogramming/models/SpeedForm.php b/frontend/modules/speedprogramming/models/SpeedForm.php index 1d4c179e0..6746113a4 100644 --- a/frontend/modules/speedprogramming/models/SpeedForm.php +++ b/frontend/modules/speedprogramming/models/SpeedForm.php @@ -1,6 +1,6 @@ 255], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('app', 'ID'), + 'name' => Yii::t('app', 'Name'), + 'description' => Yii::t('app', 'Description'), + 'challenge_image' => Yii::t('app', 'Challenge Image'), + 'validator_image' => Yii::t('app', 'Validator Image'), + 'server' => Yii::t('app', 'Server'), + 'created_at' => Yii::t('app', 'Created At'), + 'updated_at' => Yii::t('app', 'Updated At'), + ]; + } + + /** + * Gets query for [[SpeedSolutions]]. + * + * @return \yii\db\ActiveQuery|SpeedSolutionQuery + */ + public function getSpeedSolutions() + { + return $this->hasMany(SpeedSolution::class, ['problem_id' => 'id']); + } + + /** + * {@inheritdoc} + * @return SpeedProblemQuery the active query used by this AR class. + */ + public static function find() + { + return new SpeedProblemQuery(get_called_class()); + } + public function getDifficultyText() + { + return $this->difficulty; + } +} diff --git a/frontend/modules/speedprogramming/models/SpeedProblemQuery.php b/frontend/modules/speedprogramming/models/SpeedProblemQuery.php new file mode 100644 index 000000000..0fe19370b --- /dev/null +++ b/frontend/modules/speedprogramming/models/SpeedProblemQuery.php @@ -0,0 +1,34 @@ +andWhere('[[status]]=1'); + }*/ + + /** + * {@inheritdoc} + * @return SpeedProblem[]|array + */ + public function all($db = null) + { + return parent::all($db); + } + + /** + * {@inheritdoc} + * @return SpeedProblem|array|null + */ + public function one($db = null) + { + return parent::one($db); + } +} diff --git a/frontend/modules/speedprogramming/models/SpeedProblemSearch.php b/frontend/modules/speedprogramming/models/SpeedProblemSearch.php new file mode 100644 index 000000000..6e1ad410a --- /dev/null +++ b/frontend/modules/speedprogramming/models/SpeedProblemSearch.php @@ -0,0 +1,74 @@ + $query, + ]); + + $this->load($params); + + if (!$this->validate()) { + // uncomment the following line if you do not want to return any records when validation fails + // $query->where('0=1'); + return $dataProvider; + } + + // grid filtering conditions + $query->andFilterWhere([ + 'id' => $this->id, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]); + + $query->andFilterWhere(['like', 'name', $this->name]) + ->andFilterWhere(['like', 'description', $this->description]) + ->andFilterWhere(['like', 'server', $this->server]) + ->andFilterWhere(['like', 'challenge_image', $this->challenge_image]) + ->andFilterWhere(['like', 'validator_image', $this->validator_image]); + + return $dataProvider; + } +} diff --git a/frontend/modules/speedprogramming/models/SpeedSolution.php b/frontend/modules/speedprogramming/models/SpeedSolution.php new file mode 100644 index 000000000..c32c55774 --- /dev/null +++ b/frontend/modules/speedprogramming/models/SpeedSolution.php @@ -0,0 +1,112 @@ + TimestampBehavior::class, + 'createdAtAttribute' => 'created_at', + 'updatedAtAttribute' => 'updated_at', + 'value' => new Expression('NOW()'), + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function rules() + { + return [ + [['player_id', 'problem_id'], 'required'], + [['player_id', 'problem_id', 'points'], 'integer'], + [['sourcecode'], 'string'], + [['created_at', 'updated_at'], 'safe'], + [['language', 'status'], 'string', 'max' => 255], + [['player_id'], 'exist', 'skipOnError' => true, 'targetClass' => Player::class, 'targetAttribute' => ['player_id' => 'id']], + [['problem_id'], 'exist', 'skipOnError' => true, 'targetClass' => SpeedProblem::class, 'targetAttribute' => ['problem_id' => 'id']], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'player_id' => 'Player ID', + 'problem_id' => 'Problem ID', + 'language' => 'Language', + 'sourcecode' => 'Sourcecode', + 'status' => 'Status', + 'points' => 'Points', + 'created_at' => 'Created At', + 'updated_at' => 'Updated At', + ]; + } + + /** + * Gets query for [[Player]]. + * + * @return \yii\db\ActiveQuery|PlayerQuery + */ + public function getPlayer() + { + return $this->hasOne(Player::class, ['id' => 'player_id']); + } + + /** + * Gets query for [[Problem]]. + * + * @return \yii\db\ActiveQuery|SpeedProblemQuery + */ + public function getProblem() + { + return $this->hasOne(SpeedProblem::class, ['id' => 'problem_id']); + } + + /** + * {@inheritdoc} + * @return SpeedSolutionQuery the active query used by this AR class. + */ + public static function find() + { + return new SpeedSolutionQuery(get_called_class()); + } +} diff --git a/frontend/modules/speedprogramming/models/SpeedSolutionQuery.php b/frontend/modules/speedprogramming/models/SpeedSolutionQuery.php index d2d3c9d62..248891dac 100644 --- a/frontend/modules/speedprogramming/models/SpeedSolutionQuery.php +++ b/frontend/modules/speedprogramming/models/SpeedSolutionQuery.php @@ -1,6 +1,6 @@ Date: Tue, 5 Nov 2024 13:17:46 +0200 Subject: [PATCH 20/24] use correct module namespaces --- frontend/modules/speedprogramming/Module.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/frontend/modules/speedprogramming/Module.php b/frontend/modules/speedprogramming/Module.php index 35cb2b272..40d11c38d 100644 --- a/frontend/modules/speedprogramming/Module.php +++ b/frontend/modules/speedprogramming/Module.php @@ -1,9 +1,6 @@ Date: Tue, 5 Nov 2024 13:18:45 +0200 Subject: [PATCH 21/24] add initial views --- .../views/default/_problem_card.php | 35 ++++++++++ .../speedprogramming/views/default/_speed.php | 66 +++++++++++++++++++ .../views/default/_speed_form.php | 47 +++++++++++++ .../views/default/_speed_grid.php | 21 ++++++ .../views/default/_speed_item.php | 7 ++ .../speedprogramming/views/default/index.php | 36 ++++++++++ .../speedprogramming/views/default/view.php | 30 +++++++++ 7 files changed, 242 insertions(+) create mode 100644 frontend/modules/speedprogramming/views/default/_problem_card.php create mode 100644 frontend/modules/speedprogramming/views/default/_speed.php create mode 100644 frontend/modules/speedprogramming/views/default/_speed_form.php create mode 100644 frontend/modules/speedprogramming/views/default/_speed_grid.php create mode 100644 frontend/modules/speedprogramming/views/default/_speed_item.php create mode 100644 frontend/modules/speedprogramming/views/default/index.php create mode 100644 frontend/modules/speedprogramming/views/default/view.php diff --git a/frontend/modules/speedprogramming/views/default/_problem_card.php b/frontend/modules/speedprogramming/views/default/_problem_card.php new file mode 100644 index 000000000..cc2fb5955 --- /dev/null +++ b/frontend/modules/speedprogramming/views/default/_problem_card.php @@ -0,0 +1,35 @@ +difficulty) { + case 1: + $color = 'primary'; + break; + case 2: + $color = 'warning'; + break; + case 3: + $color = 'danger'; + break; + default: + $color = 'primary'; +} +$suffix = ''; + +?> +
+ 'header-icon', + 'type' => 'card-stats', + 'encode' => false, + 'icon' => sprintf('%s', $model->name, $suffix), + 'color' => $color, + //'subtitle'=>Html::a(sprintf('%s', long2ip($model->ip)),['/speedprogramming/default/view','id'=>$model->id],['class'=>'text-primary font-weight-bold']), + 'title' => Html::a(sprintf('%s (%s)', $model->name, $model->difficultyText), ['/speedprogramming/default/view', 'id' => $model->id], ['class' => 'text-primary font-weight-bold']), + 'footer' => Html::a("Details$suffix", ['/speedprogramming/default/view', 'id' => $model->id], ['class' => 'btn btn-' . $color]), + ]); + Card::end(); ?> +
\ No newline at end of file diff --git a/frontend/modules/speedprogramming/views/default/_speed.php b/frontend/modules/speedprogramming/views/default/_speed.php new file mode 100644 index 000000000..6753eb7d7 --- /dev/null +++ b/frontend/modules/speedprogramming/views/default/_speed.php @@ -0,0 +1,66 @@ +registerJsFile('@web/js/plugins/bootstrap-selectpicker.min.js', ['depends' => ['app\assets\MaterialAsset']]); +$this->registerCssFile("@web/css/bootstrap-select.min.css", [ + 'depends' => ['app\assets\MaterialAsset'], +], 'css-print-theme'); +$this->registerJs('$.fn.selectpicker.Constructor.BootstrapVersion = "4";'); + +$headshot_icon = 'fa-skull-crossbones'; +$noheadshot_icon = 'fa-not-equal'; +$player_timer = ''; +$twmsg = sprintf('Hey check this out, %s found the solution of [%s]', $identity->isMine ? "I" : $identity->twitterHandle, $problem->name); +?> +
+
+ 'header-icon', + 'type' => 'card-stats', + 'icon' => sprintf('', $problem->id), + 'color' => 'warning', + //'subtitle' => sprintf("(%s)", ucfirst($problem->difficultyText)), + 'title' => sprintf('%s (%s)', $problem->name,ucfirst($problem->difficultyText)), + ]); + Card::end(); ?> +
+
+ +
+
+ 'header-icon', + 'type' => 'card-stats', + 'icon' => sprintf('', $identity->avtr), + 'color' => 'primary', + 'title' => $identity->owner->username . " / " . $identity->rank->ordinalPlace . " Place", + 'footer' => sprintf('
%s %s
', Twitter::widget([ + 'message' => $twmsg, + 'linkOptions' => ['class' => 'target-view-tweet', 'target' => '_blank', 'style' => 'font-size: 1.4em;', 'rel' => 'noopener noreferrer nofollow'], + ]), Html::encode($identity->bio)), + ]); + Card::end(); ?> +
+
+
+
+
+
+ Yii::$app->user->id, 'problem_id' => $problem->id]) === null): ?> + render('_speed_form', ['model' => $speedForm, 'problem' => $problem, 'identity' => $identity]); ?> + Yii::$app->user->id, 'problem_id' => $problem->id]) !== null): ?> + render('_speed_grid', ['problem' => $problem, 'identity' => $identity]); ?> + + description ?> +
+
+ +
+
\ No newline at end of file diff --git a/frontend/modules/speedprogramming/views/default/_speed_form.php b/frontend/modules/speedprogramming/views/default/_speed_form.php new file mode 100644 index 000000000..d4521fde3 --- /dev/null +++ b/frontend/modules/speedprogramming/views/default/_speed_form.php @@ -0,0 +1,47 @@ +'speedForm', + 'action' => ['/speedprogramming/default/answer','id'=>$problem->id], + 'method' => 'post', + 'options' => [ + 'enctype' => 'multipart/form-data', + 'class' => 'form-horizontal' + ] + ]) +?> +

Speed Programming Solution Submission

+
+
+ field($model, 'language')->dropDownList($model->availableLanguages, ['prompt'=>'Choose programming language', 'class'=>'form-control selectpicker', 'data-size'=>'5', 'data-style'=>"btn-info"])->hint('Choose the programming lanugage for your submission')->label(false)?> +
+
+ field($model, 'file')->label('Choose file',['class'=>'btn btn-raised btn-info btn-file'])->fileInput()->hint('Upload your source code that solves this challenge.') ?> +
+
+
+
+ 'btn btn-danger', + 'data' => [ + 'confirm' => Yii::t('app', 'Are you sure you want to finalize your submission?'), + // 'method' => 'post', + ], + ]) ?> +
+
+
+
+ + +
+registerJs( + "$('#speedform-file').on('change',function(){ + var fileName = $(this).val(); + if(fileName=='') + fileName='Choose file'; + $(this).prev().prev().html(fileName); + });", + \yii\web\View::POS_READY, + 'filehandler' +); diff --git a/frontend/modules/speedprogramming/views/default/_speed_grid.php b/frontend/modules/speedprogramming/views/default/_speed_grid.php new file mode 100644 index 000000000..eff35c6aa --- /dev/null +++ b/frontend/modules/speedprogramming/views/default/_speed_grid.php @@ -0,0 +1,21 @@ + SpeedSolution::find()->where(['problem_id'=>$problem->id])->orderBy(['created_at'=>SORT_ASC]), + 'pagination' => [ + 'pageSize' => 20, + ], +]); +?> +

Speed programming Submissions:

+ $dataProvider, + 'summary'=>false, + 'itemView' => '_speed_item', +]); +?> +
diff --git a/frontend/modules/speedprogramming/views/default/_speed_item.php b/frontend/modules/speedprogramming/views/default/_speed_item.php new file mode 100644 index 000000000..182ffbd82 --- /dev/null +++ b/frontend/modules/speedprogramming/views/default/_speed_item.php @@ -0,0 +1,7 @@ + +
+

. player->username)?>, programming language: language) ?>, status: status) ?>, points: points) ?>

+
diff --git a/frontend/modules/speedprogramming/views/default/index.php b/frontend/modules/speedprogramming/views/default/index.php new file mode 100644 index 000000000..fcf94a04f --- /dev/null +++ b/frontend/modules/speedprogramming/views/default/index.php @@ -0,0 +1,36 @@ +_fluid = "-fluid"; +$this->title = Yii::$app->sys->event_name . ' Speed programming'; +$this->_description = Yii::$app->sys->event_name . ' Speed programming problems'; +$hidden_attributes = ['id']; +?> +

+ +
+
+

Speed Programming Problems

+ + ['class' => 'list-view row'], + 'itemOptions' => [ + 'tag' => false, + ], + 'dataProvider' => $dataProvider, + 'layout' => "{items}", + 'summary' => false, + 'pager' => false, + 'itemView' => '_problem_card', + ]); + ?> + +
+
\ No newline at end of file diff --git a/frontend/modules/speedprogramming/views/default/view.php b/frontend/modules/speedprogramming/views/default/view.php new file mode 100644 index 000000000..f63f48268 --- /dev/null +++ b/frontend/modules/speedprogramming/views/default/view.php @@ -0,0 +1,30 @@ +title = Yii::$app->sys->event_name . ' Problem: ' . $problem->name . ' #' . $problem->id; +//$this->_description=$problem->description; +//$this->_image = \yii\helpers\Url::to($problem->fullLogo, 'https'); +$this->_url = \yii\helpers\Url::to(['view', 'id' => $problem->id], 'https'); +$this->_fluid = '-fluid'; +?> +

+ +
+
+ + user->isGuest) + echo $this->render('_guest', ['problem' => $problem]); + else { + echo $this->render('_speed', ['speedForm' => $speedForm, 'problem' => $problem, 'identity' => Yii::$app->user->identity->profile]); + } + ?> + + 'stream-listing', 'enablePushState' => false, 'linkSelector' => '#stream-pager a', 'formSelector' => false]); ?> + 'target-activity', 'dataProvider' => $streamProvider, 'pagerID' => 'stream-pager', 'title' => 'Target activity', 'category' => 'Latest activity on the target']); ?> + +
+
From 131ebc7e26c7a4f2af017bc58a5dd9e2430b3e36 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 13:20:05 +0200 Subject: [PATCH 22/24] add speed programming module loading and indent --- frontend/config/web.php | 442 ++++++++++++++++++++-------------------- 1 file changed, 223 insertions(+), 219 deletions(-) diff --git a/frontend/config/web.php b/frontend/config/web.php index ce7d2d5b0..2300393f4 100644 --- a/frontend/config/web.php +++ b/frontend/config/web.php @@ -1,246 +1,250 @@ 'pui2', - //'language' => 'el-GR', - 'sourceLanguage' => 'en-US', - 'name'=>'echoCTF.RED Mycenae', - 'basePath' => dirname(__DIR__), - 'charset' => 'UTF-8', - 'bootstrap' => [ - 'log', - 'app\extensions\MemcacheUrlManagerBootstrap', - 'subscription' +$config = [ + 'id' => 'pui2', + //'language' => 'el-GR', + 'sourceLanguage' => 'en-US', + 'name' => 'echoCTF.RED Mycenae', + 'basePath' => dirname(__DIR__), + 'charset' => 'UTF-8', + 'bootstrap' => [ + 'log', + 'app\extensions\MemcacheUrlManagerBootstrap', + 'subscription' + ], + 'aliases' => [ + '@bower' => '@vendor/bower-asset', + '@npm' => '@vendor/npm-asset', + '@appconfig' => realpath(dirname(__FILE__)), + ], + 'modules' => [ + 'speedprogramming' => [ + 'class' => 'app\modules\speedprogramming\Module', ], - 'aliases' => [ - '@bower' => '@vendor/bower-asset', - '@npm' => '@vendor/npm-asset', - '@appconfig' => realpath(dirname(__FILE__)), + 'api' => [ + 'class' => 'app\modules\api\Module', ], - 'modules' => [ - 'api' => [ - 'class' => 'app\modules\api\Module', - ], - 'subscription' => [ - 'class' => 'app\modules\subscription\Module', - ], - 'game' => [ - 'class' => 'app\modules\game\Module', - ], - 'challenge' => [ - 'class' => 'app\modules\challenge\Module', - ], - 'tutorial' => [ - 'class' => 'app\modules\tutorial\Module', - ], - 'help' => [ - 'class' => 'app\modules\help\Module', - ], - 'target' => [ - 'class' => 'app\modules\target\Module', - ], - 'network' => [ - 'class' => 'app\modules\network\Module', - ], - 'team' => [ - 'class' => 'app\modules\team\Module', - ], + 'subscription' => [ + 'class' => 'app\modules\subscription\Module', ], - 'components' => [ - 'i18n' => [ - 'translations' => [ - 'yii' => [ - 'class' => 'yii\i18n\PhpMessageSource', - ], - 'app*' => [ - 'class' => 'yii\i18n\PhpMessageSource', - 'basePath' => '@app/messages', - 'sourceLanguage' => 'en-US', - 'fileMap' => [ - 'app' => 'app.php', - 'app/error' => 'error.php', - ], + 'game' => [ + 'class' => 'app\modules\game\Module', + ], + 'challenge' => [ + 'class' => 'app\modules\challenge\Module', + ], + 'tutorial' => [ + 'class' => 'app\modules\tutorial\Module', + ], + 'help' => [ + 'class' => 'app\modules\help\Module', + ], + 'target' => [ + 'class' => 'app\modules\target\Module', + ], + 'network' => [ + 'class' => 'app\modules\network\Module', + ], + 'team' => [ + 'class' => 'app\modules\team\Module', + ], + ], + 'components' => [ + 'i18n' => [ + 'translations' => [ + 'yii' => [ + 'class' => 'yii\i18n\PhpMessageSource', + ], + 'app*' => [ + 'class' => 'yii\i18n\PhpMessageSource', + 'basePath' => '@app/messages', + 'sourceLanguage' => 'en-US', + 'fileMap' => [ + 'app' => 'app.php', + 'app/error' => 'error.php', ], ], ], - 'assetManager' => [ - 'bundles' => [ - 'yii\captcha\CaptchaAsset' => [ - 'sourcePath' => null, - 'js' => ['js/yii.captcha.min.js', ], - ], - 'yii\bootstrap4\BootstrapAsset' => [ - 'sourcePath' => null, - 'css' => [], - ], - 'yii\validators\ValidationAsset' => [ - 'sourcePath' => null, - 'js' => [ - 'js/yii.validation.min.js', - ], - - ], - 'yii\widgets\ActiveFormAsset'=>[ - 'sourcePath' => null, - 'js' => [ - 'js/yii.activeForm.min.js', - ], - ], - 'yii\grid\GridViewAsset'=>[ - 'sourcePath' => null, - 'js' => [ - 'js/yii.gridView.min.js', - ], - ], - 'yii\web\YiiAsset' => [ - 'sourcePath' => null, - 'js' => [ - 'js/yii.min.js', - ], - ], - 'yii\widgets\PjaxAsset'=>[ - 'sourcePath' => null, - 'js' => [ - 'js/jquery.pjax.min.js', - ], - ], - 'yii\web\JqueryAsset' => [ - 'sourcePath' => null, - 'js' => [ - 'js/jquery.min.js', - ], - ], - 'yii\bootstrap\BootstrapPluginAsset' => [ - 'sourcePath' => null, - 'js'=>[] - ], - 'yii\bootstrap\BootstrapAsset' => [ - 'sourcePath' => null, - 'css' => [], - ], - 'app\assets\MaterialAsset' => [ - 'siteTitle' => '', - 'logoMini' => '/images/logo-small.png', - 'sidebarColor' => 'echoctf', - 'sidebarBackgroundColor' => 'black', - ], - ], + ], + 'assetManager' => [ + 'bundles' => [ + 'yii\captcha\CaptchaAsset' => [ + 'sourcePath' => null, + 'js' => ['js/yii.captcha.min.js',], ], - 'view' => [ - 'class' => 'app\components\echoCTFView', - 'theme' => [ - 'basePath' => '@app/themes/material', - 'baseUrl' => '@web/themes/material', - 'pathMap' => [ - '@app/views' => '@app/themes/material', - '@app/modules' => '@app/themes/material/modules', - ], - ], + 'yii\bootstrap4\BootstrapAsset' => [ + 'sourcePath' => null, + 'css' => [], ], - 'request' => [ - 'csrfParam' => '_csrf-red', -// Hard code the domain to avoid parsing HTTP_HOST -// 'hostInfo'=>'https://echoctf.red', - 'enableCsrfValidation' => true, - 'enableCsrfCookie'=>false, - 'csrfCookie'=>['httpOnly'=>true], - 'cookieValidationKey' => $cookieValidationKey, - 'parsers' => [ - 'application/json' => 'yii\web\JsonParser', - ] + 'yii\validators\ValidationAsset' => [ + 'sourcePath' => null, + 'js' => [ + 'js/yii.validation.min.js', + ], + ], - 'sys'=> [ - 'class' => 'app\components\Sysconfig', + 'yii\widgets\ActiveFormAsset' => [ + 'sourcePath' => null, + 'js' => [ + 'js/yii.activeForm.min.js', + ], ], - 'counters'=> [ - 'class' => 'app\components\Counters', + 'yii\grid\GridViewAsset' => [ + 'sourcePath' => null, + 'js' => [ + 'js/yii.gridView.min.js', + ], ], - 'DisabledRoute'=> [ - 'class' => 'app\components\DisabledRoute', + 'yii\web\YiiAsset' => [ + 'sourcePath' => null, + 'js' => [ + 'js/yii.min.js', + ], ], - 'cache' => $cache, - 'session'=>[ - 'name' => 'red', - 'timeout'=>3600 * 12, - 'cookieParams'=>[ - 'sameSite'=> 'Strict', - 'httpOnly'=>true + 'yii\widgets\PjaxAsset' => [ + 'sourcePath' => null, + 'js' => [ + 'js/jquery.pjax.min.js', ], ], - 'user' => [ - //'class' => '\app\components\User', - 'identityClass' => '\app\models\Player', - 'enableAutoLogin' => true, - 'identityCookie' => ['name' => '_identity-red', 'httpOnly' => true, /*'sameSite'=>'Lax'*/], + 'yii\web\JqueryAsset' => [ + 'sourcePath' => null, + 'js' => [ + 'js/jquery.min.js', + ], ], - 'errorHandler' => [ - 'errorAction' => 'site/error', + 'yii\bootstrap\BootstrapPluginAsset' => [ + 'sourcePath' => null, + 'js' => [] ], - 'mailer' => [ - 'class' => 'app\components\Mailer', - 'transport' => [ - 'dsn'=>'native://default', - ], + 'yii\bootstrap\BootstrapAsset' => [ + 'sourcePath' => null, + 'css' => [], ], - 'log' => [ - 'traceLevel' => YII_DEBUG ? 3 : 0, - 'targets' => [ - [ - 'class' => 'yii\log\FileTarget', - 'levels' => ['error'], - 'except' => ['yii\web\HttpException:404'], - ], -// [ -// 'class' => 'yii\log\FileTarget', -// 'logFile' => '@runtime/logs/profile.log', -// 'logVars' => [], -// 'levels' => ['profile'], -// 'categories' => ['yii\db\Command::query'], -// 'prefix' => function($message) { -// return ''; -// } -// ] - ], - + 'app\assets\MaterialAsset' => [ + 'siteTitle' => '', + 'logoMini' => '/images/logo-small.png', + 'sidebarColor' => 'echoctf', + 'sidebarBackgroundColor' => 'black', ], - 'db' => $db, - 'urlManager' => [ - 'enablePrettyUrl' => true, - 'enableStrictParsing' => true, // only rule urls are valid - 'showScriptName' => false, - 'rules' => [ - // app/controllers/SiteController.php - '' => 'site/index', - '/' => 'site/index', - ], + ], + ], + 'view' => [ + 'class' => 'app\components\echoCTFView', + 'theme' => [ + 'basePath' => '@app/themes/material', + 'baseUrl' => '@web/themes/material', + 'pathMap' => [ + '@app/views' => '@app/themes/material', + '@app/modules' => '@app/themes/material/modules', ], + ], + ], + 'request' => [ + 'csrfParam' => '_csrf-red', + // Hard code the domain to avoid parsing HTTP_HOST + // 'hostInfo'=>'https://echoctf.red', + 'enableCsrfValidation' => true, + 'enableCsrfCookie' => false, + 'csrfCookie' => ['httpOnly' => true], + 'cookieValidationKey' => $cookieValidationKey, + 'parsers' => [ + 'application/json' => 'yii\web\JsonParser', + ] + ], + 'sys' => [ + 'class' => 'app\components\Sysconfig', + ], + 'counters' => [ + 'class' => 'app\components\Counters', + ], + 'DisabledRoute' => [ + 'class' => 'app\components\DisabledRoute', + ], + 'cache' => $cache, + 'session' => [ + 'name' => 'red', + 'timeout' => 3600 * 12, + 'cookieParams' => [ + 'sameSite' => 'Strict', + 'httpOnly' => true + ], + ], + 'user' => [ + //'class' => '\app\components\User', + 'identityClass' => '\app\models\Player', + 'enableAutoLogin' => true, + 'identityCookie' => ['name' => '_identity-red', 'httpOnly' => true, /*'sameSite'=>'Lax'*/], + ], + 'errorHandler' => [ + 'errorAction' => 'site/error', + ], + 'mailer' => [ + 'class' => 'app\components\Mailer', + 'transport' => [ + 'dsn' => 'native://default', + ], + ], + 'log' => [ + 'traceLevel' => YII_DEBUG ? 3 : 0, + 'targets' => [ + [ + 'class' => 'yii\log\FileTarget', + 'levels' => ['error'], + 'except' => ['yii\web\HttpException:404'], + ], + // [ + // 'class' => 'yii\log\FileTarget', + // 'logFile' => '@runtime/logs/profile.log', + // 'logVars' => [], + // 'levels' => ['profile'], + // 'categories' => ['yii\db\Command::query'], + // 'prefix' => function($message) { + // return ''; + // } + // ] + ], + + ], + 'db' => $db, + 'urlManager' => [ + 'enablePrettyUrl' => true, + 'enableStrictParsing' => true, // only rule urls are valid + 'showScriptName' => false, + 'rules' => [ + // app/controllers/SiteController.php + '' => 'site/index', + '/' => 'site/index', + ], ], - 'params' => $params, - 'on beforeRequest' => function ($event) { - if(\Yii::$app->sys->force_https_urls!==false){ - $_SERVER['HTTPS']='on'; + ], + 'params' => $params, + 'on beforeRequest' => function ($event) { + if (\Yii::$app->sys->force_https_urls !== false) { + $_SERVER['HTTPS'] = 'on'; + } + if (\Yii::$app->sys->maintenance === true) { + if (Yii::$app->user->isGuest || !Yii::$app->user->identity->isAdmin) { + Yii::$app->catchAll = ['site/maintenance']; } - if (\Yii::$app->sys->maintenance === true) { - if (Yii::$app->user->isGuest || !Yii::$app->user->identity->isAdmin ) { - Yii::$app->catchAll = [ 'site/maintenance' ]; - } + } + }, + 'on afterRequest' => function () { + try { + if (!Yii::$app->user->isGuest) { + \Yii::$app->cache->memcache->set("last_seen:" . \Yii::$app->user->id, time()); + \Yii::$app->cache->memcache->set("online:" . \Yii::$app->user->id, time(), intval(\Yii::$app->sys->online_timeout)); + \Yii::$app->cache->memcache->set("player_session:" . \Yii::$app->user->id, \Yii::$app->session->id, intval(\Yii::$app->sys->online_timeout)); + \Yii::$app->cache->memcache->set("player_frontend_ip:" . \Yii::$app->user->id, \Yii::$app->request->remoteIP); + return; } - }, - 'on afterRequest' => function() { - try { - if (!Yii::$app->user->isGuest) { - \Yii::$app->cache->memcache->set("last_seen:".\Yii::$app->user->id, time()); - \Yii::$app->cache->memcache->set("online:".\Yii::$app->user->id, time(), intval(\Yii::$app->sys->online_timeout)); - \Yii::$app->cache->memcache->set("player_session:".\Yii::$app->user->id, \Yii::$app->session->id, intval(\Yii::$app->sys->online_timeout)); - \Yii::$app->cache->memcache->set("player_frontend_ip:".\Yii::$app->user->id, \Yii::$app->request->remoteIP); - return; - } - } catch (\Exception $e) { } - }, + } catch (\Exception $e) { + } + }, ]; /* From cfddb84187d453ffd8e6cbf67340e8795368cb2c Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 13:25:47 +0200 Subject: [PATCH 23/24] hook speed programming --- frontend/models/Menu.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/models/Menu.php b/frontend/models/Menu.php index c26fcb7ca..a09ba3c8c 100644 --- a/frontend/models/Menu.php +++ b/frontend/models/Menu.php @@ -20,9 +20,10 @@ public static function getMenu() { ['label' => \Yii::t('app','Challenges'), 'icon'=>'extension', 'url' => ['/challenge/default/index'], 'visible'=>!Yii::$app->user->isGuest && Yii::$app->DisabledRoute->enabled('challenge/default/index'), 'active'=>\Yii::$app->controller->module->id == "challenge"], ['label' => \Yii::t('app','Teams'), 'icon'=>'people', 'url' => ['/team/default/index'], 'visible'=>(!Yii::$app->user->isGuest && array_key_exists('team',Yii::$app->modules) && Yii::$app->sys->teams!==false), 'active'=>\Yii::$app->controller->module->id === "team"], ['label' => \Yii::t('app','Networks'), 'icon'=>'cloud','url' => ['/network/default/index'],'visible'=>!Yii::$app->user->isGuest && Yii::$app->DisabledRoute->enabled('network/default/index'),'active'=>\Yii::$app->controller->module->id=="network"], + ['label' => \Yii::t('app','Tutorials'), 'icon'=>'developer_board','url' => ['/tutorial/default/index'],'visible'=>!Yii::$app->user->isGuest && Yii::$app->DisabledRoute->enabled('tutorial/default/index'),'active'=>\Yii::$app->controller->module->id=="tutorial"], + ['label' => \Yii::t('app','Speed Programming'), 'icon'=>'','url' => ['/speedprogramming/default/index'],'visible'=>!Yii::$app->user->isGuest && Yii::$app->DisabledRoute->enabled('speed/default/index') && !\Yii::$app->sys->module_speedprogramming_disabled,'active'=>\Yii::$app->controller->module->id=="speedprogramming"], ['label' => \Yii::t('app','Subscriptions'), 'icon'=>'','url' => ['/subscription/default/index'],'visible'=>!Yii::$app->user->isGuest && Yii::$app->DisabledRoute->enabled('subscription/default/index') && \Yii::$app->sys->subscriptions_menu_show===true,'active'=>\Yii::$app->controller->module->id=="subscription"], ['label' => \Yii::t('app','Leaderboards'), 'icon'=>'format_list_numbered', 'url' => ['/game/leaderboards/index'], 'visible'=>!Yii::$app->user->isGuest && Yii::$app->DisabledRoute->enabled('/game/leaderboards/index')], - ['label' => \Yii::t('app','Tutorials'), 'icon'=>'developer_board','url' => ['/tutorial/default/index'],'visible'=>!Yii::$app->user->isGuest && Yii::$app->DisabledRoute->enabled('tutorial/default/index'),'active'=>\Yii::$app->controller->module->id=="tutorial"], ['label' => \Yii::t('app','Help'), 'icon'=>'help','url' => ['/help/default/index'],'visible'=>Yii::$app->DisabledRoute->enabled('help/default/index'),'active'=>\Yii::$app->controller->module->id=="help"], // ['label' => \Yii::t('app','Rules'),'icon'=>'list_alt', 'url' => ['/help/rule/index'], 'visible'=>Yii::$app->DisabledRoute->enabled('help/rule/index')], // ['label' => \Yii::t('app','Instructions'), 'icon'=>'info', 'url' => ['/help/instruction/index'], 'visible'=>Yii::$app->DisabledRoute->enabled('help/instruction/index')], From 5be7415dea2991f02314907b1db206dcd42c7e09 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Tue, 5 Nov 2024 13:27:41 +0200 Subject: [PATCH 24/24] make the module disabled by default --- .../m241105_111349_add_speed_programming_url_routes.php | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/migrations-init/m241105_111349_add_speed_programming_url_routes.php b/backend/migrations-init/m241105_111349_add_speed_programming_url_routes.php index 8c8b6e868..b6d1d5984 100644 --- a/backend/migrations-init/m241105_111349_add_speed_programming_url_routes.php +++ b/backend/migrations-init/m241105_111349_add_speed_programming_url_routes.php @@ -15,6 +15,7 @@ public function safeUp() $this->upsert('url_route', ['source' => 'speed', 'destination' => 'speedprogramming/default/index'], true); $this->upsert('url_route', ['source' => 'speed/', 'destination' => 'speedprogramming/default/view'], true); $this->upsert('url_route', ['source' => 'speed//answer', 'destination' => 'speedprogramming/default/answer'], true); + $this->upsert('sysconfig', ['id' => 'module_speedprogramming_disabled', 'val' => 1], true); } /**