From 028d683cc1cd6750e5c5044f118b1934c4d2792d Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Thu, 5 Feb 2026 21:11:15 +0200 Subject: [PATCH 01/12] add backend administer module --- backend/modules/administer/Module.php | 24 +++++++++++++++++++ .../controllers/DefaultController.php | 20 ++++++++++++++++ .../administer/views/default/index.php | 12 ++++++++++ 3 files changed, 56 insertions(+) create mode 100644 backend/modules/administer/Module.php create mode 100644 backend/modules/administer/controllers/DefaultController.php create mode 100644 backend/modules/administer/views/default/index.php diff --git a/backend/modules/administer/Module.php b/backend/modules/administer/Module.php new file mode 100644 index 000000000..663eefaa6 --- /dev/null +++ b/backend/modules/administer/Module.php @@ -0,0 +1,24 @@ +render('index'); + } +} diff --git a/backend/modules/administer/views/default/index.php b/backend/modules/administer/views/default/index.php new file mode 100644 index 000000000..d9da31754 --- /dev/null +++ b/backend/modules/administer/views/default/index.php @@ -0,0 +1,12 @@ +
+

context->action->uniqueId ?>

+

+ This is the view content for action "context->action->id ?>". + The action belongs to the controller "context) ?>" + in the "context->module->id ?>" module. +

+

+ You may customize this page by editing the following file:
+ +

