├── .gitignore ├── README.md ├── composer.json ├── src ├── Module.php ├── assets │ ├── BackendAsset.php │ ├── BackendJsAsset.php │ ├── ModalAsset.php │ ├── ToolbarAsset.php │ ├── backend-js │ │ └── is-framed.js │ ├── backend │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ └── less │ │ │ ├── backend.less │ │ │ └── json-editor.less │ ├── modal │ │ ├── js │ │ │ └── modal.js │ │ └── less │ │ │ └── widget.less │ └── toolbar │ │ ├── js │ │ ├── check-frame.js │ │ └── widget.js │ │ └── less │ │ └── widget.less ├── controllers │ ├── CacheController.php │ ├── ConfigController.php │ ├── DefaultController.php │ └── RbacController.php ├── interfaces │ └── ContextMenuItemsInterface.php ├── migrations │ ├── m160926_160610_settings.php │ └── m181207_200300_settings.php ├── views │ ├── config │ │ ├── _controller.php │ │ ├── _module.php │ │ └── view.php │ ├── default │ │ └── index.php │ ├── layouts │ │ ├── _control-sidebar.php │ │ ├── _sidebar.php │ │ ├── box.php │ │ ├── login.php │ │ └── main.php │ └── rbac │ │ ├── index.twig │ │ └── show-auth.php └── widgets │ ├── Modal.php │ ├── Toolbar.php │ └── views │ ├── modal.twig │ └── toolbar.twig └── tests ├── .env ├── Dockerfile ├── docker-compose.yml └── project ├── composer.json ├── composer.lock └── config └── test.php /.gitignore: -------------------------------------------------------------------------------- 1 | tests/project/vendor -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Backend module for Yii 2.0 Framework 2 | ==================================== 3 | 4 | [![Latest Stable Version](https://poser.pugx.org/dmstr/yii2-backend-module/v/stable.svg)](https://packagist.org/packages/dmstr/yii2-backend-module) 5 | [![Total Downloads](https://poser.pugx.org/dmstr/yii2-backend-module/downloads.svg)](https://packagist.org/packages/dmstr/yii2-backend-module) 6 | [![License](https://poser.pugx.org/dmstr/yii2-backend-module/license.svg)](https://packagist.org/packages/dmstr/yii2-backend-module) 7 | 8 | 9 | ### AdminLTE Dashboard 10 | 11 | ![Screenshot](https://raw.githubusercontent.com/dmstr/gh-media/master/dmstr/yii2-backend-module/backend-default-index.png) 12 | 13 | Installation 14 | ------------ 15 | 16 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 17 | 18 | ``` 19 | composer require dmstr/yii2-backend-module 20 | ``` 21 | 22 | - Requires `loveorigami/yii2-notification-wrapper` 23 | 24 | Usage 25 | ----- 26 | 27 | Add module to application configuration 28 | 29 | ``` 30 | 'backend' => [ 31 | 'class' => 'dmstr\modules\backend\Module', 32 | 'layout' => '@backend/views/layouts/main', 33 | ], 34 | ``` 35 | 36 | ### Minimalistic Login view from AdminLTE 2 37 | 38 | ``` 39 | 'modules' => [ 40 | 'user' => [ 41 | 'controllerMap' => [ 42 | 'security' => [ 43 | 'class' => Da\User\Controller\SecurityController::class, 44 | 'layout' => '@backend/views/layouts/login' 45 | ], 46 | 'recovery' => [ 47 | 'class' => Da\User\Controller\RecoveryController::class, 48 | 'layout' => '@backend/views/layouts/login' 49 | ], 50 | 'registration' => [ 51 | 'class' => Da\User\Controller\RegistrationController::class, 52 | 'layout' => '@backend/views/layouts/login' 53 | ] 54 | ] 55 | ] 56 | ] 57 | ``` 58 | 59 | ### Params 60 | 61 | - `context.menuItems` menu items to be shown, i.e. used by `dmstr/yii2-prototype-module` 62 | 63 | ### Settings 64 | 65 | *from `settings` module* 66 | 67 | - `backend.adminlte.skin` default `black-light` 68 | - `backend.adminlte.sidebar` default `sidebar-mini` 69 | - `backend.adminlte.navBarIcon` default `heart` 70 | - `frontend.backendWidget` default `modal` 71 | - `frontend.backendButtonsInModal` (experimental) 72 | 73 | 74 | --- 75 | 76 | #### ![dmstr logo](http://t.phundament.com/dmstr-16-cropped.png) Built by [dmstr](http://diemeisterei.de) 77 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dmstr/yii2-backend-module", 3 | "description": "Admin Dashboard", 4 | "type": "yii2-extension", 5 | "keywords": ["yii2","extension"], 6 | "license": "BSD-3-Clause", 7 | "authors": [ 8 | { 9 | "name": "Tobias Munk", 10 | "email": "tobias@diemeisterei.de" 11 | } 12 | ], 13 | "require": { 14 | "yiisoft/yii2": "2.*", 15 | "bower-asset/jquery-growl": "^1.3", 16 | "bower-asset/noty": "^2.3", 17 | "dmstr/yii2-cookie-button": "~0.2 || ^1.0.0", 18 | "dmstr/yii2-helpers": "~0.4 || ^1.0.1", 19 | "dmstr/yii2-web": "~0.3 || ^0.4.0 || ^1.0.0", 20 | "dmstr/yii2-adminlte-asset": "^2.5.0", 21 | "insolita/yii2-adminlte-widgets": "^1.1.4", 22 | "loveorigami/yii2-notification-wrapper": "^6.3", 23 | "rmrevin/yii2-fontawesome": "^2.17" 24 | }, 25 | "suggest": { 26 | "codemix/yii2-localeurls": "Extended URL manager", 27 | "dmstr/yii2-pages-module": "Menu manager", 28 | "dmstr/yii2-prototype-module": "Content prototyping", 29 | "dmstr/lajax-yii2-translate-manager-addons": "Translating" 30 | }, 31 | "autoload": { 32 | "psr-4": { 33 | "dmstr\\modules\\backend\\": "src" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Module.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | class Module extends \yii\base\Module 23 | { 24 | use AccessBehaviorTrait; 25 | 26 | /** 27 | * @var array Names of (auto-detected) modules which should not be show on dashboard 28 | */ 29 | public $modulesDashboardBlacklist = []; 30 | 31 | public $rbacDiagramExcludeRoles = ['Master']; 32 | 33 | /** 34 | * @param $label 35 | * 36 | * @return mixed 37 | */ 38 | public static function colorHash($label) 39 | { 40 | $colors = [ 41 | InfoBox::TYPE_NAVY, 42 | InfoBox::TYPE_LBLUE, 43 | InfoBox::TYPE_BLUE, 44 | InfoBox::TYPE_AQUA, 45 | InfoBox::TYPE_PURPLE, 46 | InfoBox::TYPE_MAR, 47 | InfoBox::TYPE_TEAL, 48 | InfoBox::TYPE_OLIVE, 49 | ]; 50 | /*$brightColors = [ 51 | InfoBox::TYPE_RED, 52 | InfoBox::TYPE_GREEN, 53 | InfoBox::TYPE_YEL, 54 | InfoBox::TYPE_LIME, 55 | InfoBox::TYPE_ORANGE, 56 | InfoBox::TYPE_FUS, 57 | #InfoBox::TYPE_BLACK, 58 | #InfoBox::TYPE_GRAY, 59 | ];*/ 60 | 61 | srand(crc32($label)); 62 | $rand = array_rand($colors); 63 | return $colors[$rand]; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/assets/BackendAsset.php: -------------------------------------------------------------------------------- 1 | View::POS_BEGIN 27 | ]; 28 | } 29 | -------------------------------------------------------------------------------- /src/assets/ModalAsset.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class ModalAsset extends AssetBundle 22 | { 23 | public $sourcePath = '@vendor/dmstr/yii2-backend-module/src/assets/modal'; 24 | 25 | public $js = ['js/modal.js']; 26 | 27 | public $css = ['less/widget.less']; 28 | 29 | public $depends = [ 30 | JqueryAsset::class, 31 | \rmrevin\yii\fontawesome\AssetBundle::class 32 | ]; 33 | } -------------------------------------------------------------------------------- /src/assets/ToolbarAsset.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class ToolbarAsset extends AssetBundle 22 | { 23 | public $sourcePath = '@vendor/dmstr/yii2-backend-module/src/assets/toolbar'; 24 | 25 | public $js = ['js/widget.js']; 26 | 27 | public $css = ['less/widget.less']; 28 | 29 | public $depends = [ 30 | JqueryAsset::class, 31 | \rmrevin\yii\fontawesome\AssetBundle::class 32 | ]; 33 | } -------------------------------------------------------------------------------- /src/assets/backend-js/is-framed.js: -------------------------------------------------------------------------------- 1 | if (top.location != location) { 2 | document.body.className += ' ' + 'framed'; 3 | } else { 4 | document.body.className += ' ' + 'unframed'; 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/backend/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmstr/yii2-backend-module/7008e4748905253e8fd0fc3e6e2b8b92db58dd45/src/assets/backend/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/assets/backend/fonts/glyphicons-halflings-regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /src/assets/backend/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmstr/yii2-backend-module/7008e4748905253e8fd0fc3e6e2b8b92db58dd45/src/assets/backend/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/assets/backend/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmstr/yii2-backend-module/7008e4748905253e8fd0fc3e6e2b8b92db58dd45/src/assets/backend/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/assets/backend/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmstr/yii2-backend-module/7008e4748905253e8fd0fc3e6e2b8b92db58dd45/src/assets/backend/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/assets/backend/less/backend.less: -------------------------------------------------------------------------------- 1 | @bowerAssetPath: '../../../vendor/bower-asset'; 2 | 3 | @import (reference) "@{bowerAssetPath}/bootstrap/less/bootstrap.less"; 4 | @import "json-editor"; 5 | 6 | html, 7 | body { 8 | height: 100%; 9 | } 10 | 11 | BODY.framed { 12 | .dashboard-menu, .context-items-menu, .languages-menu, .sitemap-menu { 13 | display: none; 14 | } 15 | } 16 | 17 | BODY.unframed { 18 | .expand-menu { 19 | display: none; 20 | } 21 | } 22 | 23 | header { 24 | .logo { 25 | .title { 26 | .hidden-md; 27 | } 28 | } 29 | } 30 | 31 | .wrap { 32 | min-height: 100%; 33 | height: auto; 34 | margin: 0 auto -60px; 35 | padding: 0 0 60px; 36 | } 37 | 38 | .wrap > .container { 39 | padding: 70px 15px 20px; 40 | } 41 | 42 | .footer { 43 | height: 60px; 44 | background-color: #f5f5f5; 45 | border-top: 1px solid #ddd; 46 | padding-top: 20px; 47 | } 48 | 49 | .main-header { 50 | nav { 51 | .sidebar-toggle { 52 | padding: 10px; 53 | margin: 4px; 54 | } 55 | } 56 | } 57 | 58 | .user-menu .dropdown-toggle span { 59 | @media (max-width: 768px) { 60 | display: none; 61 | } 62 | } 63 | 64 | .control-sidebar { 65 | a.active { 66 | color: white; 67 | } 68 | } 69 | 70 | section.content-header, section.content { 71 | @media (max-width: 768px) { 72 | padding-left: 10px; 73 | padding-right: 10px; 74 | } 75 | } 76 | 77 | section.content { 78 | .grid-view { 79 | @media (max-width: 768px) { 80 | border: 0px solid #fff !important; 81 | } 82 | .table-responsive; 83 | table { 84 | //.table-condensed; 85 | 86 | th { 87 | &.action-column { 88 | min-width: 150px; 89 | width: 1%; 90 | } 91 | white-space: nowrap; 92 | } 93 | 94 | a { 95 | &[data-pjax="0"] { 96 | .btn; 97 | border: 1px solid; 98 | &:hover { 99 | //.btn-primary; 100 | } 101 | } 102 | &[data-method="post"], 103 | &[data-method="POST"] { 104 | .btn; 105 | border: 1px solid; 106 | //.text-danger; 107 | &:hover { 108 | //.btn-danger; 109 | } 110 | } 111 | 112 | } 113 | } 114 | 115 | } 116 | } 117 | 118 | .table-responsive { 119 | @media (max-width: 768px) { 120 | border: 0px solid #fff !important; 121 | } 122 | 123 | } 124 | 125 | .detail-view { 126 | TH { 127 | width: 1%; 128 | white-space: nowrap; 129 | } 130 | TD { 131 | PRE { 132 | overflow: auto; 133 | word-wrap: normal; 134 | white-space: pre-wrap; 135 | } 136 | } 137 | } 138 | 139 | div.alert p { 140 | max-height: 200px; 141 | max-width: 100%; 142 | overflow: auto; 143 | } 144 | 145 | .not-set { 146 | color: #c55; 147 | font-style: italic; 148 | } 149 | 150 | a { 151 | &:focus, 152 | &:hover { 153 | text-decoration: none; 154 | } 155 | } 156 | 157 | /** fix for select2 **/ 158 | .select2-container { 159 | .select2-selection--single { 160 | .select2-selection__rendered { 161 | margin-top: 0; 162 | } 163 | } 164 | } 165 | 166 | /* add sorting icons to gridview sort links */ 167 | a.asc:after, a.desc:after { 168 | position: relative; 169 | top: 1px; 170 | display: inline-block; 171 | font-family: 'Glyphicons Halflings'; 172 | font-style: normal; 173 | font-weight: normal; 174 | line-height: 1; 175 | padding-left: 5px; 176 | } 177 | 178 | a.asc:after { 179 | content: /*"\e113"*/ "\e151"; 180 | } 181 | 182 | a.desc:after { 183 | content: /*"\e114"*/ "\e152"; 184 | } 185 | 186 | .sort-numerical a.asc:after { 187 | content: "\e153"; 188 | } 189 | 190 | .sort-numerical a.desc:after { 191 | content: "\e154"; 192 | } 193 | 194 | .sort-ordinal a.asc:after { 195 | content: "\e155"; 196 | } 197 | 198 | .sort-ordinal a.desc:after { 199 | content: "\e156"; 200 | } 201 | 202 | .jumbotron { 203 | text-align: center; 204 | background-color: transparent; 205 | } 206 | 207 | .jumbotron .btn { 208 | font-size: 21px; 209 | padding: 14px 24px; 210 | } 211 | 212 | .hint-block { 213 | display: block; 214 | margin-top: 5px; 215 | color: #999; 216 | } 217 | 218 | .error-summary { 219 | color: #a94442; 220 | background: #fdf7f7; 221 | border-left: 3px solid #eed3d7; 222 | padding: 10px 20px; 223 | margin: 0 0 15px 0; 224 | } 225 | 226 | // Fix for cursor position bug (WebKit) 227 | // see https://github.com/ajaxorg/ace/issues/1078#issuecomment-12839690 and http://stackoverflow.com/a/20932217/291573 228 | .ace_editor { 229 | font-family: monospace !important; 230 | font-size: 16px !important; 231 | } 232 | 233 | .info-box { 234 | A { 235 | color: white; 236 | } 237 | } 238 | 239 | .giiant-crud { 240 | .crud-navigation { 241 | margin: 1em 0; 242 | } 243 | 244 | .form-control, .select2-selection, .selectize-input { 245 | border-radius: 0px; 246 | } 247 | 248 | .well-sm { 249 | padding: 5px; 250 | border: none; 251 | box-shadow: none; 252 | //border-radius: @border-radius-small; 253 | } 254 | 255 | } 256 | 257 | .docs { 258 | .toc { 259 | h1 { 260 | .lead; 261 | margin-top: 0.5em; 262 | } 263 | h2 { 264 | font-size: 1em; 265 | font-weight: bold; 266 | margin-top: 1em; 267 | } 268 | ul { 269 | .list-group; 270 | margin-bottom: 0.5em; 271 | } 272 | & > ul > li { 273 | .list-group-item; 274 | } 275 | ul li ul li { 276 | margin-left: 1em; 277 | } 278 | } 279 | .file-content { 280 | font-size: 1.2em; 281 | line-height: 1.6em; 282 | blockquote { 283 | background: lighten(@gray-lighter, 3%); 284 | } 285 | } 286 | } 287 | 288 | .login-box { 289 | width: auto; 290 | } 291 | 292 | .login-box-body { 293 | background: none; 294 | } 295 | 296 | div[class^="col-"]>.btn-app { 297 | margin: 0 0 10px 0; 298 | } 299 | -------------------------------------------------------------------------------- /src/assets/backend/less/json-editor.less: -------------------------------------------------------------------------------- 1 | // json-editor well padding 2 | .well-small { 3 | .well-sm; 4 | } 5 | 6 | // json-editor Bootstrap 3 fix 7 | div[id$="-container"] div[data-schemaid] { 8 | .form-group { 9 | margin-right: 0px; 10 | margin-left: 0px; 11 | } 12 | 13 | h3 { 14 | font-size: 1em; 15 | margin-top: 0.5em; 16 | } 17 | } 18 | 19 | div[data-schematype="object"] { 20 | .well; 21 | .well-small; 22 | } 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/assets/modal/js/modal.js: -------------------------------------------------------------------------------- 1 | // assign keyboard-action for modal and view mode 2 | // - only when focus is on , to avoid unwanted keyboard actions 3 | 4 | $(document).on("keypress", function (e) { 5 | if ($(document.activeElement).is('body') && e.originalEvent.repeat !== true) { 6 | // open menu (m) 7 | if (e.charCode === 109) { 8 | $('#phd-info-button a').click(); 9 | } 10 | // toggle view-mode (v) 11 | if (e.charCode === 118) { 12 | $('#app-frontend-view-mode-button').click(); 13 | } 14 | // toggle button (h) 15 | if (e.charCode === 104) { 16 | $('#phd-info-button').toggle(); 17 | } 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /src/assets/modal/less/widget.less: -------------------------------------------------------------------------------- 1 | #phd-info-button { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | opacity: 0.5; 6 | } 7 | 8 | #phd-info-modal,#phd-info-button { 9 | z-index: 1200; // so it ist above bootstrap modals 10 | } 11 | -------------------------------------------------------------------------------- /src/assets/toolbar/js/check-frame.js: -------------------------------------------------------------------------------- 1 | function breakoutOfFrame() 2 | { 3 | // see https://www.thesitewizard.com/archive/framebreak.shtml 4 | // for an explanation of this script and how to use it on your 5 | // own website 6 | if (top.location != location) { 7 | top.location.href = document.location.href ; 8 | } else { 9 | document.body.className += ' ' + 'home'; 10 | } 11 | } 12 | 13 | breakoutOfFrame(); -------------------------------------------------------------------------------- /src/assets/toolbar/js/widget.js: -------------------------------------------------------------------------------- 1 | function toggleSidebar() { 2 | $("#sidebar-wrapper").toggleClass("active"); 3 | } 4 | 5 | $(document).ready(function () { 6 | 7 | var initialRequest = true; 8 | $('#sidebar-wrapper iframe').on('load', function () { 9 | console.log('iframe load'); 10 | if (!initialRequest) { 11 | $('#sidebar-wrapper').addClass('active'); 12 | $('#sidebar-wrapper').addClass('show-iframe'); 13 | } 14 | initialRequest = false; 15 | }); 16 | 17 | $('.hide-iframe') 18 | .on('mouseover', function () { 19 | $('#sidebar-wrapper').addClass('hide-iframe'); 20 | }) 21 | .on('mouseout', function () { 22 | $('#sidebar-wrapper').removeClass('hide-iframe'); 23 | $('#sidebar-wrapper').removeClass('show-iframe'); 24 | }) 25 | 26 | console.log('Done'); 27 | }); 28 | -------------------------------------------------------------------------------- /src/assets/toolbar/less/widget.less: -------------------------------------------------------------------------------- 1 | #sidebar-wrapper { 2 | 3 | #sidebar-toggle { 4 | position: absolute; 5 | top: 0; 6 | right: -50px; 7 | width: 50px; 8 | opacity: 0.85; 9 | &:hover { 10 | opacity: 1; 11 | } 12 | } 13 | 14 | position: fixed; 15 | top: 0; 16 | z-index: 1100; 17 | width: 80vw; 18 | max-width: 80%; 19 | margin-left: -80vw; 20 | 21 | transition-property: margin-left; 22 | transition-duration: 0.3s; 23 | transition-timing-function: ease-in-out; 24 | 25 | nav { 26 | margin-bottom: 0; 27 | } 28 | 29 | iframe { 30 | border: 0 solid white; 31 | 32 | background: #ccc; 33 | height: 100vh; 34 | width: 100%; 35 | } 36 | 37 | .sidebar-panel { 38 | height: 100vh; 39 | overflow: auto; 40 | opacity: 0.1; 41 | 42 | transition-property: opacity; 43 | transition-duration: 0.5s; 44 | transition-timing-function: ease-in-out; 45 | } 46 | 47 | &.active { 48 | margin-left: 0; 49 | } 50 | &:hover { 51 | .sidebar-panel { 52 | opacity: 1; 53 | } 54 | } 55 | 56 | &.show-iframe { 57 | .sidebar-panel { 58 | opacity: 1; 59 | } 60 | } 61 | &.hide-iframe { 62 | .sidebar-panel { 63 | opacity: 0; 64 | } 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /src/controllers/CacheController.php: -------------------------------------------------------------------------------- 1 | cache->flush()) { 29 | \Yii::$app->session->addFlash('success', Yii::t('backend-module','Cache cleared')); 30 | } else { 31 | \Yii::$app->session->addFlash('error', Yii::t('backend-module','Cannot clear cache')); 32 | } 33 | return $this->redirect(['default/index']); 34 | } 35 | } -------------------------------------------------------------------------------- /src/controllers/ConfigController.php: -------------------------------------------------------------------------------- 1 | $loadedModules]); 23 | $loadedModulesDataProvider->pagination->pageSize = 100; 24 | 25 | $components = Yii::$app->getComponents(); 26 | ksort($components); 27 | $modules = Yii::$app->getModules(); 28 | ksort($modules); 29 | $env = $_ENV; 30 | ksort($env); 31 | 32 | 33 | return $this->render( 34 | 'view', 35 | [ 36 | 'params' => Yii::$app->params, 37 | 'components' => $components, 38 | 'modules' => $modules, 39 | 'env' => $env, 40 | 'loadedModulesDataProvider' => $loadedModulesDataProvider, 41 | ] 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/controllers/DefaultController.php: -------------------------------------------------------------------------------- 1 | hasModule('pages')) { 29 | // no `use` statement, since module is optional 30 | $items = \dmstr\modules\pages\models\Tree::getMenuItems('backend', true); 31 | } else { 32 | $items = []; 33 | } 34 | 35 | return $this->render('index', ['items' => $items]); 36 | } 37 | 38 | 39 | 40 | /** 41 | * @param array $item \dmstr\modules\pages\models\Tree::getMenuItems() 42 | * 43 | * @return string 44 | * @throws \Exception 45 | */ 46 | public function renderDashboardMenu($item = []) 47 | { 48 | $menuItems = ''; 49 | 50 | if (!isset($item['items'])) { 51 | return $menuItems; 52 | } 53 | 54 | foreach ($item['items'] as $subItem) { 55 | if ($subItem['visible'] && $subItem['url'] && $subItem['url'] !== '#') { 56 | $colorSelect = ArrayHelper::getValue($item, 'icon'); 57 | $menuItems .= '
'; 58 | $infoBoxHtml = InfoBox::widget( 59 | [ 60 | 'text' => '

'.$subItem['label'].'

', 61 | 'boxBg' => Module::colorHash($colorSelect[2] ?? 0), 62 | 'icon' => (isset($subItem['icon']) && !empty($subItem['icon'])) 63 | ? Menu::$iconClassPrefix.$subItem['icon'] 64 | : 'fa fa-circle-o', 65 | ]); 66 | $menuItems .= Html::a($infoBoxHtml, $subItem['url']); 67 | $menuItems .= '
'; 68 | } 69 | 70 | if (!empty($subItem['items'])) { 71 | $menuItems .= $this->renderDashboardMenu($subItem); 72 | } 73 | } 74 | 75 | return $menuItems; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/controllers/RbacController.php: -------------------------------------------------------------------------------- 1 | _manager = \Yii::$app->authManager; 21 | 22 | } 23 | 24 | /** 25 | * @return string 26 | */ 27 | public function actionAssignments() 28 | { 29 | $allPermissions = Yii::$app->authManager->getPermissions(); 30 | $allRoles = Yii::$app->authManager->getRoles(); 31 | $userPermissions = []; 32 | $userRoles = []; 33 | 34 | foreach ($allPermissions AS $item) { 35 | if (Yii::$app->user->can($item->name)) { 36 | $userPermissions[] = [ 37 | 'description' => $item->description, 38 | 'name' => $item->name, 39 | ]; 40 | } 41 | } 42 | foreach ($allRoles AS $item) { 43 | if (Yii::$app->user->can($item->name)) { 44 | $userRoles[] = [ 45 | 'description' => $item->description, 46 | 'name' => $item->name, 47 | ]; 48 | } 49 | } 50 | 51 | return $this->render('show-auth', 52 | [ 53 | 'permissions' => new ArrayDataProvider([ 54 | 'allModels' => $userPermissions, 55 | 'pagination' => [ 56 | 'pageSize' => 100, 57 | ], 58 | ]), 59 | 'roles' => new ArrayDataProvider([ 60 | 'allModels' => $userRoles, 61 | 'pagination' => [ 62 | 'pageSize' => 100, 63 | ], 64 | ]), 65 | ]); 66 | } 67 | 68 | public function actionDiagram() 69 | { 70 | $this->renderDiagram(); 71 | 72 | $mmd = file_get_contents(\Yii::getAlias($this->_mmdFile)); 73 | return $this->render('index.twig', ['mmd' => $mmd]); 74 | } 75 | 76 | 77 | private function renderDiagram() 78 | { 79 | 80 | $mermaid = 'flowchart LR' . PHP_EOL . PHP_EOL; 81 | 82 | $assignmentsMmd = ''; 83 | $permissionMmd = ''; 84 | 85 | $rolesMmd = $this->renderRoles(); 86 | $permissionMmd .= $this->renderPermissions(); 87 | 88 | foreach ($this->_manager->getRoles() as $role) { 89 | $assignmentsMmd .= $this->renderAssignments($role); 90 | } 91 | $assignmentsMmd .= PHP_EOL . PHP_EOL; 92 | foreach ($this->_manager->getPermissions() as $permission) { 93 | $assignmentsMmd .= $this->renderAssignments($permission); 94 | } 95 | 96 | #$mermaid .= $this->renderAsSubgraph('roles',$rolesMmd) . PHP_EOL; 97 | $mermaid .= $rolesMmd . PHP_EOL; 98 | #$mermaid .= $permissionMmd . PHP_EOL; 99 | $mermaid .= $permissionMmd . PHP_EOL; 100 | $mermaid .= $assignmentsMmd . PHP_EOL; 101 | 102 | $filename = \Yii::getAlias($this->_mmdFile); 103 | FileHelper::createDirectory(dirname($filename)); 104 | file_put_contents($filename, $mermaid); 105 | 106 | } 107 | 108 | private function renderRoles() 109 | { 110 | $roles = $this->_manager->getRoles(); 111 | $rolesMmd = ''; 112 | $assignmentsMmd = ''; 113 | 114 | $_gs = []; 115 | 116 | foreach ($roles as $role) { 117 | #var_dump($this->module->rbacDiagramExcludeRoles);exit; 118 | if (in_array($role->name, $this->module->rbacDiagramExcludeRoles)) continue; 119 | 120 | $group = $role->ruleName ?? '__NONE__'; 121 | $group = '__NONE__'; 122 | 123 | if ($role->ruleName) { 124 | $_gs[$group][] = $this->renderItem($role) . PHP_EOL; 125 | $arrow = '-.->'; 126 | } else { 127 | $_gs[$group][] = $this->renderItem($role) . PHP_EOL; 128 | $arrow = '-->'; 129 | } 130 | 131 | #$assignmentsMmd .= $this->renderAssignments($role); 132 | 133 | } 134 | 135 | foreach ($_gs as $groupName => $g) { 136 | $groupMmd = implode("\n", $g); 137 | if ($groupName !== '__NONE__') { 138 | $rolesMmd .= $this->renderAsSubgraph($groupName, $groupMmd); 139 | } else { 140 | $rolesMmd .= $groupMmd; 141 | } 142 | #$rolesMmd .= $groupMmd; 143 | } 144 | 145 | 146 | return $rolesMmd . $assignmentsMmd; 147 | } 148 | 149 | private function renderAssignments($item) 150 | { 151 | if (in_array($item->name, $this->module->rbacDiagramExcludeRoles, true)) { 152 | return; 153 | } 154 | 155 | $assignmentsMmd = ''; 156 | foreach ($this->_manager->getChildren($item->name) as $child) { 157 | $arrow = ($item->ruleName) ? '-.->' : '-->'; 158 | $assignmentsMmd .= $this->renderItem($item, true) . $arrow . $this->renderItem($child, true) . PHP_EOL; 159 | } 160 | return $assignmentsMmd; 161 | } 162 | 163 | private function renderPermissions() 164 | { 165 | $permissionGroups = []; 166 | $permissionMmd = '' . PHP_EOL; 167 | foreach ($this->_manager->getPermissions() as $permission) { 168 | $group = explode('_', $permission->name)[0]; 169 | $group = explode('.', $group)[0]; 170 | $group = explode('-', $group)[0]; 171 | $permissionGroups[$group][] = $this->renderItem($permission) . PHP_EOL; 172 | } 173 | 174 | foreach ($permissionGroups as $group => $items) { 175 | $g = ''; 176 | foreach ($items as $line) { 177 | $g .= $line; 178 | } 179 | $permissionMmd .= $this->renderAsSubgraph("__$group", $g); 180 | } 181 | return $permissionMmd; 182 | } 183 | 184 | private function renderAsSubgraph($name, $diagram) 185 | { 186 | $subgraph = 'subgraph ' . $name . PHP_EOL; 187 | $subgraph .= $diagram . PHP_EOL; 188 | $subgraph .= 'end' . PHP_EOL; 189 | 190 | return $subgraph; 191 | } 192 | 193 | private function renderItem($item, $compact = false) 194 | { 195 | if ($compact) { 196 | $node = md5($item->name); 197 | } else { 198 | $symbols = ($item->type == 1) ? ["(",")"] : ["[","]"]; 199 | $node = md5($item->name) . $symbols[0]. '"' . $item->name . '

' . $item->description . '"'.$symbols[1]; 200 | 201 | $node .= ';' . PHP_EOL; 202 | $routePart = $item->type == 1 ? 'role' : 'permission'; 203 | $node .= 'click ' . md5($item->name) . ' "/user/' . $routePart . '/update?name=' . $item->name . '" "TT";'; 204 | } 205 | return $node; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/interfaces/ContextMenuItemsInterface.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | interface ContextMenuItemsInterface 19 | { 20 | /** 21 | * This methods should return array list of menu items 22 | * 23 | * Example: 24 | * 25 | * [ 26 | * [ 27 | * 'label' => 'Link label', 28 | * 'url' => ['/'] 29 | * ] 30 | * ] 31 | * 32 | * Classes which use this interface should also trigger the `registerMenuItems` preferred in the `init` method 33 | * 34 | * Example: 35 | * 36 | * public function init() 37 | * { 38 | * \Yii::$app->trigger('registerMenuItems', new Event(['sender' => $this])); 39 | * parent::init(); 40 | * } 41 | * 42 | * Heads up: You may want to add a if statement around the trigger if it should only be triggered on certain conditions. 43 | * 44 | * @return array 45 | */ 46 | public function getMenuItems(); 47 | } -------------------------------------------------------------------------------- /src/migrations/m160926_160610_settings.php: -------------------------------------------------------------------------------- 1 | settings instanceof \pheme\settings\components\Settings) { 10 | Yii::$app->settings->set('skin', 'black-light', 'backend.adminlte'); 11 | } 12 | 13 | } 14 | 15 | public function down() 16 | { 17 | if (Yii::$app->settings instanceof \pheme\settings\components\Settings) { 18 | Yii::$app->settings->delete('skin', 'backend.adminlte'); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/migrations/m181207_200300_settings.php: -------------------------------------------------------------------------------- 1 | settings instanceof \pheme\settings\components\Settings) { 10 | Yii::$app->settings->getOrSet('backendWidget', 'modal', 'frontend', 'string'); 11 | 12 | } 13 | 14 | } 15 | 16 | public function down() 17 | { 18 | if (Yii::$app->settings instanceof \pheme\settings\components\Settings) { 19 | Yii::$app->settings->delete('backendWidget', 'frontend'); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/views/config/_controller.php: -------------------------------------------------------------------------------- 1 | id) { 11 | $controller = Yii::$app->createController($model['name']); 12 | } else { 13 | $controller = Yii::$app->createController($model['module'].'/'.$model['name']); 14 | } 15 | 16 | ?> 17 | 18 | 22 |
23 |
24 | id ?> 25 |
26 |
27 |
28 | 29 |
30 | '; 33 | } 34 | ?> 35 |
36 |
37 |
38 | 39 |
40 | {moduleName}', [ 41 | 'moduleName' => $model['name'] 42 | ])?> 43 |
44 | 45 | -------------------------------------------------------------------------------- /src/views/config/_module.php: -------------------------------------------------------------------------------- 1 | 14 | 15 | beginBlock('routes') ?> 16 | 17 | Metadata::getModuleControllers($key), 21 | ] 22 | ); 23 | ?> 24 | 25 | $controllerDataProvider, 28 | 'layout' => "{items}\n{pager}", 29 | 'itemView' => '_controller', 30 | ] 31 | ) 32 | ?> 33 | 34 | endBlock() ?> 35 | 36 |
37 | $key.' '.(isset($model) && is_object($model) ? 'loaded' : ''), 39 | 'collapse' => false, 40 | 'collapse_remember' => false, 41 | 'type' => Box::TYPE_INFO 42 | ]) ?> 43 | blocks['routes'] ?> 44 | 45 |
46 | -------------------------------------------------------------------------------- /src/views/config/view.php: -------------------------------------------------------------------------------- 1 | params['breadcrumbs'][] = ['label' => Yii::t('backend-module', 'Configuration')]; 21 | $this->title = Yii::t('backend-module', 'Configuration'); 22 | ?> 23 | 24 |
25 |
26 | beginBlock('controllers') ?> 27 | 28 |
29 | render('_module', ['key' => null]) ?> 30 |
31 | 32 | $loadedModulesDataProvider, 35 | 'itemView' => '_module', 36 | 'layout' => '{summary}{pager}
{items}
' 37 | ] 38 | ) 39 | ?> 40 | endBlock('controllers') ?> 41 |
42 |
43 | 44 | 45 | beginBlock('params') ?> 46 | $element): ?> 47 |
48 |
49 | 50 |
51 |
52 |
 53 | 
 54 | 
