├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── UPGRADE.md ├── composer.json └── src ├── ActionEvent.php ├── CrudController.php ├── actions ├── Action.php ├── Callback.php ├── Create.php ├── Delete.php ├── FlushCache.php ├── Index.php ├── ModelFormTrait.php ├── Position.php ├── Restore.php ├── RoleCreate.php ├── RoleUpdate.php ├── SafeDelete.php ├── SoftDelete.php ├── Update.php ├── VariationCreate.php ├── VariationUpdate.php └── View.php ├── behaviors ├── ContextModelControlBehavior.php ├── ModelControlBehavior.php └── action │ ├── RoleBehavior.php │ └── VariationBehavior.php ├── gii ├── crud │ ├── Generator.php │ ├── default │ │ ├── controller.php │ │ ├── search.php │ │ └── views │ │ │ ├── _form.php │ │ │ ├── _search.php │ │ │ ├── create.php │ │ │ ├── index.php │ │ │ ├── update.php │ │ │ └── view.php │ └── form.php └── mainframe │ ├── Generator.php │ ├── default │ ├── controller.php │ ├── layouts │ │ ├── main.php │ │ ├── mainMenu.php │ │ └── overall.php │ └── views │ │ ├── error.php │ │ ├── index.php │ │ └── login.php │ └── form.php ├── grid ├── ActionColumn.php ├── DeleteStatusColumn.php ├── PositionColumn.php └── VariationColumn.php ├── messages ├── config.php ├── en │ └── yii2tech-admin.php ├── es │ └── yii2tech-admin.php ├── it │ └── yii2tech-admin.php ├── pt-BR │ └── yii2tech-admin.php ├── ru │ └── yii2tech-admin.php └── uk │ └── yii2tech-admin.php └── widgets ├── ActionAlert.php ├── Alert.php ├── ButtonContextMenu.php └── Nav.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Yii 2 Admin pack extension Change Log 2 | ===================================== 3 | 4 | 1.1.0, April 9, 2018 5 | -------------------- 6 | 7 | - Enh #18: Removed `yii\base\Object::className()` in favor of native PHP syntax `::class`, which does not trigger autoloading (klimov-paul) 8 | 9 | 10 | 1.0.3, April 9, 2018 11 | -------------------- 12 | 13 | - Enh #17: Widget `yii2tech\admin\widgets\Alert` now determines alert type using wildcard match (klimov-paul) 14 | 15 | 16 | 1.0.2, November 3, 2017 17 | ----------------------- 18 | 19 | - Enh #15: Added controller and actions references in doc blocks for view files generated by Gii 'CRUD' generator (naffiq, klimov-paul) 20 | 21 | 22 | 1.0.1, April 19, 2017 23 | --------------------- 24 | 25 | - Enh #14: Widget `yii2tech\admin\widgets\ActionAlert` created (klimov-paul) 26 | - Enh: Widget `yii2tech\admin\widgets\Alert` refactored allowing `run()` method to return an output instead of using `echo` (klimov-paul) 27 | 28 | 29 | 1.0.0, January 6, 2017 30 | ---------------------- 31 | 32 | - Initial release. 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The Yii framework is free software. It is released under the terms of 2 | the following BSD License. 3 | 4 | Copyright © 2015 by Yii2tech (https://github.com/yii2tech) 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | * Neither the name of Yii2tech nor the names of its 18 | contributors may be used to endorse or promote products derived 19 | from this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

Admin pack for Yii 2

6 |
7 |