+
From a4846a8e19d4bdb3d8707715be76e7e4b2e1d249 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Thu, 5 Feb 2026 21:12:07 +0200 Subject: [PATCH 02/12] fix generic markdown break policy needed for showing events --- backend/web/css/site.css | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/web/css/site.css b/backend/web/css/site.css index 2d1d3d399..1425b963d 100644 --- a/backend/web/css/site.css +++ b/backend/web/css/site.css @@ -188,4 +188,11 @@ h6.dropdown-header,h6.dropdown-header>div.dropdown-header { padding-top: 0.1rem !important; padding-bottom: 0.1rem !important; font-weight: bold; -} \ No newline at end of file +} + +.markdown pre, +.markdown pre code { + white-space: pre-wrap; /* preserve line breaks, allow wrapping */ + word-wrap: break-word; /* force long lines to wrap */ + overflow-wrap: anywhere; /* better support for very long words */ +} From c9483a741c4598d95a27acd10db9cfed7fa5e6af Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Thu, 5 Feb 2026 21:12:34 +0200 Subject: [PATCH 03/12] shring the modules and add administer there --- backend/config/web.php | 45 +++++++++++------------------------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/backend/config/web.php b/backend/config/web.php index 0a26c2907..3ee0be62e 100644 --- a/backend/config/web.php +++ b/backend/config/web.php @@ -14,39 +14,18 @@ '@npm' => '@vendor/npm-asset', ], 'modules' => [ - 'moderation' => [ - 'class' => 'app\modules\moderation\Module' - ], - 'speedprogramming' => [ - 'class' => 'app\modules\speedprogramming\Module', - ], - 'sales' => [ - 'class' => 'app\modules\sales\Module', - ], - 'content' => [ - 'class' => 'app\modules\content\Module', - ], - 'infrastructure' => [ - 'class' => 'app\modules\infrastructure\Module', - ], - 'smartcity' => [ - 'class' => 'app\modules\smartcity\Module', - ], - 'restapi' => [ - 'class' => 'app\modules\restapi\Module', - ], - 'settings' => [ - 'class' => 'app\modules\settings\Module', - ], - 'frontend' => [ - 'class' => 'app\modules\frontend\Module', - ], - 'gameplay' => [ - 'class' => 'app\modules\gameplay\Module', - ], - 'activity' => [ - 'class' => 'app\modules\activity\Module', - ], + 'administer' => ['class' => 'app\modules\administer\Module',], + 'moderation' => ['class' => 'app\modules\moderation\Module'], + 'speedprogramming' => ['class' => 'app\modules\speedprogramming\Module',], + 'sales' => ['class' => 'app\modules\sales\Module',], + 'content' => ['class' => 'app\modules\content\Module',], + 'infrastructure' => ['class' => 'app\modules\infrastructure\Module',], + 'smartcity' => ['class' => 'app\modules\smartcity\Module',], + 'restapi' => ['class' => 'app\modules\restapi\Module',], + 'settings' => ['class' => 'app\modules\settings\Module',], + 'frontend' => ['class' => 'app\modules\frontend\Module',], + 'gameplay' => ['class' => 'app\modules\gameplay\Module',], + 'activity' => ['class' => 'app\modules\activity\Module',], ], 'components' => [ 'i18n' => [ From a5b51ee79b9036143f594a82c9f7fab7f43789ef Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Thu, 5 Feb 2026 21:33:15 +0200 Subject: [PATCH 04/12] simplify admin actions --- backend/components/BaseController.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/backend/components/BaseController.php b/backend/components/BaseController.php index 18296b29c..93850a3da 100644 --- a/backend/components/BaseController.php +++ b/backend/components/BaseController.php @@ -22,11 +22,8 @@ public function behaviors() 'class' => \yii\filters\AccessControl::class, 'rules' => [ 'adminActions'=>[ - 'allow' => true, + 'allow' => \Yii::$app->user->identity && \Yii::$app->user->identity->isAdmin, 'roles' => ['@'], - 'matchCallback' => function () { - return \Yii::$app->user->identity->isAdmin; - }, ], 'authActions'=>[ 'allow' => true, From 2dce0a2b72c4b0c6c71cd9a685ea65f41c257d3e Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Fri, 6 Feb 2026 00:08:02 +0200 Subject: [PATCH 05/12] this format is correct for this instance --- backend/components/BaseController.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/components/BaseController.php b/backend/components/BaseController.php index 93850a3da..19a53a29f 100644 --- a/backend/components/BaseController.php +++ b/backend/components/BaseController.php @@ -22,8 +22,11 @@ public function behaviors() 'class' => \yii\filters\AccessControl::class, 'rules' => [ 'adminActions'=>[ - 'allow' => \Yii::$app->user->identity && \Yii::$app->user->identity->isAdmin, + 'allow'=>true, 'roles' => ['@'], + 'matchCallback' => function () { + return \Yii::$app->user->identity->isAdmin; + }, ], 'authActions'=>[ 'allow' => true, From 13b337b06b99e429c5d856496b6e71fc9f3da4ce Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Fri, 6 Feb 2026 00:10:27 +0200 Subject: [PATCH 06/12] introduce EventModel --- .../modules/administer/models/EventModel.php | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 backend/modules/administer/models/EventModel.php diff --git a/backend/modules/administer/models/EventModel.php b/backend/modules/administer/models/EventModel.php new file mode 100644 index 000000000..6fcdc37a5 --- /dev/null +++ b/backend/modules/administer/models/EventModel.php @@ -0,0 +1,98 @@ +Name; + } + public function rules() + { + return [[ + [ + 'Name', + 'Definer', + 'Time_zone', + 'Type', + 'Execute_at', + 'Interval_value', + 'Interval_field', + 'Starts', + 'Ends', + 'Status', + 'On_completion', + 'Created', + 'Last_altered', + 'Last_executed', + 'Event_comment', + 'Originator', + 'character_set_client', + 'collation_connection', + 'Database_collation' + ], + 'safe' + ]]; + } + + public static function getAll() + { + $rows = Yii::$app->db->createCommand('SHOW EVENTS')->queryAll(); + $models = []; + + foreach ($rows as $row) { + $model = new self(); + $model->Name = $row['Name']; + $model->Definer = $row['Definer']; + $model->Time_zone = $row['Time zone']; + $model->Type = $row['Type']; + $model->Execute_at = $row['Execute at']; + $model->Interval_value = $row['Interval value']; + $model->Interval_field = $row['Interval field']; + $model->Starts = $row['Starts']; + $model->Ends = $row['Ends']; + $model->Status = $row['Status']; + $model->Originator = $row['Originator']; + $model->character_set_client = $row['character_set_client']; + $model->collation_connection = $row['collation_connection']; + $model->Database_collation = $row['Database Collation']; + $models[] = $model; + } + + return $models; + } + + public static function dropEvent($name) + { + return Yii::$app->db->createCommand("DROP EVENT `$name`")->execute(); + } +} From 898813606a6ca375a707402ae7157742a98f3050 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Fri, 6 Feb 2026 00:10:47 +0200 Subject: [PATCH 07/12] add crud --- .../controllers/EventsController.php | 147 ++++++++++++++++++ .../administer/views/events/create.php | 29 ++++ .../modules/administer/views/events/index.php | 34 ++++ .../administer/views/events/update.php | 29 ++++ .../modules/administer/views/events/view.php | 42 +++++ 5 files changed, 281 insertions(+) create mode 100644 backend/modules/administer/controllers/EventsController.php create mode 100644 backend/modules/administer/views/events/create.php create mode 100644 backend/modules/administer/views/events/index.php create mode 100644 backend/modules/administer/views/events/update.php create mode 100644 backend/modules/administer/views/events/view.php diff --git a/backend/modules/administer/controllers/EventsController.php b/backend/modules/administer/controllers/EventsController.php new file mode 100644 index 000000000..f135aba79 --- /dev/null +++ b/backend/modules/administer/controllers/EventsController.php @@ -0,0 +1,147 @@ + [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + 'authActions' => [ + 'allow' => \Yii::$app->user->identity && \Yii::$app->user->identity->isAdmin, + 'actions' => ['index', 'view'], + 'roles' => ['@'], + ], + ], + ], + 'verbs' => [ + 'class' => VerbFilter::class, + 'actions' => [ + 'delete' => ['POST'], + ], + ], + ]); + } + + public function actionIndex() + { + $dataProvider = new \yii\data\ArrayDataProvider([ + 'allModels' => EventModel::getAll(), + 'pagination' => ['pageSize' => 10], + ]); + + return $this->render('index', ['dataProvider' => $dataProvider]); + } + + public function actionView($name) + { + $model = $this->findModel($name); + + // Get actual event code + $eventCode = Yii::$app->db + ->createCommand("SHOW CREATE EVENT `{$model->Name}`") + ->queryOne(); + + return $this->render('view', [ + 'model' => $model, + 'eventCode' => $eventCode['Create Event'] ?? 'N/A', + ]); + } + + public function actionCreate() + { + $model = new EventModel(); + + if ($model->load(Yii::$app->request->post())) { + $newSql = str_replace(["\r\n", "\r"], "\n", $model->Event_comment); + + try { + Yii::$app->db->createCommand($newSql)->execute(); + + if (preg_match('/EVENT\s+`([^`]+)`/i', $newSql, $matches)) { + $eventName = $matches[1]; + Yii::$app->session->setFlash('success', "Event created successfully."); + return $this->redirect(['view', 'name' => $eventName]); + } else { + Yii::$app->session->setFlash('success', "Event created successfully."); + return $this->redirect(['index']); + } + } catch (\Exception $e) { + Yii::$app->session->setFlash('error', "Failed to create event: " . $e->getMessage()); + } + } + + return $this->render('create', [ + 'model' => $model, + ]); + } + + public function actionUpdate($name) + { + $model = $this->findModel($name); + + // Get current CREATE EVENT SQL + $oldSql = Yii::$app->db + ->createCommand("SHOW CREATE EVENT `{$model->Name}`") + ->queryOne()['Create Event']; + + if ($model->load(Yii::$app->request->post())) { + $newSql = str_replace(["\r\n", "\r"], "\n", $model->Event_comment); + + try { + // Try to drop old and create new event + Yii::$app->db->createCommand("DROP EVENT IF EXISTS `{$model->Name}`")->execute(); + Yii::$app->db->createCommand($newSql)->execute(); + + Yii::$app->session->setFlash('success', "Event updated successfully."); + return $this->redirect(['view', 'name' => $model->Name]); + } catch (\Exception $e) { + // If failed, recreate the old event + try { + Yii::$app->db->createCommand($oldSql)->execute(); + } catch (\Exception $rollback) { + Yii::$app->session->setFlash('error', "Failed to update event and rollback also failed: " . $rollback->getMessage()); + return $this->redirect(['view', 'name' => $model->Name]); + } + + Yii::$app->session->setFlash('error', "Failed to update event, rolled back to previous version: " . $e->getMessage()); + return $this->redirect(['view', 'name' => $model->Name]); + } + } + + // Pass current SQL to the form + $model->Event_comment = $oldSql; + + return $this->render('update', [ + 'model' => $model, + ]); + } + + public function actionDelete($name) + { + EventModel::dropEvent($name); + return $this->redirect(['index']); + } + + protected function findModel($name) + { + $events = EventModel::getAll(); + foreach ($events as $event) { + if ($event->Name === $name) { + return $event; + } + } + throw new NotFoundHttpException("Event not found: $name"); + } +} diff --git a/backend/modules/administer/views/events/create.php b/backend/modules/administer/views/events/create.php new file mode 100644 index 000000000..964622a87 --- /dev/null +++ b/backend/modules/administer/views/events/create.php @@ -0,0 +1,29 @@ +title = 'Create Event'; +$this->params['breadcrumbs'][] = ['label' => 'Events', 'url' => ['index']]; +$this->params['breadcrumbs'][] = $this->title; + +?> +
+ +

title) ?>