55 |
56 |
57 | 58 | endBlock('params') ?> 59 | 60 | 61 | beginBlock('components') ?> 62 | $element): ?> 63 |
64 |
65 | 66 |
67 |
68 |
 69 | 
 70 | 
71 |
72 |
73 | 74 | endBlock('components') ?> 75 | 76 | beginBlock('modules') ?> 77 | $element): ?> 78 | $name, 80 | 'collapse' => true, 81 | 'collapse_remember' => false, 82 | ]) ?> 83 | 84 |
85 |
86 | 87 |
88 |
89 |
 90 | 
 91 | 
92 |
93 |
94 | 95 | 96 | 97 | endBlock('modules') ?> 98 | 99 | 100 | beginBlock('env') ?> 101 |
102 | 
103 | 
104 | endBlock('env') ?> 105 | 106 | 107 | 108 | 134 | -------------------------------------------------------------------------------- /src/views/default/index.php: -------------------------------------------------------------------------------- 1 | params['breadcrumbs'][] = ['label' => Yii::t('backend-module', 'Dashboard')]; 10 | $this->title = Yii::t('backend-module', 'Dashboard'); 11 | ?> 12 | 13 |

14 | 15 |
16 | 17 | user->can('Admin')): ?> 18 | 19 |
20 | 21 |
22 |
23 |