8 | 9 | This extension provides controllers, actions, widgets and other tools for administration panel creation in Yii2 project. 10 | 11 | For license information check the [LICENSE](LICENSE.md)-file. 12 | 13 | [![Latest Stable Version](https://poser.pugx.org/yii2tech/admin/v/stable.png)](https://packagist.org/packages/yii2tech/admin) 14 | [![Total Downloads](https://poser.pugx.org/yii2tech/admin/downloads.png)](https://packagist.org/packages/yii2tech/admin) 15 | [![Build Status](https://travis-ci.org/yii2tech/admin.svg?branch=master)](https://travis-ci.org/yii2tech/admin) 16 | 17 | 18 | Installation 19 | ------------ 20 | 21 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 22 | 23 | Either run 24 | 25 | ``` 26 | php composer.phar require --prefer-dist yii2tech/admin 27 | ``` 28 | 29 | or add 30 | 31 | ```json 32 | "yii2tech/admin": "*" 33 | ``` 34 | 35 | to the require section of your composer.json. 36 | 37 | 38 | Usage 39 | ----- 40 | 41 | This extension provides controllers, actions, widgets and other tools for administration panel creation in Yii2 project. 42 | These tools are meant to be used together for the rapid web application administration panel composition. 43 | 44 | This package supports usage of following extensions: 45 | 46 | - [yii2tech/ar-position](https://github.com/yii2tech/ar-position) 47 | - [yii2tech/ar-role](https://github.com/yii2tech/ar-role) 48 | - [yii2tech/ar-search](https://github.com/yii2tech/ar-search) 49 | - [yii2tech/ar-softdelete](https://github.com/yii2tech/ar-softdelete) 50 | - [yii2tech/ar-variation](https://github.com/yii2tech/ar-variation) 51 | 52 | > Note: none of these extensions is required by default, you'll need to install them yourself, if needed. 53 | 54 | 55 | ## Actions 56 | 57 | This extension provides several independent action classes, which provides particular operation support: 58 | 59 | - [[\yii2tech\admin\actions\Index]] - displays the models listing with search support. 60 | - [[\yii2tech\admin\actions\Create]] - supports creation of the new model using web form. 61 | - [[\yii2tech\admin\actions\Update]] - supports updating of the existing model using web form. 62 | - [[\yii2tech\admin\actions\Delete]] - performs the deleting of the existing record. 63 | - [[\yii2tech\admin\actions\View]] - displays an existing model. 64 | - [[\yii2tech\admin\actions\SoftDelete]] - performs the "soft" deleting of the existing record. 65 | - [[\yii2tech\admin\actions\SafeDelete]] - performs the "safe" deleting of the existing record. 66 | - [[\yii2tech\admin\actions\Restore]] - performs the restoration of the "soft" deleted record. 67 | - [[\yii2tech\admin\actions\Callback]] - allows invocation of specified method of the model. 68 | - [[\yii2tech\admin\actions\Position]] - allows change custom sort position of the particular model. 69 | - [[\yii2tech\admin\actions\VariationCreate]] - supports creation of the new model with variations using web form. 70 | - [[\yii2tech\admin\actions\VariationUpdate]] - supports updating of the new model with variations using web form. 71 | - [[\yii2tech\admin\actions\RoleCreate]] - supports creation of the new model with role using web form. 72 | - [[\yii2tech\admin\actions\RoleUpdate]] - supports updating of the new model with role using web form. 73 | 74 | Please refer to the particular action class for more details. 75 | 76 | For example CRUD controller based on provided actions may look like following: 77 | 78 | ```php 79 | namespace app\controllers; 80 | 81 | use yii\web\Controller; 82 | 83 | class ItemController extends Controller 84 | { 85 | public function actions() 86 | { 87 | return [ 88 | 'index' => [ 89 | 'class' => \yii2tech\admin\actions\Index::class, 90 | 'newSearchModel' => function () { 91 | return new ItemSearch(); 92 | }, 93 | ], 94 | 'view' => [ 95 | 'class' => \yii2tech\admin\actions\View::class, 96 | ], 97 | 'create' => [ 98 | 'class' => \yii2tech\admin\actions\Create::class, 99 | ], 100 | 'update' => [ 101 | 'class' => \yii2tech\admin\actions\Update::class, 102 | ], 103 | 'delete' => [ 104 | 'class' => \yii2tech\admin\actions\Delete::class, 105 | ], 106 | ]; 107 | } 108 | 109 | public function findModel($id) 110 | { 111 | if (($model = Item::findOne($id)) !== null) { 112 | return $model; 113 | } 114 | throw new NotFoundHttpException('The requested page does not exist.'); 115 | } 116 | 117 | public function newModel() 118 | { 119 | return new Item(); 120 | } 121 | } 122 | ``` 123 | 124 | 125 | ## Controllers 126 | 127 | This extension provides several predefined controllers, which can be used as a base controller classes 128 | while creating particular controllers: 129 | 130 | - [[\yii2tech\admin\CrudController]] - implements a common set of actions for supporting CRUD for ActiveRecord. 131 | 132 | Please refer to the particular controller class for more details. 133 | 134 | 135 | ## Widgets 136 | 137 | This extension provides several widgets, which simplifies view composition for the typical use cases: 138 | 139 | - [[\yii2tech\admin\widgets\Alert]] - renders a message from session flash. 140 | - [[\yii2tech\admin\widgets\ActionAlert]] - renders an action proposition based on particular condition, usually a session flag. 141 | - [[\yii2tech\admin\widgets\ButtonContextMenu]] - simplifies rendering of the context links such as 'update', 'view', 'delete' etc. 142 | - [[\yii2tech\admin\widgets\Nav]] - enhanced version of [[\yii\bootstrap\Nav]], which simplifies icon rendering. 143 | 144 | Also several enhancements for the [[\yii\grid\GridView]] are available: 145 | 146 | - [[\yii2tech\admin\grid\ActionColumn]] - simplifies composition of the action buttons 147 | - [[\yii2tech\admin\grid\DeleteStatusColumn]] - serves for the 'soft-deleted' status displaying 148 | - [[\yii2tech\admin\grid\PositionColumn]] - provides simple interface for the model custom sort position switching 149 | - [[\yii2tech\admin\grid\VariationColumn]] - allows displaying of the variation column values 150 | 151 | 152 | ## Using Gii 153 | 154 | This extension provides a code generators, which can be integrated with yii 'gii' module. 155 | In order to enable them, you should adjust your application configuration in following way: 156 | 157 | ```php 158 | return [ 159 | //.... 160 | 'modules' => [ 161 | // ... 162 | 'gii' => [ 163 | 'class' => yii\gii\Module::class, 164 | 'generators' => [ 165 | 'adminMainFrame' => [ 166 | 'class' => yii2tech\admin\gii\mainframe\Generator::class 167 | ], 168 | 'adminCrud' => [ 169 | 'class' => yii2tech\admin\gii\crud\Generator::class 170 | ] 171 | ], 172 | ], 173 | ] 174 | ]; 175 | ``` 176 | 177 | "MainFrame" generator creates a basic admin panel code, which includes layout files, main controller 178 | file and basic view files. The created structure is necessary for the correct rendering of the code created 179 | by "Admin CRUD" generator. 180 | 181 | "Admin CRUD" generator is similar to regular "CRUD" generator, but it generates code, which uses tools from 182 | this extension, so the result code is much more clean. 183 | 184 | 185 | ## Internationalization 186 | 187 | All text and messages introduced in this extension are translatable under category 'yii2tech-admin'. 188 | You may use translations provided within this extension, using following application configuration: 189 | 190 | ```php 191 | return [ 192 | 'components' => [ 193 | 'i18n' => [ 194 | 'translations' => [ 195 | 'yii2tech-admin' => [ 196 | 'class' => yii\i18n\PhpMessageSource::class, 197 | 'basePath' => '@yii2tech/admin/messages', 198 | ], 199 | // ... 200 | ], 201 | ], 202 | // ... 203 | ], 204 | // ... 205 | ]; 206 | ``` 207 | -------------------------------------------------------------------------------- /UPGRADE.md: -------------------------------------------------------------------------------- 1 | Upgrading Instructions for Admin pack Extension for Yii 2 2 | ========================================================= 3 | 4 | !!!IMPORTANT!!! 5 | 6 | The following upgrading instructions are cumulative. That is, 7 | if you want to upgrade from version A to version C and there is 8 | version B between A and C, you need to following the instructions 9 | for both A and B. 10 | 11 | Upgrade from 1.0.3 12 | ------------------ 13 | 14 | * PHP requirements were raised to 5.6. Make sure your code is updated accordingly. 15 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yii2tech/admin", 3 | "description": "Admin pack (actions, widgets, etc) for Yii2", 4 | "keywords": ["yii2", "admin", "administration", "crud"], 5 | "type": "yii2-extension", 6 | "license": "BSD-3-Clause", 7 | "support": { 8 | "issues": "https://github.com/yii2tech/admin/issues", 9 | "forum": "http://www.yiiframework.com/forum/", 10 | "wiki": "https://github.com/yii2tech/admin/wiki", 11 | "source": "https://github.com/yii2tech/admin" 12 | }, 13 | "authors": [ 14 | { 15 | "name": "Paul Klimov", 16 | "email": "klimov.paul@gmail.com" 17 | } 18 | ], 19 | "require": { 20 | "php": ">=5.6.0", 21 | "yiisoft/yii2": "~2.0.14" 22 | }, 23 | "require-dev": { 24 | "yiisoft/yii2-bootstrap": ">=2.0.5" 25 | }, 26 | "suggest": { 27 | "yiisoft/yii2-bootstrap": "you need this package, if you wish to use provided widgets" 28 | }, 29 | "repositories": [ 30 | { 31 | "type": "composer", 32 | "url": "https://asset-packagist.org" 33 | } 34 | ], 35 | "autoload": { 36 | "psr-4": {"yii2tech\\admin\\": "src"} 37 | }, 38 | "extra": { 39 | "branch-alias": { 40 | "dev-master": "1.0.x-dev" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ActionEvent.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 1.0 15 | */ 16 | class ActionEvent extends \yii\base\ActionEvent 17 | { 18 | /** 19 | * @var \yii\base\Model|null associated model instance. 20 | */ 21 | public $model; 22 | } -------------------------------------------------------------------------------- /src/CrudController.php: -------------------------------------------------------------------------------- 1 | 32 | * @since 1.0 33 | */ 34 | class CrudController extends Controller 35 | { 36 | /** 37 | * @var string the model class name. This property must be set. 38 | * The model class must implement [[ActiveRecordInterface]]. 39 | */ 40 | public $modelClass; 41 | /** 42 | * @var string class name of the model which should be used as search model. 43 | * If not set it will be composed using [[modelClass]]. 44 | */ 45 | public $searchModelClass; 46 | /** 47 | * @var string the scenario used for updating a model. 48 | * @see Model::scenarios() 49 | */ 50 | public $updateScenario = Model::SCENARIO_DEFAULT; 51 | /** 52 | * @var string the scenario used for creating a model. 53 | * @see Model::scenarios() 54 | */ 55 | public $createScenario = Model::SCENARIO_DEFAULT; 56 | 57 | 58 | /** 59 | * {@inheritdoc} 60 | */ 61 | public function behaviors() 62 | { 63 | return [ 64 | 'model' => [ 65 | 'class' => ModelControlBehavior::class, 66 | 'modelClass' => $this->modelClass, 67 | 'searchModelClass' => $this->searchModelClass, 68 | ], 69 | 'access' => [ 70 | 'class' => AccessControl::class, 71 | 'rules' => $this->accessRules(), 72 | ], 73 | 'verbs' => [ 74 | 'class' => VerbFilter::class, 75 | 'actions' => [ 76 | 'delete' => ['post'], 77 | ], 78 | ], 79 | ]; 80 | } 81 | 82 | /** 83 | * Returns the access rules for this controller. 84 | * This is method is a shortcut, allowing quick adjustment of the [[AccessControl]] filter attached at [[behaviors()]]. 85 | * Be careful in case you override [[behaviors()]] method, since it may loose configuration provided by this method. 86 | * @return array list of access rules. See [[AccessControl::rules]] for details about rule specification. 87 | */ 88 | public function accessRules() 89 | { 90 | return [ 91 | [ 92 | 'allow' => true, 93 | 'roles' => ['@'], 94 | ], 95 | ]; 96 | } 97 | 98 | /** 99 | * {@inheritdoc} 100 | */ 101 | public function actions() 102 | { 103 | return [ 104 | 'index' => [ 105 | 'class' => actions\Index::class, 106 | ], 107 | 'view' => [ 108 | 'class' => actions\View::class, 109 | ], 110 | 'create' => [ 111 | 'class' => actions\Create::class, 112 | 'scenario' => $this->createScenario, 113 | ], 114 | 'update' => [ 115 | 'class' => actions\Update::class, 116 | 'scenario' => $this->updateScenario, 117 | ], 118 | 'delete' => [ 119 | 'class' => actions\Delete::class, 120 | ], 121 | ]; 122 | } 123 | } -------------------------------------------------------------------------------- /src/actions/Action.php: -------------------------------------------------------------------------------- 1 | 22 | * @since 1.0 23 | */ 24 | class Action extends \yii\base\Action 25 | { 26 | /** 27 | * @var callable a PHP callable that will be called to return the model corresponding 28 | * to the specified primary key value. If not set, [[findModel()]] will be used instead. 29 | * The signature of the callable should be: 30 | * 31 | * ```php 32 | * function ($id, $action) { 33 | * // $id is the primary key value. If composite primary key, the key values 34 | * // will be separated by comma. 35 | * // $action is the action object currently running 36 | * } 37 | * ``` 38 | * 39 | * The callable should return the model found, or throw an exception if not found. 40 | */ 41 | public $findModel; 42 | /** 43 | * @var string ID of the controller action, which user should be redirected to on success. 44 | * This property overrides the value set by [[setReturnAction()]] method. 45 | * @see getReturnAction() 46 | * @see returnUrl 47 | */ 48 | public $returnAction; 49 | /** 50 | * @var string|array|callable URL, which user should be redirected to on success. 51 | * This could be a plain string URL, URL array configuration or callable, which returns actual URL. 52 | * The signature for the callable is following: 53 | * 54 | * ``` 55 | * string|array function (Model $model) {} 56 | * ``` 57 | * 58 | * Note: actual list of the callable arguments may vary depending on particular action class. 59 | * 60 | * Note: this option takes precedence over [[returnAction]] related logic. 61 | * 62 | * @see returnAction 63 | */ 64 | public $returnUrl; 65 | 66 | 67 | /** 68 | * Returns the data model based on the primary key given. 69 | * If the data model is not found, a 404 HTTP exception will be raised. 70 | * @param string $id the ID of the model to be loaded. If the model has a composite primary key, 71 | * the ID must be a string of the primary key values separated by commas. 72 | * The order of the primary key values should follow that returned by the `primaryKey()` method 73 | * of the model. 74 | * @return ActiveRecordInterface|Model the model found 75 | * @throws NotFoundHttpException if the model cannot be found 76 | * @throws InvalidConfigException on invalid configuration 77 | */ 78 | public function findModel($id) 79 | { 80 | if ($this->findModel !== null) { 81 | return call_user_func($this->findModel, $id, $this); 82 | } elseif ($this->controller->hasMethod('findModel')) { 83 | return call_user_func([$this->controller, 'findModel'], $id, $this); 84 | } 85 | throw new InvalidConfigException('Either "' . get_class($this) . '::$findModel" must be set or controller must declare method "findModel()".'); 86 | } 87 | 88 | /** 89 | * Checks whether action with specified ID exists in owner controller. 90 | * @param string $id action ID. 91 | * @return boolean whether action exists or not. 92 | */ 93 | public function actionExists($id) 94 | { 95 | $inlineActionMethodName = 'action' . Inflector::camelize($id); 96 | if (method_exists($this->controller, $inlineActionMethodName)) { 97 | return true; 98 | } 99 | if (array_key_exists($id, $this->controller->actions())) { 100 | return true; 101 | } 102 | return false; 103 | } 104 | 105 | /** 106 | * Sets the return action ID. 107 | * @param string|null $actionId action ID, if not set current action will be used. 108 | */ 109 | public function setReturnAction($actionId = null) 110 | { 111 | if ($actionId === null) { 112 | $actionId = $this->id; 113 | } 114 | if (strpos($actionId, '/') === false) { 115 | $actionId = $this->controller->getUniqueId() . '/' . $actionId; 116 | } 117 | $sessionKey = '__adminReturnAction'; 118 | Yii::$app->getSession()->set($sessionKey, $actionId); 119 | } 120 | 121 | /** 122 | * Returns the ID of action, which should be used for return redirect. 123 | * If action belongs to another controller or does not exist in current controller - 'index' is returned. 124 | * @param string $defaultActionId default action ID. 125 | * @return string action ID. 126 | */ 127 | public function getReturnAction($defaultActionId = 'index') 128 | { 129 | if ($this->returnAction !== null) { 130 | return $this->returnAction; 131 | } 132 | 133 | $sessionKey = '__adminReturnAction'; 134 | $actionId = Yii::$app->getSession()->get($sessionKey, $defaultActionId); 135 | $actionId = trim($actionId, '/'); 136 | if ($actionId === 'index') { 137 | return $actionId; 138 | } 139 | 140 | if (strpos($actionId, '/') !== false) { 141 | $controllerId = StringHelper::dirname($actionId); 142 | if ($controllerId !== $this->controller->getUniqueId()) { 143 | return 'index'; 144 | } 145 | $actionId = StringHelper::basename($actionId); 146 | } 147 | 148 | if (!$this->actionExists($actionId)) { 149 | return 'index'; 150 | } 151 | 152 | return $actionId; 153 | } 154 | 155 | /** 156 | * @param string $defaultActionId default action ID. 157 | * @param ActiveRecordInterface|Model|null $model model being processed by action. 158 | * @return array|string URL 159 | */ 160 | public function createReturnUrl($defaultActionId = 'index', $model = null) 161 | { 162 | if ($this->returnUrl !== null) { 163 | if (is_string($this->returnUrl)) { 164 | return $this->returnUrl; 165 | } 166 | if (!is_callable($this->returnUrl, true)) { 167 | return $this->returnUrl; 168 | } 169 | 170 | $args = func_get_args(); 171 | array_shift($args); 172 | return call_user_func_array($this->returnUrl, $args); 173 | } 174 | 175 | $actionId = $this->getReturnAction($defaultActionId); 176 | $queryParams = Yii::$app->request->getQueryParams(); 177 | unset($queryParams['id']); 178 | $url = array_merge( 179 | [$actionId], 180 | $queryParams 181 | ); 182 | if (is_object($model) && in_array($actionId, ['view', 'update'], true)) { 183 | $url = array_merge( 184 | $url, 185 | ['id' => implode(',', array_values($model->getPrimaryKey(true)))] 186 | ); 187 | } 188 | return $url; 189 | } 190 | 191 | /** 192 | * Sets a flash message. 193 | * @param string|array|null $message flash message(s) to be set. 194 | * If plain string is passed, it will be used as a message with the key 'success'. 195 | * You may specify multiple messages as an array, if element name is not integer, it will be used as a key, 196 | * otherwise 'success' will be used as key. 197 | * If empty value passed, no flash will be set. 198 | * Particular message value can be a PHP callback, which should return actual message. Such callback, should 199 | * have following signature: 200 | * 201 | * ```php 202 | * function (array $params) { 203 | * // return string 204 | * } 205 | * ``` 206 | * 207 | * @param array $params extra params for the message parsing in format: key => value. 208 | */ 209 | public function setFlash($message, $params = []) 210 | { 211 | if (empty($message)) { 212 | return; 213 | } 214 | 215 | $session = Yii::$app->session; 216 | 217 | foreach ((array)$message as $key => $value) { 218 | if (is_scalar($value)) { 219 | $value = preg_replace_callback("/{(\\w+)}/", function ($matches) use ($params) { 220 | $paramName = $matches[1]; 221 | return isset($params[$paramName]) ? $params[$paramName] : $paramName; 222 | }, $value); 223 | } else { 224 | $value = call_user_func($value, $params); 225 | } 226 | 227 | if (is_int($key)) { 228 | $session->setFlash('success', $value); 229 | } else { 230 | $session->setFlash($key, $value); 231 | } 232 | } 233 | } 234 | } -------------------------------------------------------------------------------- /src/actions/Callback.php: -------------------------------------------------------------------------------- 1 | 17 | * @since 1.0 18 | */ 19 | class Callback extends Action 20 | { 21 | /** 22 | * @var string|callable name of the model method, which should be invoked 23 | * or callback, which should be executed for the found model. 24 | * The signature of the callable should be: 25 | * 26 | * ```php 27 | * function ($model) { 28 | * } 29 | * ``` 30 | */ 31 | public $callback; 32 | /** 33 | * @var string|array|null flash message to be set on success. 34 | * @see Action::setFlash() for details on how setup flash. 35 | */ 36 | public $flash; 37 | 38 | 39 | /** 40 | * Invokes configured method on the specified model. 41 | * @param string $id the primary key of the model. 42 | * @return mixed response. 43 | * @throws InvalidConfigException on invalid configuration. 44 | */ 45 | public function run($id) 46 | { 47 | $model = $this->findModel($id); 48 | 49 | if ($this->callback === null) { 50 | throw new InvalidConfigException('"' . get_class($this) . '::$callback" must be set.'); 51 | } 52 | if (is_string($this->callback)) { 53 | call_user_func([$model, $this->callback]); 54 | } else { 55 | call_user_func($this->callback, $model); 56 | } 57 | 58 | $this->setFlash($this->flash, ['id' => $id, 'model' => $model]); 59 | 60 | return $this->controller->redirect($this->createReturnUrl('view', $model)); 61 | } 62 | } -------------------------------------------------------------------------------- /src/actions/Create.php: -------------------------------------------------------------------------------- 1 | 22 | * @since 1.0 23 | */ 24 | class Create extends Action 25 | { 26 | use ModelFormTrait; 27 | 28 | /** 29 | * @var string name of the view, which should be rendered 30 | */ 31 | public $view = 'create'; 32 | /** 33 | * @var callable a PHP callable that will be called to create the new model. 34 | * If not set, [[newModel()]] will be used instead. 35 | * The signature of the callable should be: 36 | * 37 | * ```php 38 | * function ($action) { 39 | * // $action is the action object currently running 40 | * } 41 | * ``` 42 | * 43 | * The callable should return the new model instance. 44 | */ 45 | public $newModel; 46 | /** 47 | * @var boolean|callable provides control for model default values populating. 48 | * If set to `false` - no default value population will be performed. 49 | * If set to `true` - it will invoke `loadDefaultValues()` method on model. 50 | * You can set this as a callable of following signature: 51 | * 52 | * ```php 53 | * function ($model) { 54 | * // populate default values. 55 | * } 56 | * ``` 57 | */ 58 | public $loadDefaultValues = false; 59 | 60 | 61 | /** 62 | * Creates new model instance. 63 | * @return ActiveRecordInterface|Model new model instance. 64 | * @throws InvalidConfigException on invalid configuration. 65 | */ 66 | public function newModel() 67 | { 68 | if ($this->newModel !== null) { 69 | return call_user_func($this->newModel, $this); 70 | } elseif ($this->controller->hasMethod('newModel')) { 71 | return call_user_func([$this->controller, 'newModel'], $this); 72 | } 73 | throw new InvalidConfigException('Either "' . get_class($this) . '::$newModel" must be set or controller must declare method "newModel()".'); 74 | } 75 | 76 | /** 77 | * Creates new record. 78 | * @return mixed response 79 | */ 80 | public function run() 81 | { 82 | $model = $this->newModel(); 83 | $model->scenario = $this->scenario; 84 | 85 | if ($this->load($model, Yii::$app->request->post())) { 86 | if (Yii::$app->request->isAjax) { 87 | Yii::$app->response->format = Response::FORMAT_JSON; 88 | return $this->performAjaxValidation($model); 89 | } 90 | if ($model->save()) { 91 | $this->setFlash($this->flash, ['model' => $model]); 92 | return $this->controller->redirect($this->createReturnUrl('view', $model)); 93 | } 94 | } else { 95 | $this->loadModelDefaultValues($model); 96 | } 97 | 98 | return $this->controller->render($this->view, [ 99 | 'model' => $model, 100 | ]); 101 | } 102 | 103 | /** 104 | * Populates given model with the default values. 105 | * @param Model $model model to be processed. 106 | */ 107 | protected function loadModelDefaultValues($model) 108 | { 109 | if ($this->loadDefaultValues === false) { 110 | return; 111 | } 112 | if ($this->loadDefaultValues === true) { 113 | $model->loadDefaultValues(); 114 | } else { 115 | call_user_func($this->loadDefaultValues, $model); 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /src/actions/Delete.php: -------------------------------------------------------------------------------- 1 | 16 | * @since 1.0 17 | */ 18 | class Delete extends Action 19 | { 20 | /** 21 | * {@inheritdoc} 22 | */ 23 | public $returnAction = 'index'; 24 | /** 25 | * @var string|array|null flash message to be set on success. 26 | * @see Action::setFlash() for details on how setup flash. 27 | */ 28 | public $flash; 29 | 30 | 31 | /** 32 | * Deletes a model. 33 | * @param mixed $id id of the model to be deleted. 34 | * @return mixed response. 35 | */ 36 | public function run($id) 37 | { 38 | $model = $this->findModel($id); 39 | 40 | $model->delete(); 41 | 42 | $this->setFlash($this->flash, ['id' => $id, 'model' => $model]); 43 | 44 | return $this->controller->redirect($this->createReturnUrl('index', $model)); 45 | } 46 | } -------------------------------------------------------------------------------- /src/actions/FlushCache.php: -------------------------------------------------------------------------------- 1 | 23 | * @since 1.0 24 | */ 25 | class FlushCache extends Action 26 | { 27 | /** 28 | * @var array|Cache|string|null cache component(s), which should be flushed. 29 | * If not set action will flush all cache components found in current application. 30 | * Each cache component can be specified as application component name, instance of [[Cache]] or its configuration. 31 | * For example: 32 | * 33 | * ```php 34 | * [ 35 | * 'cache', 36 | * 'frontendCache' => [ 37 | * 'class' => \yii\caching\DbCache::class, 38 | * 'cacheTable' => '{{%frontendCache}}', 39 | * ], 40 | * 'objectCache' => new \yii\caching\DbCache(['cacheTable' => '{{%objectCache}}']), 41 | * ] 42 | * ``` 43 | */ 44 | public $cache; 45 | /** 46 | * @var string|array|null flash message to be set on success. 47 | * @see Action::setFlash() for details on how setup flash. 48 | */ 49 | public $flash; 50 | /** 51 | * @var string|array the default return URL in case it was not set previously. 52 | * This URL will be used only in case automatic determine of return URL failed. 53 | */ 54 | public $returnUrl; 55 | 56 | 57 | /** 58 | * Flushes associated cache components. 59 | * @param string|array|null $name name of the cache component(s), which should be flushed. 60 | * @return mixed response. 61 | */ 62 | public function run($name = null) 63 | { 64 | $filter = (array)$name; 65 | 66 | if (empty($this->cache)) { 67 | $caches = $this->findCaches($filter); 68 | } else { 69 | $caches = (array)$this->cache; 70 | 71 | if (!empty($filter)) { 72 | foreach ($caches as $key => $value) { 73 | if (is_int($key)) { 74 | if (!in_array($value, $filter)) { 75 | unset($caches[$key]); 76 | } 77 | } else { 78 | if (!in_array($key, $filter)) { 79 | unset($caches[$key]); 80 | } 81 | } 82 | } 83 | } 84 | } 85 | 86 | $this->flushCaches($caches); 87 | 88 | $this->setFlash($this->flash); 89 | 90 | return $this->goBack(); 91 | } 92 | 93 | /** 94 | * Returns array of caches in the system, keys are cache components names, values are class names. 95 | * @param array $cachesNames caches to be found 96 | * @return array 97 | */ 98 | private function findCaches(array $cachesNames = []) 99 | { 100 | $caches = []; 101 | $components = Yii::$app->getComponents(); 102 | $findAll = ($cachesNames === []); 103 | 104 | foreach ($components as $name => $component) { 105 | if (!$findAll && !in_array($name, $cachesNames)) { 106 | continue; 107 | } 108 | 109 | if ($component instanceof Cache) { 110 | $caches[] = $name; 111 | } elseif (is_array($component) && isset($component['class']) && $this->isCacheClass($component['class'])) { 112 | $caches[] = $name; 113 | } elseif (is_string($component) && $this->isCacheClass($component)) { 114 | $caches[] = $name; 115 | } 116 | } 117 | 118 | return $caches; 119 | } 120 | 121 | /** 122 | * Checks if given class is a Cache class. 123 | * @param string $className class name. 124 | * @return boolean 125 | */ 126 | private function isCacheClass($className) 127 | { 128 | return is_subclass_of($className, Cache::class); 129 | } 130 | 131 | /** 132 | * Flushes given caches list. 133 | * @param array $caches caches list 134 | */ 135 | private function flushCaches(array $caches) 136 | { 137 | foreach ($caches as $cache) { 138 | if (is_scalar($cache)) { 139 | Yii::$app->get($cache)->flush(); 140 | } elseif ($cache instanceof Cache) { 141 | $cache->flush(); 142 | } else { 143 | Yii::createObject($cache)->flush(); 144 | } 145 | } 146 | } 147 | 148 | /** 149 | * Redirects the browser to the last visited page. 150 | * If such page can not be indicated [[returnUrl]] will be used. 151 | * @return mixed response. 152 | */ 153 | private function goBack() 154 | { 155 | $referrerUrl = Yii::$app->getRequest()->getReferrer(); 156 | if ($referrerUrl !== null) { 157 | return $this->controller->redirect($referrerUrl); 158 | } 159 | return $this->controller->goBack($this->returnUrl); 160 | } 161 | } -------------------------------------------------------------------------------- /src/actions/Index.php: -------------------------------------------------------------------------------- 1 | 18 | * @since 1.0 19 | */ 20 | class Index extends Action 21 | { 22 | /** 23 | * @var callable a PHP callable that will be called to create the new search model. 24 | * If not set, [[newSearchModel()]] will be used instead. 25 | * The signature of the callable should be: 26 | * 27 | * ```php 28 | * function ($action) { 29 | * // $action is the action object currently running 30 | * } 31 | * ``` 32 | * 33 | * The callable should return the new model instance. 34 | */ 35 | public $newSearchModel; 36 | /** 37 | * @var callable a PHP callable that will be called to prepare a data provider that 38 | * should return a collection of the models. If not set, [[prepareDataProvider()]] will be used instead. 39 | * The signature of the callable should be: 40 | * 41 | * ```php 42 | * function ($searchModel, $action) { 43 | * // $searchModel the search model instance 44 | * // $action is the action object currently running 45 | * } 46 | * ``` 47 | * 48 | * The callable should return an instance of [[\yii\data\DataProviderInterface]]. 49 | */ 50 | public $prepareDataProvider; 51 | /** 52 | * @var string name of the view, which should be rendered 53 | */ 54 | public $view = 'index'; 55 | 56 | 57 | /** 58 | * Creates new search model instance. 59 | * @return Model new model instance. 60 | * @throws InvalidConfigException on invalid configuration. 61 | */ 62 | public function newSearchModel() 63 | { 64 | if ($this->newSearchModel !== null) { 65 | return call_user_func($this->newSearchModel, $this); 66 | } elseif ($this->controller->hasMethod('newSearchModel')) { 67 | return call_user_func([$this->controller, 'newSearchModel'], $this); 68 | } 69 | throw new InvalidConfigException('Either "' . get_class($this) . '::$newSearchModel" must be set or controller must declare method "newSearchModel()".'); 70 | } 71 | 72 | /** 73 | * Displays models list. 74 | * @return mixed response. 75 | */ 76 | public function run() 77 | { 78 | $searchModel = $this->newSearchModel(); 79 | $dataProvider = $this->prepareDataProvider($searchModel); 80 | 81 | $this->setReturnAction(); 82 | 83 | return $this->controller->render($this->view, [ 84 | 'searchModel' => $searchModel, 85 | 'dataProvider' => $dataProvider, 86 | ]); 87 | } 88 | 89 | /** 90 | * Prepares the data provider that should return the requested collection of the models. 91 | * @param Model $searchModel search model instance. 92 | * @return \yii\data\DataProviderInterface data provider instance. 93 | */ 94 | protected function prepareDataProvider($searchModel) 95 | { 96 | if ($this->prepareDataProvider !== null) { 97 | return call_user_func($this->prepareDataProvider, $searchModel, $this); 98 | } 99 | 100 | return $searchModel->search(Yii::$app->request->queryParams); 101 | } 102 | } -------------------------------------------------------------------------------- /src/actions/ModelFormTrait.php: -------------------------------------------------------------------------------- 1 | 19 | * @since 1.0 20 | */ 21 | trait ModelFormTrait 22 | { 23 | /** 24 | * @var string the scenario to be assigned to the new model before it is validated and saved. 25 | */ 26 | public $scenario = Model::SCENARIO_DEFAULT; 27 | /** 28 | * @var string|array|null flash message to be set on success. 29 | * @see Action::setFlash() for details on how setup flash. 30 | */ 31 | public $flash; 32 | 33 | 34 | /** 35 | * Populates the model with input data. 36 | * @param Model $model model instance. 37 | * @param array $data the data array to load, typically `$_POST` or `$_GET`. 38 | * @return boolean whether expected forms are found in `$data`. 39 | */ 40 | protected function load($model, $data) 41 | { 42 | /* @var $this Action */ 43 | $event = new ActionEvent($this, [ 44 | 'model' => $model, 45 | 'result' => $model->load($data), 46 | ]); 47 | $this->trigger('afterDataLoad', $event); 48 | 49 | return $event->result; 50 | } 51 | 52 | /** 53 | * Performs AJAX validation of the model via [[ActiveForm::validate()]]. 54 | * @param Model $model main model. 55 | * @return array the error message array indexed by the attribute IDs. 56 | */ 57 | protected function performAjaxValidation($model) 58 | { 59 | /* @var $this Action */ 60 | $event = new ActionEvent($this, [ 61 | 'model' => $model, 62 | 'result' => ActiveForm::validate($model), 63 | ]); 64 | 65 | $this->trigger('afterAjaxValidate', $event); 66 | 67 | return $event->result; 68 | } 69 | } -------------------------------------------------------------------------------- /src/actions/Position.php: -------------------------------------------------------------------------------- 1 | 21 | * @since 1.0 22 | */ 23 | class Position extends Action 24 | { 25 | /** 26 | * @var string name of the query param, which is used for new model position specification. 27 | */ 28 | public $positionParam = 'at'; 29 | 30 | 31 | /** 32 | * Updates existing record specified by id. 33 | * @param mixed $id id of the model to be deleted. 34 | * @return mixed response. 35 | * @throws BadRequestHttpException on invalid request. 36 | */ 37 | public function run($id) 38 | { 39 | $position = Yii::$app->request->getQueryParam($this->positionParam, null); 40 | if (empty($position)) { 41 | throw new BadRequestHttpException(Yii::t('yii', '{attribute} cannot be blank.', ['attribute' => $this->positionParam])); 42 | } 43 | 44 | $model = $this->findModel($id); 45 | 46 | $this->positionModel($model, $position); 47 | 48 | return $this->respondSuccess($model); 49 | } 50 | 51 | /** 52 | * @param \yii\db\ActiveRecordInterface|\yii2tech\ar\position\PositionBehavior $model 53 | * @param $position 54 | * @throws BadRequestHttpException 55 | */ 56 | protected function positionModel($model, $position) 57 | { 58 | switch (strtolower($position)) { 59 | case 'up': 60 | case 'prev': 61 | $model->movePrev(); 62 | break; 63 | case 'down': 64 | case 'next': 65 | $model->moveNext(); 66 | break; 67 | case 'top': 68 | case 'first': 69 | $model->moveFirst(); 70 | break; 71 | case 'bottom': 72 | case 'last': 73 | $model->moveLast(); 74 | break; 75 | default: 76 | if (is_numeric($position)) { 77 | $model->moveToPosition($position); 78 | } else { 79 | throw new BadRequestHttpException(Yii::t('yii', '{attribute} is invalid.', ['attribute' => $this->positionParam])); 80 | } 81 | } 82 | } 83 | 84 | /** 85 | * Composes success response. 86 | * @param \yii\db\ActiveRecordInterface $model 87 | * @return mixed response. 88 | */ 89 | protected function respondSuccess($model) 90 | { 91 | if (Yii::$app->request->isAjax) { 92 | Yii::$app->response->format = Response::FORMAT_JSON; 93 | return [ 94 | 'success' => true 95 | ]; 96 | } 97 | 98 | return $this->controller->redirect($this->createReturnUrl('view', $model)); 99 | } 100 | 101 | /** 102 | * {@inheritdoc} 103 | */ 104 | public function createReturnUrl($defaultActionId = 'index', $model = null) 105 | { 106 | if ($this->returnUrl !== null) { 107 | return parent::createReturnUrl($defaultActionId, $model); 108 | } 109 | 110 | $url = parent::createReturnUrl($defaultActionId, $model); 111 | unset($url[$this->positionParam]); 112 | return $url; 113 | } 114 | } -------------------------------------------------------------------------------- /src/actions/Restore.php: -------------------------------------------------------------------------------- 1 | 18 | * @since 1.0 19 | */ 20 | class Restore extends Action 21 | { 22 | /** 23 | * @var string|array|null flash message to be set on success. 24 | * @see Action::setFlash() for details on how setup flash. 25 | */ 26 | public $flash; 27 | 28 | 29 | /** 30 | * Deletes a model. 31 | * @param mixed $id id of the model to be deleted. 32 | * @return mixed response. 33 | */ 34 | public function run($id) 35 | { 36 | $model = $this->findModel($id); 37 | 38 | $model->restore(); 39 | 40 | $this->setFlash($this->flash, ['id' => $id, 'model' => $model]); 41 | 42 | return $this->controller->redirect($this->createReturnUrl('view', $model)); 43 | } 44 | } -------------------------------------------------------------------------------- /src/actions/RoleCreate.php: -------------------------------------------------------------------------------- 1 | 19 | * @since 1.0 20 | */ 21 | class RoleCreate extends Create 22 | { 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function behaviors() 27 | { 28 | return [ 29 | [ 30 | 'class' => RoleBehavior::class 31 | ] 32 | ]; 33 | } 34 | } -------------------------------------------------------------------------------- /src/actions/RoleUpdate.php: -------------------------------------------------------------------------------- 1 | 19 | * @since 1.0 20 | */ 21 | class RoleUpdate extends Update 22 | { 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function behaviors() 27 | { 28 | return [ 29 | [ 30 | 'class' => RoleBehavior::class 31 | ] 32 | ]; 33 | } 34 | } -------------------------------------------------------------------------------- /src/actions/SafeDelete.php: -------------------------------------------------------------------------------- 1 | 19 | * @since 1.0 20 | */ 21 | class SafeDelete extends Delete 22 | { 23 | /** 24 | * Deletes a model. 25 | * @param mixed $id id of the model to be deleted. 26 | * @return mixed response. 27 | */ 28 | public function run($id) 29 | { 30 | $model = $this->findModel($id); 31 | 32 | $model->safeDelete(); 33 | 34 | $this->setFlash($this->flash, ['id' => $id, 'model' => $model]); 35 | 36 | return $this->controller->redirect($this->createReturnUrl('index', $model)); 37 | } 38 | } -------------------------------------------------------------------------------- /src/actions/SoftDelete.php: -------------------------------------------------------------------------------- 1 | 19 | * @since 1.0 20 | */ 21 | class SoftDelete extends Delete 22 | { 23 | /** 24 | * Deletes a model. 25 | * @param mixed $id id of the model to be deleted. 26 | * @return mixed response. 27 | */ 28 | public function run($id) 29 | { 30 | $model = $this->findModel($id); 31 | 32 | $model->softDelete(); 33 | 34 | $this->setFlash($this->flash, ['id' => $id, 'model' => $model]); 35 | 36 | return $this->controller->redirect($this->createReturnUrl('index', $model)); 37 | } 38 | } -------------------------------------------------------------------------------- /src/actions/Update.php: -------------------------------------------------------------------------------- 1 | 19 | * @since 1.0 20 | */ 21 | class Update extends Action 22 | { 23 | use ModelFormTrait; 24 | 25 | /** 26 | * @var string name of the view, which should be rendered 27 | */ 28 | public $view = 'update'; 29 | 30 | 31 | /** 32 | * Updates existing record specified by id. 33 | * @param mixed $id id of the model to be deleted. 34 | * @return mixed response. 35 | */ 36 | public function run($id) 37 | { 38 | $model = $this->findModel($id); 39 | $model->scenario = $this->scenario; 40 | 41 | if ($this->load($model, Yii::$app->request->post())) { 42 | if (Yii::$app->request->isAjax) { 43 | Yii::$app->response->format = Response::FORMAT_JSON; 44 | return $this->performAjaxValidation($model); 45 | } 46 | if ($model->save()) { 47 | $this->setFlash($this->flash, ['id' => $id, 'model' => $model]); 48 | return $this->controller->redirect($this->createReturnUrl('view', $model)); 49 | } 50 | } 51 | 52 | return $this->controller->render($this->view, [ 53 | 'model' => $model, 54 | ]); 55 | } 56 | } -------------------------------------------------------------------------------- /src/actions/VariationCreate.php: -------------------------------------------------------------------------------- 1 | 19 | * @since 1.0 20 | */ 21 | class VariationCreate extends Create 22 | { 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function behaviors() 27 | { 28 | return [ 29 | [ 30 | 'class' => VariationBehavior::class 31 | ] 32 | ]; 33 | } 34 | } -------------------------------------------------------------------------------- /src/actions/VariationUpdate.php: -------------------------------------------------------------------------------- 1 | 19 | * @since 1.0 20 | */ 21 | class VariationUpdate extends Update 22 | { 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function behaviors() 27 | { 28 | return [ 29 | [ 30 | 'class' => VariationBehavior::class 31 | ] 32 | ]; 33 | } 34 | } -------------------------------------------------------------------------------- /src/actions/View.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 1.0 15 | */ 16 | class View extends Action 17 | { 18 | /** 19 | * @var string name of the view, which should be rendered 20 | */ 21 | public $view = 'view'; 22 | 23 | 24 | /** 25 | * Displays a model. 26 | * @param string $id the primary key of the model. 27 | * @return mixed response. 28 | */ 29 | public function run($id) 30 | { 31 | $model = $this->findModel($id); 32 | 33 | $this->setReturnAction(); 34 | 35 | return $this->controller->render($this->view, [ 36 | 'model' => $model, 37 | ]); 38 | } 39 | } -------------------------------------------------------------------------------- /src/behaviors/ContextModelControlBehavior.php: -------------------------------------------------------------------------------- 1 | 32 | * @since 1.0 33 | */ 34 | class ContextModelControlBehavior extends ModelControlBehavior 35 | { 36 | /** 37 | * @var array specifies possible contexts. 38 | * The array key is considered as context name, value - as context config. 39 | * Config should contain following keys: 40 | * 41 | * - class: string, class name of context model. 42 | * - attribute: string, name of model attribute, which refers to the context model primary key. 43 | * - url: array|string, URL config or route to the controller, which manage context models. 44 | * - required: boolean, whether this context is mandatory for this controller or optional. 45 | * 46 | * For example: 47 | * 48 | * ```php 49 | * [ 50 | * 'user' => [ 51 | * 'class' => User::class, 52 | * 'attribute' => 'userId', 53 | * 'url' => 'user/view', 54 | * ] 55 | * ] 56 | * ``` 57 | */ 58 | public $contexts; 59 | 60 | /** 61 | * @var ActiveRecordInterface[]|Model[] stores the active context models, which means the ones, which passed 62 | * through the query params. 63 | */ 64 | private $_contextModels; 65 | 66 | 67 | /** 68 | * Return context info by given name. 69 | * If 'null' name provided the first declared context will be returned. 70 | * @param string|null $name context name. 71 | * @return array context config. 72 | */ 73 | public function getContext($name = null) 74 | { 75 | if ($name === null) { 76 | reset($this->contexts); 77 | return current($this->contexts); 78 | } 79 | if (!isset($this->contexts[$name])) { 80 | throw new InvalidArgumentException("Undefined context '{$name}'"); 81 | } 82 | 83 | return $this->contexts[$name]; 84 | } 85 | 86 | /** 87 | * @return ActiveRecordInterface[]|Model[] active context models 88 | */ 89 | public function getContextModels() 90 | { 91 | if (!is_array($this->_contextModels)) { 92 | $this->_contextModels = $this->findContextModels(); 93 | } 94 | return $this->_contextModels; 95 | } 96 | 97 | /** 98 | * @param ActiveRecordInterface[]|Model[] $contextModels active context models 99 | */ 100 | public function setContextModels($contextModels) 101 | { 102 | $this->_contextModels = $contextModels; 103 | } 104 | 105 | /** 106 | * Return active context model by given name. 107 | * If 'null' name provided the first active context model will be returned. 108 | * @param string|null $name context name. 109 | * @return ActiveRecordInterface|Model default active context model. 110 | */ 111 | public function getContextModel($name = null) 112 | { 113 | $contextModels = $this->getContextModels(); 114 | if ($name === null) { 115 | if (empty($contextModels)) { 116 | throw new InvalidArgumentException("There is no context model."); 117 | } 118 | reset($contextModels); 119 | return current($contextModels); 120 | } 121 | if (!isset($contextModels[$name])) { 122 | throw new InvalidArgumentException("Undefined context '{$name}'"); 123 | } 124 | 125 | return $contextModels[$name]; 126 | } 127 | 128 | /** 129 | * Finds all active context models. 130 | * @return ActiveRecordInterface[]|Model[] active contexts. 131 | * @throws InvalidConfigException on invalid configuration. 132 | * @throws NotFoundHttpException on missing required context. 133 | */ 134 | protected function findContextModels() 135 | { 136 | $contextModels = []; 137 | if (is_array($this->contexts)) { 138 | $queryParams = Yii::$app->request->getQueryParams(); 139 | foreach ($this->contexts as $name => $config) { 140 | if (empty($config['attribute'])) { 141 | throw new InvalidConfigException('Context "attribute" parameter must be set.'); 142 | } 143 | $attribute = $config['attribute']; 144 | if (array_key_exists($attribute, $queryParams)) { 145 | $contextModels[$name] = $this->findContextModel($config, $queryParams[$attribute]); 146 | } elseif (isset($config['required']) && $config['required']) { 147 | throw new NotFoundHttpException(Yii::t('yii2tech-admin', "Context '{name}' required.", ['name' => $name])); 148 | } 149 | } 150 | } 151 | return $contextModels; 152 | } 153 | 154 | /** 155 | * Initializes a particular active context. 156 | * @param array $config context configuration. 157 | * @param mixed $id context model primary key value. 158 | * @return ActiveRecordInterface|Model context model instance. 159 | * @throws InvalidConfigException on invalid configuration. 160 | * @throws NotFoundHttpException if context model not found. 161 | */ 162 | protected function findContextModel($config, $id) 163 | { 164 | if (empty($config['class'])) { 165 | throw new InvalidConfigException('Context "class" parameter must be set.'); 166 | } 167 | 168 | /* @var $modelClass ActiveRecordInterface */ 169 | $modelClass = $config['class']; 170 | $keys = $modelClass::primaryKey(); 171 | if (count($keys) > 1) { 172 | $values = explode(',', $id); 173 | if (count($keys) === count($values)) { 174 | $model = $modelClass::findOne(array_combine($keys, $values)); 175 | } 176 | } elseif ($id !== null) { 177 | $model = $modelClass::findOne($id); 178 | } 179 | 180 | if (isset($model)) { 181 | return $model; 182 | } 183 | throw new NotFoundHttpException(Yii::t('yii2tech-admin', "Context object not found: {id}", ['id' => $id])); 184 | } 185 | 186 | /** 187 | * Checks if named context is active (present in the query params). 188 | * If 'null' name provided, checks if at least one context is active. 189 | * @param string|null $name context name. 190 | * @return boolean whether context is active. 191 | */ 192 | public function isContextActive($name = null) 193 | { 194 | $contextModels = $this->getContextModels(); 195 | if ($name === null) { 196 | return (!empty($contextModels)); 197 | } 198 | return isset($contextModels[$name]); 199 | } 200 | 201 | // URL : 202 | 203 | /** 204 | * Returns query params for currently active contexts, like `['groupId' => 12]`. 205 | * This method can be used to compose links. 206 | * @return array query params. 207 | */ 208 | public function getContextQueryParams() 209 | { 210 | $queryParams = []; 211 | foreach ($this->getContextModels() as $name => $model) { 212 | $queryParams[$this->contexts[$name]['attribute']] = $model->getPrimaryKey(); 213 | } 214 | return $queryParams; 215 | } 216 | 217 | /** 218 | * Composes URL, which leads to the context model index page. 219 | * @param string|null $name context name. 220 | * @return array URL config. 221 | */ 222 | public function getContextUrl($name = null) 223 | { 224 | $config = $this->getContext($name); 225 | 226 | if (isset($config['indexUrl'])) { 227 | $url = (array)$config['indexUrl']; 228 | } else { 229 | if (isset($config['controller'])) { 230 | $controllerId = $config['controller']; 231 | } else { 232 | if ($name !== null) { 233 | $controllerId = $name; 234 | } else { 235 | $controllerId = trim(substr($config['attribute'], 0, -2), '_'); 236 | } 237 | } 238 | $url = ["/{$controllerId}/index"]; 239 | } 240 | 241 | return $url; 242 | } 243 | 244 | /** 245 | * Composes URL, which leads to the context model details page. 246 | * @param string|null $name context name. 247 | * @return array URL config. 248 | */ 249 | public function getContextModelUrl($name = null) 250 | { 251 | $model = $this->getContextModel($name); 252 | $config = $this->getContext($name); 253 | 254 | if (isset($config['viewUrl'])) { 255 | $url = (array)$config['viewUrl']; 256 | } else { 257 | if (isset($config['controller'])) { 258 | $controllerId = $config['controller']; 259 | } else { 260 | if ($name !== null) { 261 | $controllerId = $name; 262 | } else { 263 | $controllerId = Inflector::camel2id(StringHelper::basename(get_class($model))); 264 | } 265 | } 266 | $url = ["/{$controllerId}/view"]; 267 | } 268 | 269 | $url['id'] = $model->getPrimaryKey(); 270 | return $url; 271 | } 272 | 273 | // Override : 274 | 275 | /** 276 | * {@inheritdoc} 277 | */ 278 | public function findModel($id) 279 | { 280 | $model = parent::findModel($id); 281 | foreach ($this->getContextModels() as $name => $contextModel) { 282 | $attribute = $this->contexts[$name]['attribute']; 283 | if ($model->$attribute != $contextModel->getPrimaryKey()) { 284 | throw new NotFoundHttpException(Yii::t('yii2tech-admin', "Object not found: {id}", ['id' => $contextModel->getPrimaryKey()])); 285 | } 286 | } 287 | return $model; 288 | } 289 | 290 | /** 291 | * {@inheritdoc} 292 | */ 293 | public function newModel() 294 | { 295 | $model = parent::newModel(); 296 | foreach ($this->getContextModels() as $name => $contextModel) { 297 | $attribute = $this->contexts[$name]['attribute']; 298 | $model->$attribute = $contextModel->getPrimaryKey(); 299 | } 300 | return $model; 301 | } 302 | 303 | /** 304 | * {@inheritdoc} 305 | */ 306 | public function newSearchModel() 307 | { 308 | $model = parent::newSearchModel(); 309 | foreach ($this->getContextModels() as $name => $contextModel) { 310 | $attribute = $this->contexts[$name]['attribute']; 311 | $model->$attribute = $contextModel->getPrimaryKey(); 312 | } 313 | return $model; 314 | } 315 | } -------------------------------------------------------------------------------- /src/behaviors/ModelControlBehavior.php: -------------------------------------------------------------------------------- 1 | 24 | * @since 1.0 25 | */ 26 | class ModelControlBehavior extends Behavior 27 | { 28 | /** 29 | * @var string the model class name. This property must be set. 30 | * The model class must implement [[ActiveRecordInterface]]. 31 | * @see newModel() 32 | * @see findModel() 33 | */ 34 | public $modelClass; 35 | /** 36 | * @var string|callable class name of the model which should be used as search model. 37 | * This can be a PHP callback of following signature: 38 | * 39 | * ```php 40 | * function (\yii\web\Controller $controller) { 41 | * //return new \yii\base\Model; 42 | * } 43 | * ``` 44 | * 45 | * If not set it will be composed using [[modelClass]]. 46 | * If [yii2tech/ar-search](https://github.com/yii2tech/ar-search) extension is installed - 47 | * [[\yii2tech\ar\search\ActiveSearchModel]] instance will be used as a search model. 48 | * 49 | * @see newSearchModel() 50 | */ 51 | public $searchModelClass; 52 | 53 | 54 | /** 55 | * Returns the data model based on the primary key given. 56 | * If the data model is not found, a 404 HTTP exception will be raised. 57 | * @param string $id the ID of the model to be loaded. If the model has a composite primary key, 58 | * the ID must be a string of the primary key values separated by commas. 59 | * The order of the primary key values should follow that returned by the `primaryKey()` method 60 | * of the model. 61 | * @return ActiveRecordInterface|Model the model found 62 | * @throws NotFoundHttpException if the model cannot be found 63 | * @throws InvalidConfigException on invalid configuration 64 | */ 65 | public function findModel($id) 66 | { 67 | if ($this->modelClass === null) { 68 | throw new InvalidConfigException('"' . get_class($this) . '::$modelClass" must be set.'); 69 | } 70 | 71 | /* @var $modelClass ActiveRecordInterface */ 72 | $modelClass = $this->modelClass; 73 | $keys = $modelClass::primaryKey(); 74 | if (count($keys) > 1) { 75 | $values = explode(',', $id); 76 | if (count($keys) === count($values)) { 77 | $model = $modelClass::findOne(array_combine($keys, $values)); 78 | } 79 | } elseif ($id !== null) { 80 | $model = $modelClass::findOne($id); 81 | } 82 | 83 | if (isset($model)) { 84 | return $model; 85 | } 86 | throw new NotFoundHttpException(Yii::t('yii2tech-admin', "Object not found: {id}", ['id' => $id])); 87 | } 88 | 89 | /** 90 | * Creates new model instance. 91 | * @return ActiveRecordInterface|Model new model instance. 92 | * @throws InvalidConfigException on invalid configuration. 93 | */ 94 | public function newModel() 95 | { 96 | if ($this->modelClass === null) { 97 | throw new InvalidConfigException('"' . get_class($this) . '::$modelClass" must be set.'); 98 | } 99 | $modelClass = $this->modelClass; 100 | return new $modelClass(); 101 | } 102 | 103 | /** 104 | * Creates new search model instance. 105 | * @return Model new search model instance. 106 | * @throws InvalidConfigException on invalid configuration. 107 | */ 108 | public function newSearchModel() 109 | { 110 | $modelClass = $this->searchModelClass; 111 | if ($modelClass === null) { 112 | if ($this->modelClass === null) { 113 | throw new InvalidConfigException('Either "' . get_class($this) . '::$searchModelClass" or "' . get_class($this) . '::$modelClass" must be set.'); 114 | } 115 | 116 | if (class_exists('yii2tech\ar\search\ActiveSearchModel')) { 117 | $searchModel = new \yii2tech\ar\search\ActiveSearchModel(); 118 | $searchModel->setModel($this->modelClass); 119 | return $searchModel; 120 | } 121 | 122 | $modelClass = $this->modelClass . 'Search'; 123 | } elseif (!is_string($modelClass)) { 124 | return call_user_func($modelClass, $this->owner); 125 | } 126 | 127 | return new $modelClass(); 128 | } 129 | } -------------------------------------------------------------------------------- /src/behaviors/action/RoleBehavior.php: -------------------------------------------------------------------------------- 1 | 26 | * @since 1.0 27 | */ 28 | class RoleBehavior extends Behavior 29 | { 30 | /** 31 | * @var array list of model role behavior names, which should be affected by the action. 32 | * If empty - all instances of [[RoleBehavior]] will be picked up. 33 | */ 34 | public $roleNames = []; 35 | 36 | /** 37 | * @var array cache for the role models. 38 | */ 39 | private $_roleModels = []; 40 | 41 | 42 | /** 43 | * Get role models for the main one. 44 | * @param Model|ActiveRecordInterface $model main model instance. 45 | * @return Model[] list of role models 46 | */ 47 | public function getRoleModels($model) 48 | { 49 | $key = serialize($model->getPrimaryKey()); 50 | if (!isset($this->_roleModels[$key])) { 51 | $this->_roleModels[$key] = $this->findRoleModels($model); 52 | } 53 | return $this->_roleModels[$key]; 54 | } 55 | 56 | /** 57 | * @param Model|ActiveRecordInterface $model 58 | * @return array list of variation models in format: behaviorName => Model[] 59 | */ 60 | private function findRoleModels($model) 61 | { 62 | $roleModels = []; 63 | foreach ($model->getBehaviors() as $name => $behavior) { 64 | if ((empty($this->roleNames) && ($behavior instanceof \yii2tech\ar\role\RoleBehavior)) || in_array($name, $this->roleNames)) { 65 | $roleModels[$name] = $behavior->getRoleRelationModel(); 66 | } 67 | } 68 | return $roleModels; 69 | } 70 | 71 | // Events : 72 | 73 | /** 74 | * {@inheritdoc} 75 | */ 76 | public function events() 77 | { 78 | return [ 79 | 'afterDataLoad' => 'afterDataLoad', 80 | 'afterAjaxValidate' => 'afterAjaxValidate', 81 | ]; 82 | } 83 | 84 | /** 85 | * Handles `afterDataLoad` event. 86 | * Populates the role models with input data. 87 | * @param ActionEvent $event event instance. 88 | */ 89 | public function afterDataLoad($event) 90 | { 91 | if (!$event->result) { 92 | return; 93 | } 94 | 95 | $model = $event->model; 96 | $data = Yii::$app->request->post(); 97 | 98 | foreach ($this->getRoleModels($model) as $roleModel) { 99 | if (!$roleModel->load($data)) { 100 | return; 101 | } 102 | } 103 | } 104 | 105 | /** 106 | * Performs AJAX validation of the role models via [[ActiveForm::validate()]]. 107 | * @param ActionEvent $event event instance. 108 | */ 109 | public function afterAjaxValidate($event) 110 | { 111 | $model = $event->model; 112 | 113 | $roleModels = $this->getRoleModels($model); 114 | 115 | $event->result = array_merge( 116 | $event->result, 117 | call_user_func_array([ActiveForm::class, 'validate'], $roleModels) 118 | ); 119 | } 120 | } -------------------------------------------------------------------------------- /src/behaviors/action/VariationBehavior.php: -------------------------------------------------------------------------------- 1 | 26 | * @since 1.0 27 | */ 28 | class VariationBehavior extends Behavior 29 | { 30 | /** 31 | * @var array list of model variation behavior names, which should be affected by the action. 32 | * If empty - all instances of [[\yii2tech\ar\variation\VariationBehavior]] will be picked up. 33 | */ 34 | public $variationNames = []; 35 | 36 | /** 37 | * @var array cache for the variation model batches. 38 | */ 39 | private $_variationModelBatches = []; 40 | 41 | 42 | /** 43 | * Get variation models for the main one in batches. 44 | * @param Model|ActiveRecordInterface $model main model instance. 45 | * @return array list of variation models in format: behaviorName => Model[] 46 | */ 47 | protected function getVariationModelBatches($model) 48 | { 49 | $key = serialize($model->getPrimaryKey()); 50 | if (!isset($this->_variationModelBatches[$key])) { 51 | $this->_variationModelBatches[$key] = $this->findVariationModelBatches($model); 52 | } 53 | return $this->_variationModelBatches[$key]; 54 | } 55 | 56 | /** 57 | * @param Model|ActiveRecordInterface $model 58 | * @return array list of variation models in format: behaviorName => Model[] 59 | */ 60 | protected function findVariationModelBatches($model) 61 | { 62 | $variationModels = []; 63 | foreach ($model->getBehaviors() as $name => $behavior) { 64 | if ((empty($this->variationNames) && ($behavior instanceof \yii2tech\ar\variation\VariationBehavior)) || in_array($name, $this->variationNames)) { 65 | $variationModels[$name] = $behavior->getVariationModels(); 66 | } 67 | } 68 | return $variationModels; 69 | } 70 | 71 | // Events : 72 | 73 | /** 74 | * {@inheritdoc} 75 | */ 76 | public function events() 77 | { 78 | return [ 79 | 'afterDataLoad' => 'afterDataLoad', 80 | 'afterAjaxValidate' => 'afterAjaxValidate', 81 | ]; 82 | } 83 | 84 | /** 85 | * Handles `afterDataLoad` event. 86 | * Populates the variation models with input data. 87 | * @param ActionEvent $event event instance. 88 | */ 89 | public function afterDataLoad($event) 90 | { 91 | if (!$event->result) { 92 | return; 93 | } 94 | 95 | $model = $event->model; 96 | $data = Yii::$app->request->post(); 97 | 98 | foreach ($this->getVariationModelBatches($model) as $variationName => $variationModels) { 99 | if (empty($variationModels)) { 100 | continue; 101 | } 102 | if (!Model::loadMultiple($variationModels, $data)) { 103 | $event->result = false; 104 | } 105 | } 106 | } 107 | 108 | /** 109 | * Handles `afterAjaxValidate` event. 110 | * Performs AJAX validation of the variation models via [[ActiveForm::validate()]]. 111 | * @param ActionEvent $event event instance. 112 | */ 113 | protected function afterAjaxValidate($event) 114 | { 115 | $model = $event->model; 116 | $response = $event->result; 117 | 118 | // validate variations manually for tabular input matching : 119 | foreach ($this->getVariationModelBatches($model) as $variationModels) { 120 | foreach ($variationModels as $index => $variationModel) { 121 | /* @var $variationModel Model */ 122 | if (!$variationModel->validate()) { 123 | foreach ($model->getErrors() as $attribute => $errors) { 124 | $response[Html::getInputId($model, '[' . $index . ']' . $attribute)] = $errors; 125 | } 126 | } 127 | } 128 | } 129 | 130 | $event->data['result'] = $response; 131 | } 132 | } -------------------------------------------------------------------------------- /src/gii/crud/Generator.php: -------------------------------------------------------------------------------- 1 | 18 | * @since 1.0 19 | */ 20 | class Generator extends \yii\gii\generators\crud\Generator 21 | { 22 | /** 23 | * @var string controller context model classes. 24 | */ 25 | public $contextClass; 26 | /** 27 | * {@inheritdoc} 28 | */ 29 | public $baseControllerClass = 'yii2tech\admin\CrudController'; 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public $messageCategory = 'admin'; 34 | 35 | 36 | /** 37 | * {@inheritdoc} 38 | */ 39 | public function getName() 40 | { 41 | return 'Admin CRUD Generator'; 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | public function getDescription() 48 | { 49 | return 'This generator generates a controller and views that implement CRUD (Create, Read, Update, Delete) 50 | operations for the specified data model.'; 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | public function rules() 57 | { 58 | return array_merge(parent::rules(), [ 59 | ['contextClass', 'safe'], 60 | ['contextClass', 'match', 'pattern' => '/^[\w\\\\]+(\s*,\s*[\w\\\\]+)*$/', 'message' => 'Only word characters and backslashes are allowed.'], 61 | ]); 62 | } 63 | 64 | /** 65 | * {@inheritdoc} 66 | */ 67 | public function attributeLabels() 68 | { 69 | return array_merge(parent::attributeLabels(), [ 70 | 'contextClass' => 'Context Class', 71 | ]); 72 | } 73 | 74 | /** 75 | * {@inheritdoc} 76 | */ 77 | public function hints() 78 | { 79 | return array_merge(parent::hints(), [ 80 | 'contextClass' => 'This is the ActiveRecord class, which serves as context for the contoller. 81 | You should provide a fully qualified class name, e.g., app\models\User. 82 | You may specify several classes separated by comma.', 83 | ]); 84 | } 85 | 86 | /** 87 | * {@inheritdoc} 88 | */ 89 | public function stickyAttributes() 90 | { 91 | return array_merge(parent::stickyAttributes(), ['contextClass']); 92 | } 93 | 94 | /** 95 | * Returns the root path to the original parent default code template files. 96 | * @return string the root path to the original parent default code template files. 97 | */ 98 | public function parentDefaultTemplate() 99 | { 100 | return Yii::getAlias('@yii/gii/generators/crud/default'); 101 | } 102 | 103 | /** 104 | * {@inheritdoc} 105 | */ 106 | public function getNameAttribute() 107 | { 108 | foreach ($this->getColumnNames() as $name) { 109 | if (!strcasecmp($name, 'username') || !strcasecmp($name, 'email')) { 110 | return $name; 111 | } 112 | } 113 | return parent::getNameAttribute(); 114 | } 115 | 116 | /** 117 | * @return array contexts in format: contextName => contextClassName. 118 | */ 119 | public function getContexts() 120 | { 121 | if (empty($this->contextClass)) { 122 | return []; 123 | } 124 | $result = []; 125 | $classes = preg_split('/,/', $this->contextClass, -1, PREG_SPLIT_NO_EMPTY); 126 | foreach ($classes as $class) { 127 | $result[Inflector::camel2id(StringHelper::basename($class))] = $class; 128 | } 129 | return $result; 130 | } 131 | } -------------------------------------------------------------------------------- /src/gii/crud/default/controller.php: -------------------------------------------------------------------------------- 1 | controllerClass); 12 | $contexts = $generator->getContexts(); 13 | 14 | echo " 16 | 17 | namespace controllerClass, '\\')) ?>; 18 | 19 | use baseControllerClass, '\\') ?>; 20 | 21 | use yii\helpers\ArrayHelper; 22 | use yii2tech\admin\behaviors\ContextModelControlBehavior; 23 | 24 | 25 | /** 26 | * implements the CRUD actions for [[modelClass ?>]] model. 27 | * @see \modelClass . "\n" ?> 28 | */ 29 | class extends baseControllerClass) . "\n" ?> 30 | { 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public $modelClass = \modelClass ?>::class; 35 | searchModelClass)): ?> 36 | /** 37 | * {@inheritdoc} 38 | */ 39 | public $searchModelClass = \searchModelClass ?>::class; 40 | 41 | 42 | /** 43 | * Contexts configuration 44 | * @see ContextModelControlBehavior::$contexts 45 | */ 46 | public $contexts = [ 47 | $class) : ?> 48 | // Specify actual contexts : 49 | '' => [ 50 | 'class' => \::class, 51 | 'attribute' => 'Id', 52 | 'controller' => '', 53 | 'required' => false, 54 | ], 55 | ]; 56 | 57 | 58 | 59 | /** 60 | * {@inheritdoc} 61 | */ 62 | public function behaviors() 63 | { 64 | return ArrayHelper::merge( 65 | parent::behaviors(), 66 | [ 67 | 'model' => [ 68 | 'class' => ContextModelControlBehavior::class, 69 | 'contexts' => $this->contexts 70 | ] 71 | ] 72 | ); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/gii/crud/default/search.php: -------------------------------------------------------------------------------- 1 | parentDefaultTemplate() . DIRECTORY_SEPARATOR . 'search.php'; -------------------------------------------------------------------------------- /src/gii/crud/default/views/_form.php: -------------------------------------------------------------------------------- 1 | modelClass(); 8 | $safeAttributes = $model->safeAttributes(); 9 | if (empty($safeAttributes)) { 10 | $safeAttributes = $model->attributes(); 11 | } 12 | 13 | echo " 15 | /** 16 | * @see \controllerClass . "\n" ?> 17 | controllerClass, 'yii2tech\admin\CrudController')): ?> 18 | * @see \yii2tech\admin\actions\Create 19 | * @see \yii2tech\admin\actions\Update 20 | 21 | */ 22 | 23 | use yii\bootstrap\Html; 24 | use yii\bootstrap\ActiveForm; 25 | 26 | /* @var $this yii\web\View */ 27 | /* @var $model modelClass, '\\') ?> */ 28 | /* @var $form yii\widgets\ActiveForm */ 29 | ?> 30 | 31 |
32 |
33 | 34 | $form = ActiveForm::begin(); ?> 35 | 36 | getColumnNames() as $attribute) { 37 | if (in_array($attribute, $safeAttributes)) { 38 | echo " generateActiveField($attribute) . " ?>\n\n"; 39 | } 40 | } ?> 41 |
42 | Html::submitButton($model->isNewRecord ? generateString('Create') ?> : generateString('Update') ?>, ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?> 43 |
44 | 45 | ActiveForm::end(); ?> 46 | 47 |
48 |
-------------------------------------------------------------------------------- /src/gii/crud/default/views/_search.php: -------------------------------------------------------------------------------- 1 | parentDefaultTemplate() . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . '_search.php'; 7 | -------------------------------------------------------------------------------- /src/gii/crud/default/views/create.php: -------------------------------------------------------------------------------- 1 | getContexts(); 10 | 11 | echo " 13 | /** 14 | * @see \controllerClass . "\n" ?> 15 | controllerClass, 'yii2tech\admin\CrudController')): ?> 16 | * @see \yii2tech\admin\actions\Create 17 | 18 | */ 19 | 20 | /* @var $this yii\web\View */ 21 | /* @var $model modelClass, '\\') ?> */ 22 | 23 | /* @var $controller controllerClass ?>|yii2tech\admin\behaviors\ContextModelControlBehavior */ 24 | 25 | $controller = $this->context; 26 | $contextUrlParams = $controller->getContextQueryParams(); 27 | 28 | 29 | $this->title = generateString('Create ' . Inflector::camel2words(StringHelper::basename($generator->modelClass))) ?>; 30 | 31 | foreach ($controller->getContextModels() as $name => $contextModel) { 32 | $this->params['breadcrumbs'][] = ['label' => $name, 'url' => $controller->getContextUrl($name)]; 33 | $this->params['breadcrumbs'][] = ['label' => $contextModel->id, 'url' => $controller->getContextModelUrl($name)]; 34 | } 35 | $this->params['breadcrumbs'][] = ['label' => generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>, 'url' => array_merge(['index'], $contextUrlParams)]; 36 | 37 | $this->params['breadcrumbs'][] = ['label' => generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>, 'url' => ['index']]; 38 | 39 | $this->params['breadcrumbs'][] = $this->title; 40 | ?> 41 | 42 | $this->render('_form', [ 43 | 'model' => $model, 44 | ]) ?> 45 | -------------------------------------------------------------------------------- /src/gii/crud/default/views/index.php: -------------------------------------------------------------------------------- 1 | generateUrlParams(); 10 | $nameAttribute = $generator->getNameAttribute(); 11 | $contexts = $generator->getContexts(); 12 | 13 | echo " 15 | /** 16 | * @see \controllerClass . "\n" ?> 17 | controllerClass, yii2tech\admin\CrudController::class)): ?> 18 | * @see \yii2tech\admin\actions\Index 19 | 20 | */ 21 | 22 | indexWidgetType === 'grid'): ?> 23 | use yii\grid\GridView; 24 | use yii2tech\admin\grid\ActionColumn; 25 | 26 | use yii\widgets\ListView; 27 | 28 | 29 | /* @var $this yii\web\View */ 30 | /* @var $searchModel searchModelClass) ? ltrim($generator->searchModelClass, '\\') : 'yii\base\Model' ?> */ 31 | /* @var $dataProvider yii\data\ActiveDataProvider */ 32 | 33 | /* @var $controller controllerClass ?>|yii2tech\admin\behaviors\ContextModelControlBehavior */ 34 | 35 | $controller = $this->context; 36 | $contextUrlParams = $controller->getContextQueryParams(); 37 | 38 | 39 | $this->title = generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>; 40 | 41 | foreach ($controller->getContextModels() as $name => $contextModel) { 42 | $this->params['breadcrumbs'][] = ['label' => $name, 'url' => $controller->getContextUrl($name)]; 43 | $this->params['breadcrumbs'][] = ['label' => $contextModel->id, 'url' => $controller->getContextModelUrl($name)]; 44 | } 45 | 46 | $this->params['breadcrumbs'][] = $this->title; 47 | 48 | $this->params['contextMenuItems'] = [ 49 | array_merge(['create'], $contextUrlParams) 50 | ]; 51 | 52 | $this->params['contextMenuItems'] = [ 53 | ['create'] 54 | ]; 55 | 56 | ?> 57 | searchModelClass) && $generator->indexWidgetType !== 'grid'): ?> 58 | echo $this->render('_search', ['model' => $searchModel]); ?> 59 | 60 | 61 | indexWidgetType === 'grid'): ?> 62 | GridView::widget([ 63 | 'dataProvider' => $dataProvider, 64 | searchModelClass) ? "'filterModel' => \$searchModel,\n 'columns' => [\n" : "'columns' => [\n"; ?> 65 | ['class' => \yii\grid\SerialColumn::class], 66 | 67 | getTableSchema()) === false) { 70 | foreach ($generator->getColumnNames() as $name) { 71 | if (++$count < 6) { 72 | echo " '" . $name . "',\n"; 73 | } else { 74 | echo " // '" . $name . "',\n"; 75 | } 76 | } 77 | } else { 78 | foreach ($tableSchema->columns as $column) { 79 | $format = $generator->generateColumnFormat($column); 80 | if (++$count < 6) { 81 | echo " '" . $column->name . ($format === 'text' ? "" : ":" . $format) . "',\n"; 82 | } else { 83 | echo " // '" . $column->name . ($format === 'text' ? "" : ":" . $format) . "',\n"; 84 | } 85 | } 86 | } 87 | ?> 88 | 89 | [ 90 | 'class' => ActionColumn::class, 91 | ], 92 | ], 93 | ]); ?> 94 | 95 | ListView::widget([ 96 | 'dataProvider' => $dataProvider, 97 | 'itemOptions' => ['class' => 'item'], 98 | 'itemView' => function ($model, $key, $index, $widget) { 99 | return Html::a(Html::encode($model->), array_merge(['view', ], $contextUrlParams)); 100 | }, 101 | ]) ?> 102 | 103 | -------------------------------------------------------------------------------- /src/gii/crud/default/views/update.php: -------------------------------------------------------------------------------- 1 | generateUrlParams(); 10 | $contexts = $generator->getContexts(); 11 | 12 | echo " 14 | /** 15 | * @see \controllerClass . "\n" ?> 16 | controllerClass, 'yii2tech\admin\CrudController')): ?> 17 | * @see \yii2tech\admin\actions\Update 18 | 19 | */ 20 | 21 | /* @var $this yii\web\View */ 22 | /* @var $model modelClass, '\\') ?> */ 23 | 24 | /* @var $controller controllerClass ?>|yii2tech\admin\behaviors\ContextModelControlBehavior */ 25 | 26 | $controller = $this->context; 27 | $contextUrlParams = $controller->getContextQueryParams(); 28 | 29 | 30 | $this->title = generateString('Update ' . Inflector::camel2words(StringHelper::basename($generator->modelClass)) . ': ') ?> . $model->getNameAttribute() ?>; 31 | 32 | foreach ($controller->getContextModels() as $name => $contextModel) { 33 | $this->params['breadcrumbs'][] = ['label' => $name, 'url' => $controller->getContextUrl($name)]; 34 | $this->params['breadcrumbs'][] = ['label' => $contextModel->id, 'url' => $controller->getContextModelUrl($name)]; 35 | } 36 | $this->params['breadcrumbs'][] = ['label' => generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>, 'url' => array_merge(['index'], $contextUrlParams)]; 37 | $this->params['breadcrumbs'][] = ['label' => $model->getNameAttribute() ?>, 'url' => array_merge(['view', ], $contextUrlParams)]; 38 | 39 | $this->params['breadcrumbs'][] = ['label' => generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>, 'url' => ['index']]; 40 | $this->params['breadcrumbs'][] = ['label' => $model->getNameAttribute() ?>, 'url' => ['view', ]]; 41 | 42 | $this->params['breadcrumbs'][] = generateString('Update') ?>; 43 | ?> 44 | 45 | $this->render('_form', [ 46 | 'model' => $model, 47 | ]) ?> 48 | -------------------------------------------------------------------------------- /src/gii/crud/default/views/view.php: -------------------------------------------------------------------------------- 1 | generateUrlParams(); 10 | $contexts = $generator->getContexts(); 11 | 12 | echo " 14 | /** 15 | * @see \controllerClass . "\n" ?> 16 | controllerClass, 'yii2tech\admin\CrudController')): ?> 17 | * @see \yii2tech\admin\actions\View 18 | 19 | */ 20 | 21 | use yii\bootstrap\Html; 22 | use yii\widgets\DetailView; 23 | 24 | /* @var $this yii\web\View */ 25 | /* @var $model modelClass, '\\') ?> */ 26 | 27 | /* @var $controller controllerClass ?>|yii2tech\admin\behaviors\ContextModelControlBehavior */ 28 | 29 | $controller = $this->context; 30 | $contextUrlParams = $controller->getContextQueryParams(); 31 | 32 | 33 | $this->title = $model->getNameAttribute() ?>; 34 | 35 | foreach ($controller->getContextModels() as $name => $contextModel) { 36 | $this->params['breadcrumbs'][] = ['label' => $name, 'url' => $controller->getContextUrl($name)]; 37 | $this->params['breadcrumbs'][] = ['label' => $contextModel->id, 'url' => $controller->getContextModelUrl($name)]; 38 | } 39 | $this->params['breadcrumbs'][] = ['label' => generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>, 'url' => array_merge(['index'], $contextUrlParams)]; 40 | 41 | $this->params['breadcrumbs'][] = ['label' => generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>, 'url' => ['index']]; 42 | 43 | $this->params['breadcrumbs'][] = $this->title; 44 | 45 | $this->params['contextMenuItems'] = [ 46 | array_merge(['update', ], $contextUrlParams), 47 | array_merge(['delete', ], $contextUrlParams) 48 | ]; 49 | 50 | $this->params['contextMenuItems'] = [ 51 | ['update', ], 52 | ['delete', ] 53 | ]; 54 | 55 | ?> 56 |
57 |
58 | DetailView::widget([ 59 | 'model' => $model, 60 | 'attributes' => [ 61 | getTableSchema()) === false) { 63 | foreach ($generator->getColumnNames() as $name) { 64 | echo " '" . $name . "',\n"; 65 | } 66 | } else { 67 | foreach ($generator->getTableSchema()->columns as $column) { 68 | $format = $generator->generateColumnFormat($column); 69 | echo " '" . $column->name . ($format === 'text' ? "" : ":" . $format) . "',\n"; 70 | } 71 | } 72 | ?> 73 | ], 74 | ]) ?> 75 |
76 |
-------------------------------------------------------------------------------- /src/gii/crud/form.php: -------------------------------------------------------------------------------- 1 | field($generator, 'modelClass'); 7 | echo $form->field($generator, 'searchModelClass'); 8 | echo $form->field($generator, 'controllerClass'); 9 | echo $form->field($generator, 'viewPath'); 10 | echo $form->field($generator, 'contextClass'); 11 | echo $form->field($generator, 'baseControllerClass'); 12 | echo $form->field($generator, 'indexWidgetType')->dropDownList([ 13 | 'grid' => 'GridView', 14 | 'list' => 'ListView', 15 | ]); 16 | echo $form->field($generator, 'enableI18N')->checkbox(); 17 | echo $form->field($generator, 'messageCategory'); 18 | -------------------------------------------------------------------------------- /src/gii/mainframe/Generator.php: -------------------------------------------------------------------------------- 1 | 20 | * @since 1.0 21 | */ 22 | class Generator extends \yii\gii\Generator 23 | { 24 | /** 25 | * @var string main controller class name 26 | */ 27 | public $controllerClass = 'app\controllers\admin\SiteController'; 28 | /** 29 | * @var string base controller class 30 | */ 31 | public $baseControllerClass = 'yii\web\Controller'; 32 | /** 33 | * @var string view path 34 | */ 35 | public $viewPath = '@app/views/admin'; 36 | /** 37 | * @var string login form model class 38 | */ 39 | public $loginModelClass = 'app\models\admin\LoginForm'; 40 | /** 41 | * {@inheritdoc} 42 | */ 43 | public $messageCategory = 'admin'; 44 | 45 | 46 | /** 47 | * {@inheritdoc} 48 | */ 49 | public function getName() 50 | { 51 | return 'Admin MainFrame Generator'; 52 | } 53 | 54 | /** 55 | * {@inheritdoc} 56 | */ 57 | public function getDescription() 58 | { 59 | return 'This generator generates layouts and common views for administration area. Created layouts supports correct 60 | rendering of the admin CRUD views'; 61 | } 62 | 63 | /** 64 | * {@inheritdoc} 65 | */ 66 | public function generate() 67 | { 68 | $controllerFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->controllerClass, '\\')) . '.php'); 69 | 70 | // Controller : 71 | $files = [ 72 | new CodeFile($controllerFile, $this->render('controller.php')), 73 | ]; 74 | 75 | // Layouts : 76 | $viewPath = $this->getViewPath() . '/layouts'; 77 | $templatePath = $this->getTemplatePath() . '/layouts'; 78 | foreach (scandir($templatePath) as $file) { 79 | if (is_file($templatePath . '/' . $file) && pathinfo($file, PATHINFO_EXTENSION) === 'php') { 80 | $files[] = new CodeFile("$viewPath/$file", $this->render("layouts/$file")); 81 | } 82 | } 83 | 84 | // Controller views : 85 | $viewPath = $this->getViewPath() . '/' . $this->getControllerID(); 86 | $templatePath = $this->getTemplatePath() . '/views'; 87 | foreach (scandir($templatePath) as $file) { 88 | if (is_file($templatePath . '/' . $file) && pathinfo($file, PATHINFO_EXTENSION) === 'php') { 89 | $files[] = new CodeFile("$viewPath/$file", $this->render("views/$file")); 90 | } 91 | } 92 | 93 | return $files; 94 | } 95 | 96 | /** 97 | * {@inheritdoc} 98 | */ 99 | public function rules() 100 | { 101 | return array_merge(parent::rules(), [ 102 | [['loginModelClass', 'controllerClass', 'viewPath'], 'filter', 'filter' => 'trim'], 103 | [['loginModelClass', 'controllerClass', 'viewPath'], 'required'], 104 | [['loginModelClass'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'], 105 | [['loginModelClass'], 'validateClass', 'params' => ['extends' => Model::class]], 106 | [['baseControllerClass'], 'validateClass', 'params' => ['extends' => Controller::class]], 107 | [['controllerClass'], 'match', 'pattern' => '/Controller$/', 'message' => 'Controller class name must be suffixed with "Controller".'], 108 | [['controllerClass'], 'match', 'pattern' => '/(^|\\\\)[A-Z][^\\\\]+Controller$/', 'message' => 'Controller class name must start with an uppercase letter.'], 109 | [['controllerClass'], 'validateNewClass'], 110 | [['viewPath'], 'match', 'pattern' => '/^@?\w+[\\-\\/\w]*$/', 'message' => 'Only word characters, dashes, slashes and @ are allowed.'], 111 | [['viewPath'], 'validatePath'], 112 | [['enableI18N'], 'boolean'], 113 | [['messageCategory'], 'validateMessageCategory', 'skipOnEmpty' => false], 114 | ]); 115 | } 116 | 117 | /** 118 | * {@inheritdoc} 119 | */ 120 | public function attributeLabels() 121 | { 122 | return array_merge(parent::attributeLabels(), [ 123 | 'loginModelClass' => 'Login Model Class', 124 | 'controllerClass' => 'Controller Class', 125 | 'viewPath' => 'View Path', 126 | 'baseControllerClass' => 'Base Controller Class', 127 | ]); 128 | } 129 | 130 | /** 131 | * {@inheritdoc} 132 | */ 133 | public function requiredTemplates() 134 | { 135 | return ['controller.php']; 136 | } 137 | 138 | /** 139 | * {@inheritdoc} 140 | */ 141 | public function hints() 142 | { 143 | return array_merge(parent::hints(), [ 144 | 'loginModelClass' => 'This is the model class for admin panel login form. You should provide a fully qualified class name, e.g., app\models\admin\LoginForm.', 145 | 'controllerClass' => 'This is the name of the controller class to be generated. You should 146 | provide a fully qualified namespaced class (e.g. app\controllers\admin\SiteController), 147 | and class name should be in CamelCase with an uppercase first letter. Make sure the class 148 | is using the same namespace as specified by your application\'s controllerNamespace property.', 149 | 'viewPath' => 'This is the root view path to keep the generated view files. You may provide either a directory or a path alias, e.g., @app/views/admin.', 150 | 'baseControllerClass' => 'This is the class that the new CRUD controller class will extend from. 151 | You should provide a fully qualified class name, e.g., yii\web\Controller.', 152 | ]); 153 | } 154 | 155 | /** 156 | * {@inheritdoc} 157 | */ 158 | public function stickyAttributes() 159 | { 160 | return array_merge(parent::stickyAttributes(), ['baseControllerClass']); 161 | } 162 | 163 | /** 164 | * Validates file path to make sure it is a valid path or path alias and exists. 165 | * @param string $attribute the attribute currently being validated. 166 | * @param array $params the additional name-value pairs given in the rule. 167 | */ 168 | public function validatePath($attribute, $params) 169 | { 170 | $path = Yii::getAlias($this->{$attribute}, false); 171 | if ($path === false || !is_dir($path)) { 172 | $this->addError($attribute, 'Path does not exist.'); 173 | } 174 | } 175 | 176 | /** 177 | * @return string the controller ID (without the module ID prefix) 178 | */ 179 | public function getControllerID() 180 | { 181 | $pos = strrpos($this->controllerClass, '\\'); 182 | $class = substr(substr($this->controllerClass, $pos + 1), 0, -10); 183 | 184 | return Inflector::camel2id($class); 185 | } 186 | 187 | /** 188 | * @return string the controller view path 189 | */ 190 | public function getViewPath() 191 | { 192 | return Yii::getAlias($this->viewPath); 193 | } 194 | } -------------------------------------------------------------------------------- /src/gii/mainframe/default/controller.php: -------------------------------------------------------------------------------- 1 | controllerClass); 12 | $loginModelClass = StringHelper::basename($generator->loginModelClass); 13 | 14 | echo " 16 | 17 | namespace controllerClass, '\\')) ?>; 18 | 19 | use Yii; 20 | use yii\filters\AccessControl; 21 | use yii\filters\VerbFilter; 22 | use baseControllerClass, '\\') ?>; 23 | use loginModelClass, '\\') ?>; 24 | 25 | /** 26 | * implements the common actions for admin panel. 27 | */ 28 | class extends baseControllerClass) . "\n" ?> 29 | { 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public function behaviors() 34 | { 35 | return [ 36 | 'access' => [ 37 | 'class' => AccessControl::class, 38 | 'rules' => [ 39 | [ 40 | 'actions' => ['login', 'error'], 41 | 'allow' => true, 42 | ], 43 | [ 44 | 'actions' => ['logout', 'index'], 45 | 'allow' => true, 46 | 'roles' => ['@'], 47 | ], 48 | ], 49 | ], 50 | 'verbs' => [ 51 | 'class' => VerbFilter::class, 52 | 'actions' => [ 53 | 'logout' => ['post'], 54 | ], 55 | ], 56 | ]; 57 | } 58 | 59 | /** 60 | * {@inheritdoc} 61 | */ 62 | public function actions() 63 | { 64 | return [ 65 | 'error' => [ 66 | 'class' => \yii\web\ErrorAction::class, 67 | ], 68 | ]; 69 | } 70 | 71 | /** 72 | * Renders dashborad. 73 | * @return mixed response. 74 | */ 75 | public function actionIndex() 76 | { 77 | return $this->render('index'); 78 | } 79 | 80 | /** 81 | * Renders login form. 82 | * @return mixed response. 83 | */ 84 | public function actionLogin() 85 | { 86 | if (!Yii::$app->user->isGuest) { 87 | return $this->goHome(); 88 | } 89 | 90 | $model = new (); 91 | if ($model->load(Yii::$app->request->post()) && $model->login()) { 92 | return $this->goBack(); 93 | } 94 | 95 | return $this->render('login', [ 96 | 'model' => $model, 97 | ]); 98 | } 99 | 100 | /** 101 | * Logs out user. 102 | * @return mixed response. 103 | */ 104 | public function actionLogout() 105 | { 106 | Yii::$app->user->logout(); 107 | 108 | return $this->goHome(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/gii/mainframe/default/layouts/main.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | use yii\bootstrap\Html; 13 | use yii2tech\admin\widgets\ButtonContextMenu; 14 | 15 | /* @var $this \yii\web\View */ 16 | /* @var $content string */ 17 | " ?> 18 | $this->beginContent($this->findViewFile('/layouts/overall')); ?> 19 |
20 |