+ + +
+ + + + field($model, 'Event_comment')->textarea([ + 'rows' => 20, + 'style' => 'font-family: monospace;', + ])->label('Event code'); ?> + +
+ 'btn btn-success']); ?> +
+ +
+
\ No newline at end of file diff --git a/backend/modules/administer/views/events/index.php b/backend/modules/administer/views/events/index.php new file mode 100644 index 000000000..640d2702c --- /dev/null +++ b/backend/modules/administer/views/events/index.php @@ -0,0 +1,34 @@ +title = 'Events'; +$this->params['breadcrumbs'][] = ['label' => $this->title, 'url' => ['index']]; +?> +
+

title) ?>

+ +

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

+ + $dataProvider, + 'columns' => [ + 'Name', + 'Status', + 'Time_zone', + 'Execute_at', + 'Starts', + 'Ends', + 'Interval_value', + 'Interval_field', + [ + 'class' => 'yii\grid\ActionColumn', + 'urlCreator' => function ($action, $model) { + return [$action, 'name' => $model->Name]; + }, + ], + ], +]);?> +
diff --git a/backend/modules/administer/views/events/update.php b/backend/modules/administer/views/events/update.php new file mode 100644 index 000000000..3de46113d --- /dev/null +++ b/backend/modules/administer/views/events/update.php @@ -0,0 +1,29 @@ +title = 'Update event: ' . $model->Name; +$this->params['breadcrumbs'][] = ['label' => 'Events', 'url' => ['index']]; +$this->params['breadcrumbs'][] = ['label' => $model->Name, 'url' => ['view', 'name' => $model->Name]]; +$this->params['breadcrumbs'][] = 'Update'; +?> +
+ +