24 | 25 |

26 | 27 |

28 | 29 | 30 |

31 |
32 |
33 | 34 |
35 | 36 | 37 | 38 |
39 |
40 | 41 | 42 |
43 | 44 |
45 |
46 |

47 | user->identityClass, 'find') 49 | && method_exists(\Yii::$app->user->identityClass::find(), 'count')) { 50 | echo \Yii::$app->user->identityClass::find()->count(); 51 | } else { 52 | echo '-'; 53 | } 54 | ?> 55 |

56 | 57 |

58 | 59 |

60 |
61 |
62 | 63 |
64 | 65 | 66 | 67 |
68 |
69 | 70 | 71 |
72 | 73 |
74 |
75 |

76 | getModules()) ?> 77 |

78 | 79 |

80 | 81 |

82 |
83 |
84 | 85 |
86 | 87 | 88 | 89 |
90 | 91 |
92 | 93 | 94 |
95 | 96 |
97 |
98 |

99 | 100 |

101 | 102 |

103 | 104 |

105 |
106 |
107 | 108 |
109 | 110 | 111 | 112 |
113 |
114 | 115 | 116 |
117 | 118 |
119 |
120 |

121 | 122 |

123 |

124 | 125 |

126 |
127 |
128 | 129 |
130 | 131 | 132 | 133 |
134 |
135 | 136 | 137 |
138 | 139 |
140 |
141 |