Html::encode(isset($this->params['header']) ? $this->params['header'] : $this->title) ?>

21 | 22 |

23 | ButtonContextMenu::widget([ 24 | 'items' => isset($this->params['contextMenuItems']) ? $this->params['contextMenuItems'] : [] 25 | ]) ?> 26 |

27 | 28 | $content ?> 29 |
30 | $this->endContent(); ?> -------------------------------------------------------------------------------- /src/gii/mainframe/default/layouts/mainMenu.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | use yii2tech\admin\widgets\Nav; 13 | use yii\bootstrap\NavBar; 14 | 15 | /* @var $this \yii\web\View */ 16 | 17 | $webUser = Yii::$app->user; 18 | 19 | NavBar::begin([ 20 | 'id' => 'header-nav-bar', 21 | 'brandLabel' => Yii::$app->name, 22 | 'brandUrl' => Yii::$app->homeUrl, 23 | 'options' => [ 24 | 'class' => 'navbar-inverse navbar-fixed-top', 25 | ], 26 | 'innerContainerOptions' => [ 27 | 'class' => 'container-fluid' 28 | ], 29 | ]); 30 | 31 | if (!$webUser->isGuest) { 32 | echo Nav::widget([ 33 | 'id' => 'header-main-menu', 34 | 'options' => ['class' => 'navbar-nav'], 35 | 'items' => [ 36 | [ 37 | 'label' => generateString('Users') ?>, 38 | 'icon' => 'user', 39 | 'items' => [ 40 | [ 41 | 'label' => generateString('Administrators') ?>, 42 | 'icon' => 'user', 43 | 'url' => ['admin/index'], 44 | ], 45 | [ 46 | 'label' => generateString('Users') ?>, 47 | 'icon' => 'user', 48 | 'url' => ['user/index'], 49 | ], 50 | ], 51 | ], 52 | ], 53 | ]); 54 | } 55 | 56 | $menuItems = [ 57 | ['label' => Yii::t('yii', 'Home'), 'url' => Yii::$app->request->getBaseUrl() . '/', 'icon' => 'home'], 58 | ]; 59 | if ($webUser->isGuest) { 60 | $menuItems[] = ['label' => generateString('Login') ?>, 'url' => ['/site/login'], 'icon' => 'log-in']; 61 | } else { 62 | $menuItems[] = [ 63 | 'label' => $webUser->identity->username, 64 | 'items' => [ 65 | [ 66 | 'label' => generateString('Profile') ?>, 67 | 'url' => ['/admin/update', 'id' => $webUser->id], 68 | 'icon' => 'pencil' 69 | ], 70 | [ 71 | 'label' => generateString('Logout') ?>, 72 | 'url' => ['/site/logout'], 73 | 'linkOptions' => ['data-method' => 'post'], 74 | 'icon' => 'log-out', 75 | ], 76 | ], 77 | 78 | ]; 79 | } 80 | echo Nav::widget([ 81 | 'id' => 'header-common-menu', 82 | 'options' => ['class' => 'navbar-nav navbar-right'], 83 | 'items' => $menuItems, 84 | ]); 85 | NavBar::end(); -------------------------------------------------------------------------------- /src/gii/mainframe/default/layouts/overall.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | use app\assets\admin\AppAsset; 13 | use yii\helpers\Html; 14 | use yii\widgets\Breadcrumbs; 15 | use yii2tech\admin\widgets\Alert; 16 | 17 | /* @var $this \yii\web\View */ 18 | /* @var $content string */ 19 | 20 | AppAsset::register($this); 21 | ?> 22 | $this->beginPage() ?> 23 | 24 | 25 | 26 | 27 | 28 | Html::csrfMetaTags() ?> 29 | <?= "<?= " ?> Html::encode($this->title) ?> 30 | $this->head() ?> 31 | 32 | 33 | $this->beginBody() ?> 34 | 35 |
36 | $this->render('/layouts/mainMenu'); ?> 37 | 38 |
39 | if (!Yii::$app->user->isGuest) : ?> 40 | Breadcrumbs::widget([ 41 | 'homeLink' => [ 42 | 'label' => Yii::t('admin', 'Administration'), 43 | 'url' => Yii::$app->homeUrl, 44 | ], 45 | 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], 46 | ]) ?> 47 | Alert::widget() ?> 48 | endif; ?> 49 | $content ?> 50 |
51 |
52 | 53 | 58 | 59 | $this->endBody() ?> 60 | 61 | 62 | $this->endPage() ?> 63 | -------------------------------------------------------------------------------- /src/gii/mainframe/default/views/error.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | /* @var $this yii\web\View */ 13 | /* @var $name string */ 14 | /* @var $message string */ 15 | /* @var $exception Exception */ 16 | 17 | use yii\helpers\Html; 18 | 19 | $this->title = $name; 20 | ?> 21 | 22 |
23 | nl2br(Html::encode($message)) ?> 24 |
25 | 26 |