title) ?>

+
+ + + field($model, 'Name')->textInput(['readonly' => true]); ?> + + field($model, 'Event_comment')->textarea([ + 'rows' => 20, + 'style' => 'font-family: monospace;', + ]); ?> +
+ 'btn btn-success']); ?> +
+ + +
+
\ No newline at end of file diff --git a/backend/modules/administer/views/events/view.php b/backend/modules/administer/views/events/view.php new file mode 100644 index 000000000..f4b1bebd7 --- /dev/null +++ b/backend/modules/administer/views/events/view.php @@ -0,0 +1,42 @@ +title = "View event {$model->Name}"; +$this->params['breadcrumbs'][] = ['label' => 'Events', 'url' => ['index']]; +$this->params['breadcrumbs'][] = $this->title; +\yii\web\YiiAsset::register($this); +?> +
+

title) ?>

+

+ $model->Name], ['class' => 'btn btn-primary']); ?> + $model->Name], [ + 'class' => 'btn btn-danger', + 'data-method' => 'post', + 'data-confirm' => 'Are you sure?', + ]); ?> +

+ + + $model, + 'attributes' => [ + 'Name', + 'Status', + 'Time_zone', + 'Execute_at', + 'Starts', + 'Ends', + 'Interval_value', + 'Interval_field', + 'Event_comment' + ], + ]); ?> + +
+