142 | 143 |

144 | 145 |

146 | 147 |

148 |
149 |
150 | 151 |
152 | 153 | 154 | 155 |
156 |
157 | 158 | 159 |
160 | 161 |
162 |
163 |

164 | 165 |

166 | 167 |

168 | 169 |

170 |
171 |
172 | 173 |
174 | 175 | 176 | 177 |
178 |
179 | 180 | 181 | 182 |
183 | 184 |
185 | context->renderDashboardMenu(['items' => $items]); 187 | ?> 188 |
189 | 190 | 191 | -------------------------------------------------------------------------------- /src/views/layouts/_control-sidebar.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | getModule('backend'); 13 | foreach (\dmstr\helpers\Metadata::getModules() as $name => $module) { 14 | if (in_array($name, $backendModule->modulesDashboardBlacklist,true)) { 15 | continue; 16 | } 17 | 18 | $role = $name; 19 | 20 | $defaultItem = [ 21 | 'icon' => 'fa fa-cube', 22 | 'label' => $name, 23 | 'url' => ['/' . $name], 24 | 'visible' => Yii::$app->user->can($role), 25 | 'items' => [], 26 | ]; 27 | 28 | $developerMenuItems[] = $defaultItem; 29 | } 30 | 31 | if (Yii::$app->hasModule('pages')) { 32 | $rootItems = \dmstr\modules\pages\models\Tree::getMenuItems('root'); 33 | } else { 34 | $rootItems = []; 35 | } 36 | 37 | if (Yii::$app->urlManager->hasProperty('languages')) { 38 | $languages = Yii::$app->urlManager->languages; 39 | } else { 40 | $languages = explode(',', getenv('APP_LANGUAGES')); 41 | } 42 | 43 | ?> 44 | 45 | 160 | -------------------------------------------------------------------------------- /src/views/layouts/_sidebar.php: -------------------------------------------------------------------------------- 1 | hasModule('pages')) { 11 | $menuItems = \dmstr\modules\pages\models\Tree::getMenuItems('backend', true); 12 | } else { 13 | $menuItems = []; 14 | } 15 | 16 | ?> 17 | 18 | 19 | ['class' => 'sidebar-menu tree', 'data-widget' => 'tree'], 23 | 'encodeLabels' => false, 24 | 'items' => ArrayHelper::merge( 25 | ['items' => ['label' => Yii::t('backend-module', 'Backend navigation') , 'options' => ['class' => 'header']]], 26 | $menuItems 27 | ), 28 | ] 29 | ); 30 | ?> 31 | -------------------------------------------------------------------------------- /src/views/layouts/box.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | beginContent('@vendor/dmstr/yii2-backend-module/src/views/layouts/main.php'); ?> 15 | 16 |
17 |
18 | 19 |
20 | 21 |
22 | 23 | endContent(); ?> -------------------------------------------------------------------------------- /src/views/layouts/login.php: -------------------------------------------------------------------------------- 1 | 10 | 11 | beginPage() ?> 12 | 13 | 14 | 15 | 16 | 17 | <?= Html::encode($this->title) . ' - ' . Yii::$app->name ?> 18 | 19 | 20 | head() ?> 21 | 22 | 23 | 24 | beginBody() ?> 25 |
26 | 29 | 30 |
31 |
32 |
33 | false]) ?> 34 |
35 |
36 | 37 | 38 | 39 |
40 | 41 |
42 | 43 | 44 | endBody() ?> 45 | 46 | 47 | endPage() ?> 48 | 49 | -------------------------------------------------------------------------------- /src/views/layouts/main.php: -------------------------------------------------------------------------------- 1 | has('settings')) { 28 | $adminLteSkin = Yii::$app->settings->get('skin', 'backend.adminlte') ?: 'black-light'; 29 | $navBarIcon = Yii::$app->settings->get('navBarIcon', 'backend.adminlte') ?: FA::_HEART; 30 | $sidebarClass = Yii::$app->settings->get('sidebar', 'backend.adminlte', 'sidebar-mini '); 31 | $growlLocation = Yii::$app->settings->getOrSet('growl.location', 'br', 'backend', 'string'); 32 | } 33 | 34 | // prepare assets 35 | BackendAsset::register($this); 36 | BackendJsAsset::register($this); 37 | $js = <<registerJs($js); 46 | 47 | ?> 48 | 49 | beginPage() ?> 50 | 51 | 52 | 53 | 54 | 55 | <?= Html::encode($this->title) . ' - ' . Yii::$app->name ?> 56 | 57 | 58 | head() ?> 59 | 60 | 61 | 64 | beginBody() ?> 65 | 66 | 67 | hasModule('prototype')) { 71 | try { 72 | $this->beginBlock('twig-main-top'); 73 | echo TwigWidget::widget([ 74 | 'position' => 'main-top', 75 | 'registerMenuItems' => true, 76 | 'queryParam' => false, 77 | 'renderEmpty' => false, 78 | ]); 79 | $this->endBlock('twig-main-top'); 80 | } catch (InvalidCallException $e) { 81 | $this->blocks['twig-main-top'] = 'X'; 82 | Yii::$app->session->addFlash('error', $e->getMessage()); 83 | } 84 | 85 | try { 86 | $this->beginBlock('twig-main-bottom'); 87 | echo TwigWidget::widget([ 88 | 'position' => 'main-bottom', 89 | 'registerMenuItems' => true, 90 | 'queryParam' => false, 91 | 'renderEmpty' => false, 92 | ]); 93 | $this->endBlock('twig-main-bottom'); 94 | } catch (InvalidCallException $e) { 95 | $this->blocks['twig-main-bottom'] = 'X'; 96 | Yii::$app->session->addFlash('error', $e->getMessage()); 97 | } 98 | 99 | try { 100 | $this->beginBlock('extra-content'); 101 | echo TwigWidget::widget([ 102 | 'key' => 'backend.extra.content', 103 | 'position' => 'bottom', 104 | 'registerMenuItems' => true, 105 | 'queryParam' => false, 106 | 'renderEmpty' => false, 107 | ]); 108 | $this->endBlock('extra-content'); 109 | } catch (InvalidCallException $e) { 110 | $this->blocks['extra-content'] = ''; 111 | Yii::$app->session->addFlash('error', $e->getMessage()); 112 | } 113 | } else { 114 | $this->blocks['twig-main-top'] = ''; 115 | $this->blocks['twig-main-bottom'] = ''; 116 | $this->blocks['extra-content'] = ''; 117 | } 118 | ?> 119 | 120 |
121 | 122 |
123 | 124 | 128 | 129 | 205 |
206 | 207 | 214 | 215 | 216 |
217 | 218 |
219 |