27 | generateString('The above error occurred while the Web server was processing your request.') ?> 28 |

-------------------------------------------------------------------------------- /src/gii/mainframe/default/views/index.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | use yii\bootstrap\Html; 13 | 14 | /* @var $this yii\web\View */ 15 | 16 | $this->title = generateString('{appName} Administration', ['appName' => Yii::$app->name]) ?>; 17 | 18 | $blocks = [ 19 | [ 20 | 'title' => 'Administrator Accounts', 21 | 'description' => 'Setup administrator accounts', 22 | 'label' => 'Administrators', 23 | 'icon' => 'user', 24 | 'url' => ['/administrator/index'], 25 | ], 26 | [ 27 | 'title' => 'Users Accounts', 28 | 'description' => 'Manage users accounts', 29 | 'label' => 'Users', 30 | 'icon' => 'user', 31 | 'url' => ['/user/index'], 32 | ], 33 | ]; 34 | ?> 35 |
36 |
37 | 38 | foreach ($blocks as $number => $block): ?> 39 | if ($number % 3 == 0): ?> 40 | if ($number != 0): ?> 41 |
42 | endif; ?> 43 |
44 | endif; ?> 45 |
46 |

$block['title'] ?>

47 |

$block['description'] ?>

48 |

Html::a(Html::icon($block['icon']) . ' ' . $block['label'], $block['url'], ['class' => 'btn btn-default']) ?>