Event SQL Code

+ formatter->asMarkdown('```sql' . "\n" . $eventCode . "\n" . '```'); ?> +
+
\ No newline at end of file From 23fc14ed2b2fe1608ffdd406bf03636aca73fc7a Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Fri, 6 Feb 2026 00:11:12 +0200 Subject: [PATCH 08/12] add migrations --- .../m251207_102327_populate_mui_menu.php | 65 ++++++++++--------- ...60205_183413_add_administer_menu_items.php | 47 ++++++++++++++ 2 files changed, 83 insertions(+), 29 deletions(-) create mode 100644 backend/migrations/m260205_183413_add_administer_menu_items.php diff --git a/backend/migrations-init/m251207_102327_populate_mui_menu.php b/backend/migrations-init/m251207_102327_populate_mui_menu.php index 80c3c2fc2..77198aa64 100644 --- a/backend/migrations-init/m251207_102327_populate_mui_menu.php +++ b/backend/migrations-init/m251207_102327_populate_mui_menu.php @@ -42,15 +42,15 @@ class m251207_102327_populate_mui_menu extends Migration 'icon' => 'fas fa-money-check-alt', 'visibility' => 'admin', 'items' => [ - ['label' => 'Sales Dashboard', 'url' => ['/sales/default/index'], 'visibility' => 'admin' ], - ['label' => 'Customers', 'url' => ['/sales/player-customer/index'], 'visibility' => 'admin' ,], - ['label' => 'Subscriptions', 'url' => ['/sales/player-subscription/index'], 'visibility' => 'admin' ,], - ['label' => 'Player Products', 'url' => ['/sales/player-product/index'], 'visibility' => 'admin' ,], - ['label' => 'Products', 'url' => ['/sales/product/index'], 'visibility' => 'admin' ,], - ['label' => 'Prices', 'url' => ['/sales/price/index'], 'visibility' => 'admin' ,], - ['label' => 'Product Networks', 'url' => ['/sales/product-network/index'], 'visibility' => 'admin' ,], - ['label' => 'Payment History', 'url' => ['/sales/player-payment-history/index'], 'visibility' => 'admin' ,], - ['label' => 'Webhook', 'url' => ['/sales/stripe-webhook/index'], 'visibility' => 'admin' ,], + ['label' => 'Sales Dashboard', 'url' => ['/sales/default/index'], 'visibility' => 'admin'], + ['label' => 'Customers', 'url' => ['/sales/player-customer/index'], 'visibility' => 'admin',], + ['label' => 'Subscriptions', 'url' => ['/sales/player-subscription/index'], 'visibility' => 'admin',], + ['label' => 'Player Products', 'url' => ['/sales/player-product/index'], 'visibility' => 'admin',], + ['label' => 'Products', 'url' => ['/sales/product/index'], 'visibility' => 'admin',], + ['label' => 'Prices', 'url' => ['/sales/price/index'], 'visibility' => 'admin',], + ['label' => 'Product Networks', 'url' => ['/sales/product-network/index'], 'visibility' => 'admin',], + ['label' => 'Payment History', 'url' => ['/sales/player-payment-history/index'], 'visibility' => 'admin',], + ['label' => 'Webhook', 'url' => ['/sales/stripe-webhook/index'], 'visibility' => 'admin',], ] ], [ @@ -58,10 +58,10 @@ class m251207_102327_populate_mui_menu extends Migration 'url' => ['/speedprogramming/default/index'], 'icon' => 'fas fa-money-check-alt', 'visibility' => 'admin', - 'enabled'=>'0', + 'enabled' => '0', 'items' => [ - ['label' => 'Problems', 'url' => ['/speedprogramming/speed-problem/index'], 'visibility' => 'admin' ,], - ['label' => 'Solutions', 'url' => ['/speedprogramming/default/index'], 'visibility' => 'admin' ,], + ['label' => 'Problems', 'url' => ['/speedprogramming/speed-problem/index'], 'visibility' => 'admin',], + ['label' => 'Solutions', 'url' => ['/speedprogramming/default/index'], 'visibility' => 'admin',], ] ], [ @@ -108,7 +108,7 @@ class m251207_102327_populate_mui_menu extends Migration 'label' => ' SmartCity', 'url' => ['/smartcity/default/index'], 'visibility' => 'user', - 'enabled'=>0, + 'enabled' => 0, 'items' => [ ['label' => 'Infrastructure', 'url' => ['/smartcity/infrastructure/index'], 'visibility' => 'user',], ['label' => 'Infrastructure Targets', 'url' => ['/smartcity/infrastructure-target/index'], 'visibility' => 'user',], @@ -185,10 +185,10 @@ class m251207_102327_populate_mui_menu extends Migration ['label' => 'Hints', 'url' => ['/gameplay/hint/index'], 'visibility' => 'admin',], ['label' => 'Achievements', 'url' => ['/gameplay/achievement/index'], 'visibility' => 'admin',], ['label' => 'Badges', 'url' => ['/gameplay/badge/index'], 'visibility' => 'admin',], - ['label' => 'Tutorials', 'url' => ['/gameplay/tutorial/index'], 'visibility' => 'admin','enabled'=>0], - ['label' => 'Tutorial Target', 'url' => ['/gameplay/tutorial-target/index'], 'visibility' => 'admin','enabled'=>0], - ['label' => 'Tutorial Tasks', 'url' => ['/gameplay/tutorial-task/index'], 'visibility' => 'admin','enabled'=>0], - ['label' => 'Tutorial Task Dependencies', 'url' => ['/gameplay/tutorial-task-dependency/index'], 'visibility' => 'admin','enabled'=>0], + ['label' => 'Tutorials', 'url' => ['/gameplay/tutorial/index'], 'visibility' => 'admin', 'enabled' => 0], + ['label' => 'Tutorial Target', 'url' => ['/gameplay/tutorial-target/index'], 'visibility' => 'admin', 'enabled' => 0], + ['label' => 'Tutorial Tasks', 'url' => ['/gameplay/tutorial-task/index'], 'visibility' => 'admin', 'enabled' => 0], + ['label' => 'Tutorial Task Dependencies', 'url' => ['/gameplay/tutorial-task-dependency/index'], 'visibility' => 'admin', 'enabled' => 0], ['label' => 'Credentials', 'url' => ['/gameplay/credential/index'], 'visibility' => 'admin',], ], ], @@ -216,27 +216,34 @@ class m251207_102327_populate_mui_menu extends Migration ['label' => 'Users', 'url' => ['/settings/user/index'], 'visibility' => 'admin',], ], ], + [ + 'label' => ' Administer', + 'url' => ['/administer'], + 'visibility' => 'admin', + 'items' => [ + ['label' => 'Main', 'url' => ['/administer/default/index'], 'visibility' => 'admin',], + ['label' => 'Events', 'url' => ['/administer/events/index'], 'visibility' => 'admin',], + ], + ], + ]; /** * {@inheritdoc} */ public function safeUp() { - $root=0; - foreach($this->items as $menu) - { - $this->insert('mui_menu',['label'=>$menu['label'],'url'=>$menu['url'][0],'visibility'=>$menu['visibility'],'sort_order'=>$root++,'enabled'=>intval(@$menu['enabled'] ?? 1 )]); - $id=Yii::$app->db->getLastInsertID(); - $child=0; - foreach($menu['items'] as $item) - { - if(is_array($item)) - $this->insert('mui_menu',['label'=>$item['label'],'url'=>$item['url'][0],'visibility'=>$item['visibility'],'parent_id'=>$id,'sort_order'=>$child++,'enabled'=>intval(@$item['enabled'] ?? (@$menu['enabled'] ?? 1) )]); + $root = 0; + foreach ($this->items as $menu) { + $this->insert('mui_menu', ['label' => $menu['label'], 'url' => $menu['url'][0], 'visibility' => $menu['visibility'], 'sort_order' => $root++, 'enabled' => intval(@$menu['enabled'] ?? 1)]); + $id = Yii::$app->db->getLastInsertID(); + $child = 0; + foreach ($menu['items'] as $item) { + if (is_array($item)) + $this->insert('mui_menu', ['label' => $item['label'], 'url' => $item['url'][0], 'visibility' => $item['visibility'], 'parent_id' => $id, 'sort_order' => $child++, 'enabled' => intval(@$item['enabled'] ?? (@$menu['enabled'] ?? 1))]); else - $this->insert('mui_menu',['label'=>$item,'visibility'=>'admin','parent_id'=>$id,'sort_order'=>$child++]); + $this->insert('mui_menu', ['label' => $item, 'visibility' => 'admin', 'parent_id' => $id, 'sort_order' => $child++]); } } - } /** diff --git a/backend/migrations/m260205_183413_add_administer_menu_items.php b/backend/migrations/m260205_183413_add_administer_menu_items.php new file mode 100644 index 000000000..01bf8f5d1 --- /dev/null +++ b/backend/migrations/m260205_183413_add_administer_menu_items.php @@ -0,0 +1,47 @@ + ' Administer', + 'url' => ['/administer'], + 'visibility' => 'admin', + 'items' => [ + ['label' => 'Main', 'url' => ['/administer/default/index'], 'visibility' => 'admin',], + ['label' => 'Events', 'url' => ['/administer/events/index'], 'visibility' => 'admin',], + ], + ], + + ]; + /** + * {@inheritdoc} + */ + public function safeUp() + { + $root = 8; + foreach ($this->items as $menu) { + $this->insert('mui_menu', ['label' => $menu['label'], 'url' => $menu['url'][0], 'visibility' => $menu['visibility'], 'sort_order' => $root++, 'enabled' => intval(@$menu['enabled'] ?? 1)]); + $id = Yii::$app->db->getLastInsertID(); + $child = 0; + foreach ($menu['items'] as $item) { + if (is_array($item)) + $this->insert('mui_menu', ['label' => $item['label'], 'url' => $item['url'][0], 'visibility' => $item['visibility'], 'parent_id' => $id, 'sort_order' => $child++, 'enabled' => intval(@$item['enabled'] ?? (@$menu['enabled'] ?? 1))]); + else + $this->insert('mui_menu', ['label' => $item, 'visibility' => 'admin', 'parent_id' => $id, 'sort_order' => $child++]); + } + } + } + + + /** + * {@inheritdoc} + */ + public function safeDown() + { + echo "m260205_183413_add_administer_menu_items cannot be reverted.\n"; + } + +} From bd23f133b8622e74baf5434340dc80b629865080 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Fri, 6 Feb 2026 00:25:45 +0200 Subject: [PATCH 09/12] fix behavior for default controller --- .../controllers/DefaultController.php | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/backend/modules/administer/controllers/DefaultController.php b/backend/modules/administer/controllers/DefaultController.php index cf88449d9..4d1b7f0eb 100644 --- a/backend/modules/administer/controllers/DefaultController.php +++ b/backend/modules/administer/controllers/DefaultController.php @@ -2,19 +2,39 @@ namespace app\modules\administer\controllers; -use yii\web\Controller; +use Yii; +use yii\web\NotFoundHttpException; +use yii\web\Response; +use yii\helpers\ArrayHelper; +use yii\filters\VerbFilter; /** * Default controller for the `administer` module */ -class DefaultController extends Controller +class DefaultController extends \app\components\BaseController { - /** - * Renders the index view for the module - * @return string - */ - public function actionIndex() - { - return $this->render('index'); - } + public function behaviors() + { + return ArrayHelper::merge(parent::behaviors(), [ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + 'authActions' => [ + 'allow' => \Yii::$app->user->identity && \Yii::$app->user->identity->isAdmin, + 'actions' => ['index'], + 'roles' => ['@'], + ], + ], + ], + ]); + } + + /** + * Renders the index view for the module + * @return string + */ + public function actionIndex() + { + return $this->render('index'); + } } From 07031f98f2f0b41a73529360f6ec0a34aacc9c41 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Fri, 6 Feb 2026 00:25:59 +0200 Subject: [PATCH 10/12] update the default view --- .../modules/administer/views/default/index.php | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/backend/modules/administer/views/default/index.php b/backend/modules/administer/views/default/index.php index d9da31754..99249df50 100644 --- a/backend/modules/administer/views/default/index.php +++ b/backend/modules/administer/views/default/index.php @@ -1,12 +1,6 @@
-