220 | context->module->id)) ?> 221 | 222 |

223 | ['label' => Yii::t('backend-module', 'Backend'), 'url' => ['/backend']], 227 | 'links' => $this->params['breadcrumbs'] ?? [], 228 | ] 229 | ) ?> 230 |
231 | 232 | 233 | 234 |
235 | Growl::class, 237 | 'options' => [ 238 | 'dismissQueue' => true, 239 | 'location' => $growlLocation, 240 | 'timeout' => 4000, 241 | ], 242 | ]) ?> 243 | blocks['twig-main-top'] ?> 244 | 245 | blocks['twig-main-bottom'] ?> 246 |
247 | 248 |
249 | 250 | blocks['extra-content'] ?> 251 | 252 | 253 |
{appName}-{projectVersion} build with {phdLink} {appVersion}', 255 | [ 256 | 'appName' => getenv('APP_NAME'), 257 | 'appVersion' => APP_VERSION, 258 | 'projectVersion' => PROJECT_VERSION, 259 | 'phdLink' => 'phd' 260 | ]) ?>
261 | 262 | render('_control-sidebar') ?> 263 |
264 | 265 | 266 | endBody() ?> 267 | 268 | 269 | endPage() ?> 270 | -------------------------------------------------------------------------------- /src/views/rbac/index.twig: -------------------------------------------------------------------------------- 1 | {# register_asset_bundle('dmstr/web/MermaidAsset') #} 2 | 3 | 4 | 5 |
6 |
7 | Roles and Permissions 8 |
9 |
10 | {{ mmd }} 11 |
12 |
13 | 14 | {# 15 |

Debug

16 |

17 |  {{ mmd }}
18 | 
19 | #} 20 | -------------------------------------------------------------------------------- /src/views/rbac/show-auth.php: -------------------------------------------------------------------------------- 1 | title = Yii::t('backend-module', 'Authorizations'); 7 | 8 | ?> 9 |
10 |
11 | 'Roles']); 13 | echo GridView::widget([ 14 | 'dataProvider' => $roles, 15 | 16 | ]); 17 | Box::end(); 18 | ?> 19 | 20 |
21 |
22 | 'Permissions']); 24 | echo GridView::widget([ 25 | 'dataProvider' => $permissions 26 | ]); 27 | Box::end(); 28 | ?> 29 |
30 |
31 | 32 | -------------------------------------------------------------------------------- /src/widgets/Modal.php: -------------------------------------------------------------------------------- 1 | registerAssets(); 18 | parent::init(); 19 | } 20 | 21 | /** 22 | * @return string 23 | */ 24 | public function run() 25 | { 26 | 27 | $data['name'] = Yii::$app->name; 28 | $data['appVersion'] = defined('APP_VERSION') ? APP_VERSION : '-'; 29 | $data['projectVersion'] = defined('PROJECT_VERSION') ? PROJECT_VERSION : '-'; 30 | $data['virtualHost'] = getenv('VIRTUAL_HOST'); 31 | $data['hostname'] = getenv('HOSTNAME') ?: 'local'; 32 | 33 | return $this->render('modal.twig', $data); 34 | } 35 | 36 | public function registerAssets() 37 | { 38 | ModalAsset::register($this->view); 39 | } 40 | } -------------------------------------------------------------------------------- /src/widgets/Toolbar.php: -------------------------------------------------------------------------------- 1 | registerAssets(); 31 | parent::init(); 32 | } 33 | 34 | /** 35 | * @return string 36 | */ 37 | public function run() 38 | { 39 | return $this->render('toolbar.twig', ['useIframe' => $this->useIframe]); 40 | } 41 | 42 | public function registerAssets() 43 | { 44 | $file = \Yii::$app->assetManager->publish(dirname(__DIR__) . '/assets/toolbar/js/check-frame.js'); 45 | $this->view->registerJsFile($file[1], ['position' => View::POS_BEGIN]); 46 | ToolbarAsset::register($this->view); 47 | } 48 | } -------------------------------------------------------------------------------- /src/widgets/views/modal.twig: -------------------------------------------------------------------------------- 1 | {{ use ('dmstr/cookiebutton') }} 2 | {{ use ('yii/widgets') }} 3 | {{ use ('rmrevin/yii/fontawesome/FA') }} 4 | {{ use ('dmstr/lajax/translatemanager/widgets/ToggleTranslateLink') }} 5 | 6 | 7 | {% if not app.user.isGuest %} 8 | {% set backendItems = Tree.getMenuItems('backend', true, {'target': app.params['backend.iframe.name']} ) ?: [] %} 9 | 10 |
11 | 12 |
13 | 14 | {% if not app.settings.get('backendButtonsInModal', 'frontend') %} 15 | 16 | {{ Html.a( 17 | FA.icon(FA._ARROW_CIRCLE_LEFT), 18 | Url.to(['/backend/default/index']), 19 | { 20 | 'target': app.params['backend.iframe.name'], 21 | 'class': 'btn btn-xs btn-default btn-primary', 22 | 'title': 'Application backend' 23 | } 24 | ) | raw }} 25 | 26 | {% if (app.user.can('frontend.toggle-view-mode')) %} 27 | {{ cookie_button_widget( 28 | { 29 | 'id': 'app-frontend-view-mode-button', 30 | 'label': FA.icon(FA._EYE_SLASH), 31 | 'encodeLabel': false, 32 | 'cookieName': 'app-frontend-view-mode', 33 | 'cookieValue': 'on', 34 | 'cookieOptions': { 35 | 'path': '/', 36 | 'http': true, 37 | 'expires': 7 38 | }, 39 | 'options': { 40 | 'class': 'btn btn-xs btn-default', 41 | 'title': 'Toggle frontend view-mode (v)' 42 | } 43 | } 44 | ) }} 45 | {% endif %} 46 | 47 | {% endif %} 48 | 49 | {{ Html.a( 50 | FA.icon(FA._WRENCH), 51 | '#', 52 | { 53 | 'data-toggle': 'modal', 54 | 'data-target': '#phd-info-modal', 55 | 'class': 'btn btn-xs btn-default', 56 | 'title': 'Open menu (m)' 57 | } 58 | ) | raw }} 59 | 60 | {% if app.components['translatemanager'] is defined %} 61 | {{ ToggleTranslateLink_widget({ 62 | 'options': { 63 | 'class': 'btn btn-xs btn-default', 64 | 'title': 'Toggle translate' 65 | } 66 | }) }} 67 | {% endif %} 68 |
69 | 70 |
71 | 72 | 181 | 182 | {% endif %} 183 | -------------------------------------------------------------------------------- /src/widgets/views/toolbar.twig: -------------------------------------------------------------------------------- 1 | {{ use ('yii/widgets') }} 2 | {{ use ('pheme/settings/models/Setting') }} 3 | {{ use ('yii/helpers/Url') }} 4 | {{ use ('dmstr/modules/prototype/widgets/TwigWidget') }} 5 | {{ use ('dmstr/cookiebutton/CookieButton') }} 6 | {{ use ('rmrevin/yii/fontawesome/FA') }} 7 | {{ use ('dmstr/lajax/translatemanager/widgets/ToggleTranslateLink') }} 8 | 9 | {% if not app.user.isGuest %} 10 | {% set backendItems = Tree.getMenuItems('backend', true, {'target': app.params['backend.iframe.name']} ) %} 11 | 12 | 120 | {% endif %} -------------------------------------------------------------------------------- /tests/.env: -------------------------------------------------------------------------------- 1 | COMPOSE_PROJECT_NAME=test-yii2-backend-module 2 | -------------------------------------------------------------------------------- /tests/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM dmstr/yii2-app:0.6.2 2 | 3 | # Install npm and lessc 4 | RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - \ 5 | && apt-get install -y npm \ 6 | && npm install -g less 7 | ENV PATH /app:/repo/tests/project/vendor/bin:/usr/lib/node_modules/less/bin:$PATH 8 | 9 | ENV COMPOSER=/repo/tests/project/composer.json 10 | 11 | # Clean vendor from base image 12 | RUN rm -rf /app/vendor 13 | RUN ln -s /repo/tests/project/vendor /app/vendor -------------------------------------------------------------------------------- /tests/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | 4 | php: 5 | build: 6 | dockerfile: Dockerfile 7 | context: . 8 | working_dir: /repo/tests 9 | ports: 10 | - 80 11 | environment: 12 | - YII_ENV=test 13 | - YII_DEBUG=1 14 | - APP_CONFIG_FILE=/repo/tests/project/config/test.php 15 | volumes: 16 | # mount this repository read-only 17 | - ./..:/repo:ro 18 | # mount tests 19 | - ./:/repo/tests 20 | # composer cache 21 | - ~/.composer-docker/cache:/root/.composer/cache:delegated 22 | networks: 23 | default: 24 | aliases: 25 | - web -------------------------------------------------------------------------------- /tests/project/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "repositories": [ 3 | { 4 | "type": "vcs", 5 | "url": "file:///repo" 6 | }, 7 | { 8 | "type": "composer", 9 | "url": "https://asset-packagist.org" 10 | } 11 | ], 12 | "require": { 13 | "wikimedia/composer-merge-plugin": "~1.4" 14 | }, 15 | "extra": { 16 | "merge-plugin": { 17 | "require": [ 18 | "/app/composer.json", 19 | "/repo/composer.json" 20 | ] 21 | } 22 | }, 23 | "config": { 24 | "fxp-asset": { 25 | "installer-paths": { 26 | "npm-asset-library": "vendor/npm", 27 | "bower-asset-library": "vendor/bower" 28 | }, 29 | "vcs-driver-options": { 30 | "github-no-api": true 31 | }, 32 | "git-skip-update": "2 days", 33 | "pattern-skip-version": "(-build|-patch)", 34 | "optimize-with-installed-packages": false 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /tests/project/config/test.php: -------------------------------------------------------------------------------- 1 | [ 8 | 'backend' => '/repo/src', 9 | 'vendor/dmstr/yii2-backend-module' => '/repo' 10 | ], 11 | 'components' => [ 12 | 'assetManager' => [ 13 | 'converter' => [ 14 | 'class' => 'yii\web\AssetConverter', 15 | 'commands' => [ 16 | 'less' => ['css', 'lessc {from} {to} --no-color --modify-var="bowerAssetPath=/app/vendor/bower-asset"'], 17 | ], 18 | ], 19 | ], 20 | 'user' => [ 21 | 'class' => 'dmstr\web\User', 22 | 'identityClass' => 'app\components\AdminIdentity', 23 | #'rootUsers' => ['admin'] 24 | ], 25 | ], 26 | 'modules' => [ 27 | 'backend' => [ 28 | 'class' => 'dmstr\modules\backend\Module', 29 | 'layout' => '@backend/views/layouts/main', 30 | ] 31 | ], 32 | ]; --------------------------------------------------------------------------------