49 |
50 | endforeach; ?> 51 | 52 |
53 |
54 | -------------------------------------------------------------------------------- /src/gii/mainframe/default/views/login.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | /* @var $this yii\web\View */ 13 | /* @var $form yii\bootstrap\ActiveForm */ 14 | /* @var $model loginModelClass ?> */ 15 | 16 | use yii\bootstrap\Html; 17 | use yii\bootstrap\ActiveForm; 18 | 19 | $this->title = generateString('Login') ?>; 20 | $this->params['breadcrumbs'][] = $this->title; 21 | ?> 22 |
23 |
24 | $form = ActiveForm::begin(['id' => 'login-form']); ?> 25 | 26 | $form->field($model, 'username') ?> 27 | 28 | $form->field($model, 'password')->passwordInput() ?> 29 | 30 | if (Yii::$app->user->enableAutoLogin) : ?> 31 | $form->field($model, 'rememberMe')->checkbox() ?> 32 | endif; ?> 33 | 34 |
35 | Html::submitButton(generateString('Login') ?>, ['class' => 'btn btn-primary', 'name' => 'login-button']) ?> 36 |
37 | 38 | ActiveForm::end(); ?> 39 |
40 |
41 | -------------------------------------------------------------------------------- /src/gii/mainframe/form.php: -------------------------------------------------------------------------------- 1 | field($generator, 'loginModelClass'); 7 | echo $form->field($generator, 'controllerClass'); 8 | echo $form->field($generator, 'viewPath'); 9 | echo $form->field($generator, 'baseControllerClass'); 10 | echo $form->field($generator, 'enableI18N')->checkbox(); 11 | echo $form->field($generator, 'messageCategory'); 12 | -------------------------------------------------------------------------------- /src/grid/ActionColumn.php: -------------------------------------------------------------------------------- 1 | 30 | * @since 1.0 31 | */ 32 | class ActionColumn extends \yii\grid\ActionColumn 33 | { 34 | /** 35 | * {@inheritdoc} 36 | */ 37 | public $template = '{view} {update} {delete}{restore}'; 38 | 39 | 40 | /** 41 | * Merges buttons with default configurations. 42 | */ 43 | protected function initDefaultButtons() 44 | { 45 | $this->buttons = ArrayHelper::merge( 46 | [ 47 | 'view' => [ 48 | 'icon' => 'eye-open', 49 | 'options' => [ 50 | 'title' => Yii::t('yii', 'View'), 51 | 'aria-label' => Yii::t('yii', 'View'), 52 | 'data-pjax' => '0', 53 | ], 54 | ], 55 | 'update' => [ 56 | 'icon' => 'pencil', 57 | 'options' => [ 58 | 'title' => Yii::t('yii', 'Update'), 59 | 'aria-label' => Yii::t('yii', 'Update'), 60 | 'data-pjax' => '0', 61 | ], 62 | ], 63 | 'delete' => [ 64 | 'icon' => 'trash', 65 | 'visible' => function ($model) { 66 | /* @var $model \yii\db\BaseActiveRecord */ 67 | if (is_object($model) && $model->canGetProperty('isDeleted')) { 68 | return !$model->isDeleted; 69 | } 70 | return true; 71 | }, 72 | 'options' => [ 73 | 'title' => Yii::t('yii', 'Delete'), 74 | 'aria-label' => Yii::t('yii', 'Delete'), 75 | 'data-confirm' => Yii::t('yii', 'Are you sure you want to delete this item?'), 76 | 'data-method' => 'post', 77 | 'data-pjax' => '0', 78 | ], 79 | ], 80 | 'restore' => [ 81 | 'icon' => 'repeat', 82 | 'visible' => function ($model) { 83 | /* @var $model \yii\db\BaseActiveRecord */ 84 | if (is_object($model) && $model->canGetProperty('isDeleted')) { 85 | return $model->isDeleted; 86 | } 87 | return false; 88 | }, 89 | 'options' => [ 90 | 'title' => Yii::t('yii2tech-admin', 'Restore'), 91 | 'aria-label' => Yii::t('yii2tech-admin', 'Restore'), 92 | 'data-confirm' => Yii::t('yii2tech-admin', 'Are you sure you want to restore this item?'), 93 | 'data-method' => 'post', 94 | 'data-pjax' => '0', 95 | ], 96 | ], 97 | ], 98 | $this->buttons 99 | ); 100 | } 101 | 102 | /** 103 | * {@inheritdoc} 104 | */ 105 | protected function renderDataCellContent($model, $key, $index) 106 | { 107 | return preg_replace_callback('/\\{([\w\-\/]+)\\}/', function ($matches) use ($model, $key, $index) { 108 | $name = $matches[1]; 109 | return $this->renderButton($name, $model, $key, $index); 110 | }, $this->template); 111 | } 112 | 113 | /** 114 | * Renders button. 115 | * @param string $name button name. 116 | * @param $model 117 | * @param $key 118 | * @param $index 119 | * @return string rendered HTML 120 | * @throws InvalidConfigException on invalid button format. 121 | */ 122 | protected function renderButton($name, $model, $key, $index) 123 | { 124 | if (!isset($this->buttons[$name])) { 125 | return ''; 126 | } 127 | $button = $this->buttons[$name]; 128 | 129 | if ($button instanceof \Closure) { 130 | $url = $this->createUrl($name, $model, $key, $index); 131 | return call_user_func($button, $url, $model, $key); 132 | } 133 | if (!is_array($button)) { 134 | throw new InvalidConfigException("Button should be either a Closure or array configuration."); 135 | } 136 | 137 | // Visibility : 138 | if (isset($button['visible'])) { 139 | if ($button['visible'] instanceof \Closure) { 140 | if (!call_user_func($button['visible'], $model, $key, $index)) { 141 | return ''; 142 | } 143 | } elseif (!$button['visible']) { 144 | return ''; 145 | } 146 | } 147 | 148 | // URL : 149 | if (isset($button['url'])) { 150 | $url = call_user_func($button['url'], $name, $model, $key, $index); 151 | } else { 152 | $url = $this->createUrl($name, $model, $key, $index); 153 | } 154 | 155 | // label : 156 | if (isset($button['label'])) { 157 | $label = $button['label']; 158 | 159 | if (isset($button['encode'])) { 160 | $encodeLabel = $button['encode']; 161 | unset($button['encode']); 162 | } else { 163 | $encodeLabel = true; 164 | } 165 | if ($encodeLabel) { 166 | $label = Html::encode($label); 167 | } 168 | } else { 169 | $label = ''; 170 | } 171 | 172 | // icon : 173 | if (isset($button['icon'])) { 174 | $icon = $button['icon']; 175 | $label = Html::icon($icon) . (empty($label) ? '' : ' ' . $label); 176 | } 177 | 178 | $options = array_merge(ArrayHelper::getValue($button, 'options', []), $this->buttonOptions); 179 | 180 | return Html::a($label, $url, $options); 181 | } 182 | 183 | /** 184 | * {@inheritdoc} 185 | */ 186 | public function createUrl($action, $model, $key, $index) 187 | { 188 | if (is_callable($this->urlCreator)) { 189 | return call_user_func($this->urlCreator, $action, $model, $key, $index); 190 | } 191 | 192 | $params = is_array($key) ? $key : ['id' => (string) $key]; 193 | $params[0] = $this->controller ? $this->controller . '/' . $action : $action; 194 | $params = $params + Yii::$app->request->getQueryParams(); // preserve numeric keys 195 | return Url::toRoute($params); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/grid/DeleteStatusColumn.php: -------------------------------------------------------------------------------- 1 | statusId)) { 35 | * // filter is not set - display only actual records : 36 | * $query->andWhere(['not', ['statusId' => Item::STATUS_DELETED]]); 37 | * } elseif($this->statusId > 0) { 38 | * // 'show all' is not selected - apply filter : 39 | * $query->andFilterWhere(['statusId' => $this->statusId]); 40 | * } 41 | * 42 | * // ... 43 | * } 44 | * } 45 | * ``` 46 | * 47 | * @author Paul Klimov 48 | * @since 1.0 49 | */ 50 | class DeleteStatusColumn extends DataColumn 51 | { 52 | /** 53 | * {@inheritdoc} 54 | */ 55 | public $format = null; 56 | /** 57 | * @var mixed value, which indicates 'show all records' entry in the filter drop down. 58 | */ 59 | public $filterAllValue = '-1'; 60 | 61 | 62 | /** 63 | * {@inheritdoc} 64 | */ 65 | public function init() 66 | { 67 | parent::init(); 68 | if ($this->format === null) { 69 | if (stripos($this->attribute, 'deleted') !== false || stripos($this->attribute, 'active') !== false) { 70 | $this->format = 'boolean'; 71 | } else { 72 | $this->format = 'text'; 73 | } 74 | } 75 | } 76 | 77 | /** 78 | * {@inheritdoc} 79 | */ 80 | protected function renderFilterCellContent() 81 | { 82 | if (is_string($this->filter)) { 83 | return $this->filter; 84 | } 85 | 86 | $model = $this->grid->filterModel; 87 | 88 | if ($this->filter !== false && $model instanceof Model && $this->attribute !== null && $model->isAttributeActive($this->attribute)) { 89 | if ($model->hasErrors($this->attribute)) { 90 | Html::addCssClass($this->filterOptions, 'has-error'); 91 | $error = ' ' . Html::error($model, $this->attribute, $this->grid->filterErrorOptions); 92 | } else { 93 | $error = ''; 94 | } 95 | if (is_array($this->filter)) { 96 | $filterItems = $this->filter; 97 | $filterItems[$this->filterAllValue] = Yii::t('yii2tech-admin', 'All records'); 98 | } else { 99 | $filterItems = [ 100 | '0' => Yii::t('yii2tech-admin', 'Deleted'), 101 | $this->filterAllValue => Yii::t('yii2tech-admin', 'All records') 102 | ]; 103 | } 104 | $options = array_merge(['prompt' => Yii::t('yii2tech-admin', 'Actual only')], $this->filterInputOptions); 105 | return Html::activeDropDownList($model, $this->attribute, $filterItems, $options) . $error; 106 | } 107 | 108 | return parent::renderFilterCellContent(); 109 | } 110 | } -------------------------------------------------------------------------------- /src/grid/PositionColumn.php: -------------------------------------------------------------------------------- 1 | 23 | * @since 1.0 24 | */ 25 | class PositionColumn extends DataColumn 26 | { 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | public $headerOptions = ['class' => 'position-column']; 31 | /** 32 | * @var string the template that is used to render the content in each cell. 33 | * These default tokens are recognized: {first}, {prev}, {next}, {last} and {value}. 34 | */ 35 | public $template = '{first} {prev} {value} {next} {last}'; 36 | /** 37 | * @var array configuration for the switch position buttons. 38 | */ 39 | public $buttons = []; 40 | /** 41 | * @var array html options to be applied to the [[initDefaultButtons()|default buttons]]. 42 | */ 43 | public $buttonOptions = []; 44 | /** 45 | * @var string route to the action, which should process position switching, for example: 'item/position'. 46 | */ 47 | public $route = 'position'; 48 | /** 49 | * @var string name of the query param, which is used for new position specification. 50 | */ 51 | public $positionParam = 'at'; 52 | /** 53 | * @var callable a callback that creates a button URL using the specified model information. 54 | * The signature of the callback should be the same as that of [[createUrl()]]. 55 | * If this property is not set, button URLs will be created using [[createUrl()]]. 56 | */ 57 | public $urlCreator; 58 | 59 | 60 | /** 61 | * {@inheritdoc} 62 | */ 63 | public function init() 64 | { 65 | parent::init(); 66 | $this->initDefaultButtons(); 67 | } 68 | 69 | /** 70 | * Initializes the default buttons. 71 | */ 72 | protected function initDefaultButtons() 73 | { 74 | $this->buttons = ArrayHelper::merge( 75 | [ 76 | 'first' => [ 77 | 'icon' => 'triangle-top', 78 | 'visible' => function ($model) { 79 | /* @var $model \yii\db\BaseActiveRecord */ 80 | if ($this->attribute !== null && isset($model[$this->attribute])) { 81 | return $model[$this->attribute] > 1; 82 | } 83 | return true; 84 | }, 85 | 'options' => [ 86 | 'title' => Yii::t('yii2tech-admin', 'Move top'), 87 | 'aria-label' => Yii::t('yii2tech-admin', 'Move top'), 88 | ], 89 | ], 90 | 'last' => [ 91 | 'icon' => 'triangle-bottom', 92 | 'visible' => function ($model) { 93 | /* @var $model \yii\db\BaseActiveRecord */ 94 | if ($this->attribute !== null && isset($model[$this->attribute])) { 95 | return $model[$this->attribute] < $this->grid->dataProvider->getTotalCount(); 96 | } 97 | return true; 98 | }, 99 | 'options' => [ 100 | 'title' => Yii::t('yii2tech-admin', 'Move bottom'), 101 | 'aria-label' => Yii::t('yii2tech-admin', 'Move bottom'), 102 | ], 103 | ], 104 | 'prev' => [ 105 | 'icon' => 'arrow-up', 106 | 'visible' => function ($model) { 107 | /* @var $model \yii\db\BaseActiveRecord */ 108 | if ($this->attribute !== null && isset($model[$this->attribute])) { 109 | return $model[$this->attribute] > 1; 110 | } 111 | return true; 112 | }, 113 | 'options' => [ 114 | 'title' => Yii::t('yii2tech-admin', 'Move up'), 115 | 'aria-label' => Yii::t('yii2tech-admin', 'Move up'), 116 | ], 117 | ], 118 | 'next' => [ 119 | 'icon' => 'arrow-down', 120 | 'visible' => function ($model) { 121 | /* @var $model \yii\db\BaseActiveRecord */ 122 | if ($this->attribute !== null && isset($model[$this->attribute])) { 123 | return $model[$this->attribute] < $this->grid->dataProvider->getTotalCount(); 124 | } 125 | return true; 126 | }, 127 | 'options' => [ 128 | 'title' => Yii::t('yii2tech-admin', 'Move down'), 129 | 'aria-label' => Yii::t('yii2tech-admin', 'Move down'), 130 | ], 131 | ], 132 | ], 133 | $this->buttons 134 | ); 135 | } 136 | 137 | /** 138 | * {@inheritdoc} 139 | */ 140 | protected function renderDataCellContent($model, $key, $index) 141 | { 142 | if ($this->content === null) { 143 | return preg_replace_callback('/\\{([\w\-\/]+)\\}/', function ($matches) use ($model, $key, $index) { 144 | $name = $matches[1]; 145 | if ($name === 'value') { 146 | return $this->grid->formatter->format($this->getDataCellValue($model, $key, $index), $this->format); 147 | } 148 | return $this->renderButton($name, $model, $key, $index); 149 | }, $this->template); 150 | } 151 | 152 | return parent::renderDataCellContent($model, $key, $index); 153 | } 154 | 155 | /** 156 | * Renders button. 157 | * @param string $name button name. 158 | * @param mixed $model 159 | * @param string $key 160 | * @param integer $index 161 | * @return string rendered HTML 162 | * @throws InvalidConfigException on invalid button format. 163 | */ 164 | protected function renderButton($name, $model, $key, $index) 165 | { 166 | if (!isset($this->buttons[$name])) { 167 | return ''; 168 | } 169 | $button = $this->buttons[$name]; 170 | 171 | if ($button instanceof \Closure) { 172 | $url = $this->createUrl($name, $model, $key, $index); 173 | return call_user_func($button, $url, $model, $key); 174 | } 175 | if (!is_array($button)) { 176 | throw new InvalidConfigException("Button should be either a Closure or array configuration."); 177 | } 178 | 179 | // Visibility : 180 | if (isset($button['visible'])) { 181 | if ($button['visible'] instanceof \Closure) { 182 | if (!call_user_func($button['visible'], $model, $key, $index)) { 183 | return ''; 184 | } 185 | } elseif (!$button['visible']) { 186 | return ''; 187 | } 188 | } 189 | 190 | // URL : 191 | if (isset($button['url'])) { 192 | $url = call_user_func($button['url'], $name, $model, $key, $index); 193 | } else { 194 | $url = $this->createUrl($name, $model, $key, $index); 195 | } 196 | 197 | // label : 198 | if (isset($button['label'])) { 199 | $label = $button['label']; 200 | 201 | if (isset($button['encode'])) { 202 | $encodeLabel = $button['encode']; 203 | unset($button['encode']); 204 | } else { 205 | $encodeLabel = true; 206 | } 207 | if ($encodeLabel) { 208 | $label = Html::encode($label); 209 | } 210 | } else { 211 | $label = ''; 212 | } 213 | 214 | // icon : 215 | if (isset($button['icon'])) { 216 | $icon = $button['icon']; 217 | $label = Html::icon($icon) . (empty($label) ? '' : ' ' . $label); 218 | } 219 | 220 | $options = array_merge(ArrayHelper::getValue($button, 'options', []), $this->buttonOptions); 221 | 222 | return Html::a($label, $url, $options); 223 | } 224 | 225 | /** 226 | * Creates a URL for the given position and model. 227 | * This method is called for each button and each row. 228 | * @param string $position the position name 229 | * @param \yii\db\BaseActiveRecord $model the data model 230 | * @param mixed $key the key associated with the data model 231 | * @param integer $index the current row index 232 | * @return string the created URL 233 | */ 234 | public function createUrl($position, $model, $key, $index) 235 | { 236 | if (is_callable($this->urlCreator)) { 237 | return call_user_func($this->urlCreator, $position, $model, $key, $index); 238 | } 239 | 240 | $params = array_merge( 241 | Yii::$app->getRequest()->getQueryParams(), 242 | is_array($key) ? $key : ['id' => (string) $key] 243 | ); 244 | $params[$this->positionParam] = $position; 245 | $params[0] = $this->route; 246 | 247 | return Url::toRoute($params); 248 | } 249 | } -------------------------------------------------------------------------------- /src/grid/VariationColumn.php: -------------------------------------------------------------------------------- 1 | 20 | * @since 1.0 21 | */ 22 | class VariationColumn extends DataColumn 23 | { 24 | /** 25 | * @var string model variation behavior name, which should be used by this column. 26 | * It should refer to [[\yii2tech\ar\variation\VariationBehavior]] instance. 27 | * If not set - model itself will be used for the method invocations. 28 | */ 29 | public $variationName; 30 | /** 31 | * @var string name of the variation model attribute. 32 | * If not set [[attribute]] value will be used. 33 | */ 34 | public $variationAttribute; 35 | /** 36 | * @var string|callable variation label source. This should be either a string - variation option 37 | * model attribute, which value should be used as variation label, or a callback of following signature: 38 | * 39 | * ```php 40 | * function ($mainModel, $variationModel) { 41 | * // return string label 42 | * } 43 | * ``` 44 | */ 45 | public $variationLabel; 46 | /** 47 | * @var array the HTML attributes for the variation table element. 48 | * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. 49 | */ 50 | public $tableOptions = ['class' => 'table table-bordered table-condensed', 'style' => 'margin-bottom:0px;']; 51 | 52 | /** 53 | * @var array internal cache for variation labels. 54 | */ 55 | private $_variationLabels; 56 | 57 | 58 | /** 59 | * {@inheritdoc} 60 | */ 61 | protected function renderDataCellContent($model, $key, $index) 62 | { 63 | if ($this->content === null) { 64 | $contentParts = []; 65 | $variationBehavior = $this->getVariationBehavior($model); 66 | foreach ($variationBehavior->getVariationModels() as $variationModel) { 67 | $contentParts[] = '' . $this->getVariationLabel($model, $variationModel) . '' . $this->getVariationValue($variationModel) . ''; 68 | } 69 | return Html::tag('table', implode("\n", $contentParts), $this->tableOptions); 70 | } 71 | 72 | return parent::renderDataCellContent($model, $key, $index); 73 | } 74 | 75 | /** 76 | * Returns the variation value. 77 | * @param \yii\base\Model $variationModel variation model instance. 78 | * @return string value. 79 | */ 80 | protected function getVariationValue($variationModel) 81 | { 82 | $attribute = $this->variationAttribute === null ? $this->attribute : $this->variationAttribute; 83 | return $this->grid->formatter->format($variationModel->{$attribute}, $this->format); 84 | } 85 | 86 | /** 87 | * Returns the variation label. 88 | * @param \yii\base\Model|\yii2tech\ar\variation\VariationBehavior $mainModel main model instance. 89 | * @param \yii\base\Model $variationModel variation model instance. 90 | * @return string label. 91 | * @throws InvalidConfigException on empty [[variationLabel]] value. 92 | */ 93 | protected function getVariationLabel($mainModel, $variationModel) 94 | { 95 | if (empty($this->variationLabel)) { 96 | throw new InvalidConfigException('"' . get_class($this) . '::$variationLabel" must be specified'); 97 | } 98 | if (!is_string($this->variationLabel)) { 99 | return call_user_func($this->variationLabel, $mainModel, $variationModel); 100 | } 101 | 102 | $variationLabels = $this->getVariationLabels($mainModel, $this->variationLabel); 103 | 104 | $variationBehavior = $this->getVariationBehavior($mainModel); 105 | $referenceAttribute = $variationBehavior->variationOptionReferenceAttribute; 106 | $variationPk = $variationModel->$referenceAttribute; 107 | 108 | if (isset($variationLabels[$variationPk])) { 109 | return $variationLabels[$variationPk]; 110 | } 111 | 112 | return $variationModel->$referenceAttribute; 113 | } 114 | 115 | /** 116 | * Returns all available variation labels. 117 | * @param \yii\base\Model $mainModel main model instance. 118 | * @param string $labelAttribute name of the attribute, which is used as label source. 119 | * @return array list labels in format: optionPk => label 120 | */ 121 | protected function getVariationLabels($mainModel, $labelAttribute) 122 | { 123 | if (!isset($this->_variationLabels[$labelAttribute])) { 124 | /* @var $optionClass \yii\db\ActiveRecordInterface */ 125 | $variationBehavior = $this->getVariationBehavior($mainModel); 126 | $optionClass = $variationBehavior->optionModelClass; 127 | foreach ($optionClass::find()->all() as $optionModel) { 128 | /* @var $optionModel \yii\db\ActiveRecordInterface */ 129 | $this->_variationLabels[$labelAttribute][$optionModel->getPrimaryKey()] = $optionModel->$labelAttribute; 130 | } 131 | } 132 | return $this->_variationLabels[$labelAttribute]; 133 | } 134 | 135 | /** 136 | * Gets the variation behavior from model. 137 | * @param \yii\base\Model $model model instance. 138 | * @return \yii2tech\ar\variation\VariationBehavior variation behavior instance. 139 | */ 140 | protected function getVariationBehavior($model) 141 | { 142 | return $this->variationName === null ? $model : $model->getBehavior($this->variationName); 143 | } 144 | } -------------------------------------------------------------------------------- /src/messages/config.php: -------------------------------------------------------------------------------- 1 | __DIR__ . DIRECTORY_SEPARATOR . '..', 6 | // array, required, list of language codes that the extracted messages 7 | // should be translated to. For example, ['zh-CN', 'de']. 8 | 'languages' => ['en', 'es', 'it', 'pt-BR', 'ru', 'uk'], 9 | // string, the name of the function for translating messages. 10 | // Defaults to 'Yii::t'. This is used as a mark to find the messages to be 11 | // translated. You may use a string for single function name or an array for 12 | // multiple function names. 13 | 'translator' => 'Yii::t', 14 | // boolean, whether to sort messages by keys when merging new messages 15 | // with the existing ones. Defaults to false, which means the new (untranslated) 16 | // messages will be separated from the old (translated) ones. 17 | 'sort' => true, 18 | // boolean, whether to remove messages that no longer appear in the source code. 19 | // Defaults to false, which means each of these messages will be enclosed with a pair of '@@' marks. 20 | 'removeUnused' => false, 21 | // array, list of patterns that specify which files (not directories) should be processed. 22 | // If empty or not set, all files will be processed. 23 | // Please refer to "except" for details about the patterns. 24 | 'only' => ['*.php'], 25 | // array, list of patterns that specify which files/directories should NOT be processed. 26 | // If empty or not set, all files/directories will be processed. 27 | // A path matches a pattern if it contains the pattern string at its end. For example, 28 | // '/a/b' will match all files and directories ending with '/a/b'; 29 | // the '*.svn' will match all files and directories whose name ends with '.svn'. 30 | // and the '.svn' will match all files and directories named exactly '.svn'. 31 | // Note, the '/' characters in a pattern matches both '/' and '\'. 32 | // See helpers/FileHelper::findFiles() description for more details on pattern matching rules. 33 | // If a file/directory matches both a pattern in "only" and "except", it will NOT be processed. 34 | 'except' => [ 35 | '.svn', 36 | '.git', 37 | '.gitignore', 38 | '.gitkeep', 39 | '.hgignore', 40 | '.hgkeep', 41 | '/messages', 42 | '/tests', 43 | '/runtime', 44 | '/vendor', 45 | ], 46 | 47 | // 'php' output format is for saving messages to php files. 48 | 'format' => 'php', 49 | // Root directory containing message translations. 50 | 'messagePath' => __DIR__, 51 | // boolean, whether the message file should be overwritten with the merged messages 52 | 'overwrite' => true, 53 | 54 | // Message categories to ignore 55 | 'ignoreCategories' => [ 56 | 'yii', 57 | ], 58 | ]; 59 | -------------------------------------------------------------------------------- /src/messages/en/yii2tech-admin.php: -------------------------------------------------------------------------------- 1 | 'Actual only', 21 | 'All records' => 'All records', 22 | 'Are you sure you want to restore defaults?' => 'Are you sure you want to restore defaults?', 23 | 'Are you sure you want to restore this item?' => 'Are you sure you want to restore this item?', 24 | 'Back' => 'Back', 25 | 'Context \'{name}\' required.' => 'Context \'{name}\' required.', 26 | 'Context object not found: {id}' => 'Context object not found: {id}', 27 | 'Create' => 'Create', 28 | 'Delete' => 'Delete', 29 | 'Deleted' => 'Deleted', 30 | 'Do it now' => 'Do it now', 31 | 'Import' => 'Import', 32 | 'Move bottom' => 'Move bottom', 33 | 'Move down' => 'Move down', 34 | 'Move top' => 'Move top', 35 | 'Move up' => 'Move up', 36 | 'Object not found: {id}' => 'Object not found: {id}', 37 | 'Restore' => 'Restore', 38 | 'Restore Defaults' => 'Restore Defaults', 39 | 'Update' => 'Update', 40 | 'View' => 'View', 41 | ]; 42 | -------------------------------------------------------------------------------- /src/messages/es/yii2tech-admin.php: -------------------------------------------------------------------------------- 1 | 'Actual unicamente', 21 | 'All records' => 'Todos los Registro', 22 | 'Are you sure you want to restore defaults?' => '¿Esta seguro que quiere restaurar por defecto?', 23 | 'Are you sure you want to restore this item?' => '¿Esta seguro que quiere restaurar este artículo', 24 | 'Back' => 'Volver', 25 | 'Context \'{name}\' required.' => 'Contexto \'{name}\' requerido.', 26 | 'Context object not found: {id}' => 'Objeto de contexto no encontrado: {id}', 27 | 'Create' => 'Сrear', 28 | 'Delete' => 'Borrar', 29 | 'Deleted' => 'Borrado', 30 | 'Do it now' => 'Hacerlo ahora', 31 | 'Import' => 'Importar', 32 | 'Move bottom' => 'Mover al fondo', 33 | 'Move down' => 'Mover abajo', 34 | 'Move top' => 'Mover al tope', 35 | 'Move up' => 'Mover arriva', 36 | 'Object not found: {id}' => 'Objeto no encontrado: {id}', 37 | 'Restore' => 'Restaurar', 38 | 'Restore Defaults' => 'Restaurar valores por defecto', 39 | 'Update' => 'Actualizar', 40 | 'View' => 'Ver', 41 | ]; 42 | -------------------------------------------------------------------------------- /src/messages/it/yii2tech-admin.php: -------------------------------------------------------------------------------- 1 | 'Actual only', 21 | 'All records' => 'Tutti i record', 22 | 'Are you sure you want to restore defaults?' => 'Sei sicuro di voler ripristinare i predefiniti?', 23 | 'Are you sure you want to restore this item?' => 'Sei sicuro di voler ripristinare questo elemento?', 24 | 'Back' => 'Indietro', 25 | 'Context \'{name}\' required.' => 'Contesto \'{name}\' richiesto.', 26 | 'Context object not found: {id}' => 'Oggetto del contesto {id} non trovato', 27 | 'Create' => 'Nuovo', 28 | 'Delete' => 'Cancella', 29 | 'Deleted' => 'Cancellato', 30 | 'Do it now' => 'Farlo ora', 31 | 'Import' => 'Importa', 32 | 'Move bottom' => 'Sposta in fondo', 33 | 'Move down' => 'Sposta in giù', 34 | 'Move top' => 'Sposta in cima', 35 | 'Move up' => 'Sposta in su', 36 | 'Object not found: {id}' => 'Oggetto non trovato: {id}', 37 | 'Restore' => 'Ripristina', 38 | 'Restore Defaults' => 'Ripristina predefiniti', 39 | 'Update' => 'Modifica', 40 | 'View' => 'Visualizza', 41 | ]; 42 | -------------------------------------------------------------------------------- /src/messages/pt-BR/yii2tech-admin.php: -------------------------------------------------------------------------------- 1 | 'Apenas atual', 21 | 'All records' => 'Todos registros', 22 | 'Are you sure you want to restore defaults?' => 'Tem certeza de que deseja restaurar os padrões?', 23 | 'Are you sure you want to restore this item?' => 'Tem certeza de que deseja restaurar este item?', 24 | 'Back' => 'Voltar', 25 | 'Context \'{name}\' required.' => 'Contexto \'{name}\' necessário.', 26 | 'Context object not found: {id}' => 'Contexto objeto não encontrado: {id}', 27 | 'Create' => 'Criar', 28 | 'Delete' => 'Excluir', 29 | 'Deleted' => 'Excluído', 30 | 'Do it now' => 'Fazê-lo agora', 31 | 'Import' => 'Importar', 32 | 'Move bottom' => 'Mover inferior', 33 | 'Move down' => 'Mover para baixo', 34 | 'Move top' => 'Mover topo', 35 | 'Move up' => 'Mover para cima', 36 | 'Object not found: {id}' => 'Objeto não encontrado: {id}', 37 | 'Restore' => 'Restaurar', 38 | 'Restore Defaults' => 'Restaurar padrões', 39 | 'Update' => 'Atualizar', 40 | 'View' => 'Visualizar', 41 | ]; 42 | -------------------------------------------------------------------------------- /src/messages/ru/yii2tech-admin.php: -------------------------------------------------------------------------------- 1 | 'Только актуальные', 21 | 'All records' => 'Все записи', 22 | 'Are you sure you want to restore defaults?' => 'Вы уверены, что хотите восстановить значения по умолчанию?', 23 | 'Are you sure you want to restore this item?' => 'Вы уверены, что хотите восстановить этот элемент?', 24 | 'Back' => 'Назад', 25 | 'Context \'{name}\' required.' => 'Контекст \'{name}\' должен быть задан.', 26 | 'Context object not found: {id}' => 'Контекстный объект не найден: {id}', 27 | 'Create' => 'Создать', 28 | 'Delete' => 'Удалить', 29 | 'Deleted' => 'Удалено', 30 | 'Do it now' => 'Выполнить сейчас', 31 | 'Import' => 'Импорт', 32 | 'Move bottom' => 'Переместить в конец', 33 | 'Move down' => 'Переместить вниз', 34 | 'Move top' => 'Переместить в начало', 35 | 'Move up' => 'Переместить вверх', 36 | 'Object not found: {id}' => 'Объект не найден: {id}', 37 | 'Restore' => 'Восстановить', 38 | 'Restore Defaults' => 'По умолчанию', 39 | 'Update' => 'Редактировать', 40 | 'View' => 'Просмотр', 41 | ]; 42 | -------------------------------------------------------------------------------- /src/messages/uk/yii2tech-admin.php: -------------------------------------------------------------------------------- 1 | 'Тільки актуальні', 21 | 'All records' => 'Усі записи', 22 | 'Are you sure you want to restore defaults?' => 'Ви впевнені, що хочете відновити стандартні значення?', 23 | 'Are you sure you want to restore this item?' => 'Ви впевнені, що хочете відновити цей елемент?', 24 | 'Back' => 'Назад', 25 | 'Context \'{name}\' required.' => 'Контекст \'{name}\' має бути заданий.', 26 | 'Context object not found: {id}' => 'Котекстний об\'єкт не знайдено: {id}', 27 | 'Create' => 'Створити', 28 | 'Delete' => 'Видалити', 29 | 'Deleted' => 'Видалено', 30 | 'Do it now' => 'Виконати зараз', 31 | 'Import' => 'Імпорт', 32 | 'Move bottom' => 'Перемістити в кінець', 33 | 'Move down' => 'Перемістити вниз', 34 | 'Move top' => 'Перемістити на початок', 35 | 'Move up' => 'Перемістити вгору', 36 | 'Object not found: {id}' => 'Об\'єкт не знайдено: {id}', 37 | 'Restore' => 'Відновити', 38 | 'Restore Defaults' => 'Відновити стандарт', 39 | 'Update' => 'Оновити', 40 | 'View' => 'Переглянути', 41 | ]; 42 | -------------------------------------------------------------------------------- /src/widgets/ActionAlert.php: -------------------------------------------------------------------------------- 1 | 33 | * @since 1.0.1 34 | */ 35 | class ActionAlert extends Widget 36 | { 37 | /** 38 | * @var array[] list of actions to be displayed. Each item should be an array of following structure: 39 | * 40 | * - title: string, optional, alert title as HTML (it will not be html-encoded). If not set - it will be composed from 41 | * action key in array using [[Inflector]]. 42 | * - url: array|string, action URL. 43 | * - visible: bool|callable, optional, condition, which check should be successful in order to make alert visible. 44 | * If not set - visibility is determined by value of the session variable, which names is equal to the action key in array. 45 | * - linkText: string, optional, action link text. 46 | * - linkOptions: array, optional, action link HTML options. 47 | * 48 | * For example: 49 | * 50 | * ```php 51 | * [ 52 | * 'cacheFlushRequired' => [ 53 | * 'title' => 'Cache Flush Required', 54 | * 'url' => ['/maintenance/flush-cache'], 55 | * ], 56 | * 'siteMapRegenerationRequired' => [ 57 | * 'title' => 'Sitemap regeneration Required', 58 | * 'linkText' => 'Regenerate Sitemap', 59 | * 'url' => ['/sitemap/generate'], 60 | * 'visible' => function () { 61 | * return SiteMapActiveRecord::find()->max('createdAt') < PageActiveRecord::find->max('createdAt'); 62 | * }, 63 | * ], 64 | * ] 65 | * ``` 66 | */ 67 | public $actions = []; 68 | /** 69 | * @var string alert body layout. Following placeholders are available: 70 | * 71 | * - {title} - Alert title text. 72 | * - {link} - link button HTML. 73 | */ 74 | public $layout = '{title} {link}'; 75 | /** 76 | * @var array the HTML attributes for the alert widget container tag. 77 | * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. 78 | */ 79 | public $options = [ 80 | 'class' => 'alert-warning' 81 | ]; 82 | /** 83 | * @var array|false the options for rendering the alert close button tag. 84 | */ 85 | public $closeButton = false; 86 | 87 | 88 | /** 89 | * {@inheritdoc} 90 | */ 91 | public function run() 92 | { 93 | $alerts = []; 94 | 95 | foreach ($this->actions as $key => $action) { 96 | $action = array_merge( 97 | [ 98 | 'title' => '', 99 | 'url' => '', 100 | 'linkText' => '', 101 | 'linkOptions' => [ 102 | 'class' => 'btn btn-warning', 103 | ], 104 | ], 105 | $action 106 | ); 107 | 108 | if (!isset($action['visible'])) { 109 | $action['visible'] = function () use ($key) { 110 | return (bool)Yii::$app->session->get($key, false); 111 | }; 112 | } 113 | 114 | if (is_bool($action['visible'])) { 115 | if ($action['visible']) { 116 | continue; 117 | } 118 | } else { 119 | if (!call_user_func($action['visible'])) { 120 | continue; 121 | } 122 | } 123 | 124 | Html::addCssClass($action['linkOptions'], ['widget' => 'btn-action-alert']); 125 | 126 | if (empty($action['title'])) { 127 | $action['title'] = Inflector::camel2words(Inflector::humanize($key)); 128 | } 129 | if (empty($action['linkText'])) { 130 | $action['linkText'] = Yii::t('yii2tech-admin', 'Do it now'); 131 | } 132 | 133 | $body = strtr($this->layout, [ 134 | '{title}' => $action['title'], 135 | '{link}' => Html::a($action['linkText'], $action['url'], $action['linkOptions']), 136 | ]); 137 | 138 | $alerts[] = Alert::widget([ 139 | 'body' => $body, 140 | 'closeButton' => $this->closeButton, 141 | 'options' => $this->options, 142 | ]); 143 | } 144 | 145 | return implode("\n", $alerts); 146 | } 147 | } -------------------------------------------------------------------------------- /src/widgets/Alert.php: -------------------------------------------------------------------------------- 1 | session->setFlash('error', 'This is the message'); 20 | * Yii::$app->session->setFlash('success', 'This is the message'); 21 | * Yii::$app->session->setFlash('info', 'This is the message'); 22 | * ``` 23 | * 24 | * Multiple messages could be set as following: 25 | * 26 | * ```php 27 | * Yii::$app->session->setFlash('error', ['Error 1', 'Error 2']); 28 | * ``` 29 | * 30 | * Since 1.0.3 alert type is determined via wildcard match, so messages could be set as following: 31 | * 32 | * ```php 33 | * Yii::$app->session->setFlash('saveSuccess', 'This is the success message'); 34 | * Yii::$app->session->setFlash('errorSave', 'This is the error message'); 35 | * ``` 36 | * 37 | * @see \yii\bootstrap\Alert 38 | * 39 | * @author Kartik Visweswaran 40 | * @author Alexander Makarov 41 | * @author Paul Klimov 42 | * @since 1.0 43 | */ 44 | class Alert extends Widget 45 | { 46 | /** 47 | * @var array the alert types configuration for the flash messages. 48 | * This array is setup as $key => $value, where: 49 | * 50 | * - $key is the case-insensitive wildcard pattern for the name of the session flash variable. 51 | * - $value is the bootstrap alert type (i.e. danger, success, info, warning). 52 | */ 53 | public $alertTypes = [ 54 | '*error*' => 'alert-danger', 55 | '*danger*' => 'alert-danger', 56 | '*success*' => 'alert-success', 57 | '*warning*' => 'alert-warning', 58 | '*' => 'alert-info', 59 | ]; 60 | /** 61 | * @var array the options for rendering the close button tag. 62 | */ 63 | public $closeButton = []; 64 | 65 | 66 | /** 67 | * {@inheritdoc} 68 | */ 69 | public function run() 70 | { 71 | $session = Yii::$app->session; 72 | $flashes = $session->getAllFlashes(); 73 | $appendCss = isset($this->options['class']) ? ' ' . $this->options['class'] : ''; 74 | 75 | $alerts = []; 76 | foreach ($flashes as $type => $data) { 77 | foreach ($this->alertTypes as $pattern => $css) { 78 | if (StringHelper::matchWildcard($pattern, $type, ['caseSensitive' => false])) { 79 | $data = (array) $data; 80 | foreach ($data as $i => $message) { 81 | /* initialize css class for each alert box */ 82 | $this->options['class'] = $css . $appendCss; 83 | 84 | /* assign unique id to each alert box */ 85 | $this->options['id'] = $this->getId() . '-' . $type . '-' . $i; 86 | 87 | $alerts[] = \yii\bootstrap\Alert::widget([ 88 | 'body' => $message, 89 | 'closeButton' => $this->closeButton, 90 | 'options' => $this->options, 91 | ]); 92 | } 93 | 94 | $session->removeFlash($type); 95 | break; 96 | } 97 | } 98 | } 99 | 100 | return implode("\n", $alerts); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/widgets/ButtonContextMenu.php: -------------------------------------------------------------------------------- 1 | 19 | * @package yii2tech\admin\widgets 20 | */ 21 | class ButtonContextMenu extends Widget 22 | { 23 | /** 24 | * @var array[] list of items. Each array element represents a single menu item, which should be an array. 25 | * Item array should have the following structure: 26 | * 27 | * - url: array, required, the item's URL. 28 | * - label: string, optional, the item label. 29 | * - encode: boolean, optional, whether to encode item label. 30 | * - icon: string, optional, item label icon short name. 31 | * 32 | * Any additional keys will be used as link tag options. 33 | * 34 | * If item array contains zero key, it will be taken as 'url' key. 35 | */ 36 | public $items = []; 37 | /** 38 | * @var boolean whether the nav items labels should be HTML-encoded. 39 | */ 40 | public $encodeLabels = true; 41 | 42 | 43 | /** 44 | * {@inheritdoc} 45 | */ 46 | public function run() 47 | { 48 | $contentParts = []; 49 | foreach ($this->items as $item) { 50 | $contentParts[] = $this->renderItem($item); 51 | } 52 | return implode("\n", $contentParts); 53 | } 54 | 55 | /** 56 | * Renders single item. 57 | * @param array $item item configuration. 58 | * @return string rendered HTML 59 | */ 60 | protected function renderItem($item) 61 | { 62 | if (isset($item[0])) { 63 | $url = $item; 64 | $options = []; 65 | } else { 66 | $url = $item['url']; 67 | $options = $item; 68 | unset($options['url']); 69 | } 70 | 71 | // label : 72 | if (isset($options['label'])) { 73 | $label = $options['label']; 74 | unset($options['label']); 75 | } else { 76 | $label = $this->detectLabel($url); 77 | } 78 | if (isset($options['encode'])) { 79 | $encodeLabel = $options['encode']; 80 | unset($options['encode']); 81 | } else { 82 | $encodeLabel = $this->encodeLabels; 83 | } 84 | if ($encodeLabel) { 85 | $label = Html::encode($label); 86 | } 87 | 88 | // icon : 89 | if (isset($options['icon'])) { 90 | $icon = $options['icon']; 91 | unset($options['icon']); 92 | } else { 93 | $icon = $this->detectIcon($url); 94 | } 95 | if ($icon) { 96 | $label = Html::icon($icon) . ' ' . $label; 97 | } 98 | 99 | // CSS class : 100 | if (isset($options['class'])) { 101 | Html::addCssClass($options, ['widget' => 'btn']); 102 | } else { 103 | $options['class'] = [ 104 | 'btn', 105 | $this->detectClass($url) 106 | ]; 107 | } 108 | 109 | if (!isset($options['data'])) { 110 | $options['data'] = $this->detectData($url); 111 | } 112 | 113 | return Html::a($label, $url, $options); 114 | } 115 | 116 | /** 117 | * @param array $url URL config. 118 | * @return string label 119 | */ 120 | protected function detectLabel($url) 121 | { 122 | switch ($url[0]) { 123 | case 'index': 124 | return Yii::t('yii2tech-admin', 'Back'); 125 | case 'create': 126 | return Yii::t('yii2tech-admin', 'Create'); 127 | case 'update': 128 | return Yii::t('yii2tech-admin', 'Update'); 129 | case 'delete': 130 | return Yii::t('yii2tech-admin', 'Delete'); 131 | case 'view': 132 | return Yii::t('yii2tech-admin', 'View'); 133 | case 'default': 134 | return Yii::t('yii2tech-admin', 'Restore Defaults'); 135 | case 'import': 136 | return Yii::t('yii2tech-admin', 'Import'); 137 | default: 138 | return ucfirst($url[0]); 139 | } 140 | } 141 | 142 | /** 143 | * @param array $url URL config 144 | * @return boolean|string icon short name, 'false' on failure 145 | */ 146 | protected function detectIcon($url) 147 | { 148 | switch ($url[0]) { 149 | case 'index': 150 | return 'arrow-left'; 151 | case 'create': 152 | return 'plus'; 153 | case 'update': 154 | return 'pencil'; 155 | case 'delete': 156 | return 'trash'; 157 | case 'view': 158 | return 'eye-open'; 159 | case 'default': 160 | return 'btn-repeat'; 161 | case 'import': 162 | return 'import'; 163 | default: 164 | return false; 165 | } 166 | } 167 | 168 | /** 169 | * @param array $url URL config 170 | * @return string CSS class. 171 | */ 172 | protected function detectClass($url) 173 | { 174 | switch ($url[0]) { 175 | case 'index': 176 | return 'btn-default'; 177 | case 'create': 178 | return 'btn-success'; 179 | case 'update': 180 | return 'btn-primary'; 181 | case 'delete': 182 | return 'btn-danger'; 183 | case 'view': 184 | return 'btn-info'; 185 | case 'default': 186 | return 'btn-danger'; 187 | case 'import': 188 | return 'btn-success'; 189 | default: 190 | return 'btn-default'; 191 | } 192 | } 193 | 194 | /** 195 | * @param array $url URL config 196 | * @return array|null link data 197 | */ 198 | protected function detectData($url) 199 | { 200 | switch ($url[0]) { 201 | case 'delete': 202 | return [ 203 | 'confirm' => Yii::t('yii', 'Are you sure you want to delete this item?'), 204 | 'method' => 'post', 205 | ]; 206 | case 'restore': 207 | return [ 208 | 'confirm' => Yii::t('yii2tech-admin', 'Are you sure you want to restore this item?'), 209 | 'method' => 'post', 210 | ]; 211 | case 'default': 212 | return [ 213 | 'confirm' => Yii::t('yii2tech-admin', 'Are you sure you want to restore defaults?'), 214 | ]; 215 | default: 216 | return null; 217 | } 218 | } 219 | } -------------------------------------------------------------------------------- /src/widgets/Nav.php: -------------------------------------------------------------------------------- 1 | 19 | * @since 1.0 20 | */ 21 | class Nav extends \yii\bootstrap\Nav 22 | { 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function renderItem($item) 27 | { 28 | return parent::renderItem($this->normalizeItem($item)); 29 | } 30 | 31 | /** 32 | * @param string|array $item the item to be normalized. 33 | * @return string|array normalized item. 34 | */ 35 | protected function normalizeItem($item) 36 | { 37 | if (is_array($item)) { 38 | if (isset($item['icon'])) { 39 | if (isset($item['label'])) { 40 | $label = $item['label']; 41 | $encodeLabel = isset($item['encode']) ? $item['encode'] : $this->encodeLabels; 42 | if ($encodeLabel) { 43 | $label = Html::encode($label); 44 | } 45 | } else { 46 | $label = ''; 47 | } 48 | $item['encode'] = false; 49 | $label = Html::icon($item['icon']) . ' ' . $label; 50 | $item['label'] = $label; 51 | } 52 | if (isset($item['items'])) { 53 | foreach ($item['items'] as $key => $value) { 54 | $item['items'][$key] = $this->normalizeItem($value); 55 | } 56 | } 57 | } 58 | return $item; 59 | } 60 | } --------------------------------------------------------------------------------