context->action->uniqueId ?>

-

- This is the view content for action "context->action->id ?>". - The action belongs to the controller "context) ?>" - in the "context->module->id ?>" module. -

-

- You may customize this page by editing the following file:
- -

-
+

Administer

+

+ Administer differrent operations of your platform installation. +

+ \ No newline at end of file From 79d3098f6bf82f08070db7a67e3c69f98eaa18ce Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Fri, 6 Feb 2026 00:26:11 +0200 Subject: [PATCH 11/12] add the missing migrations for people installing from scratch --- schemas/echoCTF.sql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/schemas/echoCTF.sql b/schemas/echoCTF.sql index 9f1a8f810..478e13806 100644 --- a/schemas/echoCTF.sql +++ b/schemas/echoCTF.sql @@ -559,7 +559,9 @@ INSERT INTO `migration` values ('m251126_122227_add_private_network_url_route',1), ('m251207_100542_create_mui_menu_table',1), ('m251214_115824_create_ws_token_table',1), - ('m251215_105113_create_event_ws_token_expiration',1); + ('m251215_105113_create_event_ws_token_expiration',1), + ('m260125_090222_drop_trigger_after_delete_on_player_token',1), + ('m260205_183413_add_administer_menu_items',1); -- -- Table structure for table `migration_red` From bb613aff3f30e5ea6d03e969c3661f4269706933 Mon Sep 17 00:00:00 2001 From: Pantelis Roditis Date: Fri, 6 Feb 2026 00:33:25 +0200 Subject: [PATCH 12/12] rename classes --- backend/modules/administer/views/events/update.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/modules/administer/views/events/update.php b/backend/modules/administer/views/events/update.php index 3de46113d..74546595b 100644 --- a/backend/modules/administer/views/events/update.php +++ b/backend/modules/administer/views/events/update.php @@ -8,10 +8,10 @@ $this->params['breadcrumbs'][] = ['label' => $model->Name, 'url' => ['view', 'name' => $model->Name]]; $this->params['breadcrumbs'][] = 'Update'; ?> -
+

title) ?>

-
+
field($model, 'Name')->textInput(['readonly' => true]); ?>