1
|
|
|
<?php
|
|
|
|
|
2
|
|
|
/**
|
3
|
|
|
* @author Sergey Glagolev <[email protected]>
|
4
|
|
|
* @link https://github.com/shogodev/argilla/
|
5
|
|
|
* @copyright Copyright © 2003-2014 Shogo
|
6
|
|
|
* @license http://argilla.ru/LICENSE
|
7
|
|
|
* @package backend.components
|
8
|
|
|
*
|
9
|
|
|
* Класс для работы с модулями бэкенда.
|
10
|
|
|
* Методы используются для построения меню групп, для построения меню отдельных групп
|
11
|
|
|
*/
|
12
|
|
|
Yii::import('backend.modules.brac.components.AccessHelper');
|
13
|
|
|
Yii::import('backend.modules.brac.models');
|
14
|
|
|
|
15
|
|
|
class BMenu extends CComponent
|
16
|
|
|
{
|
17
|
|
|
public $groupNames = array(
|
18
|
|
|
'content' => 'Контент',
|
19
|
|
|
'seo' => 'SEO',
|
20
|
|
|
'settings' => 'Настройки',
|
21
|
|
|
'help' => 'Помощь',
|
22
|
|
|
);
|
23
|
|
|
|
24
|
|
|
private $groups = array();
|
25
|
|
|
|
26
|
|
|
private $modules;
|
27
|
|
|
|
28
|
|
|
private $submodules = array();
|
29
|
|
|
|
30
|
|
|
/**
|
31
|
|
|
* @var BController
|
32
|
|
|
*/
|
33
|
|
|
private $controller;
|
34
|
|
|
|
35
|
|
|
/**
|
36
|
|
|
* @var BModule
|
37
|
|
|
*/
|
38
|
|
|
private $currentModule;
|
39
|
|
|
|
40
|
|
|
/**
|
41
|
|
|
* @var string
|
42
|
|
|
*/
|
43
|
|
|
private $currentGroup;
|
44
|
|
|
|
45
|
|
|
public function init()
|
46
|
|
|
{
|
47
|
|
|
$this->controller = Yii::app()->controller;
|
48
|
|
|
$this->currentModule = $this->controller->module;
|
49
|
|
|
|
50
|
|
|
if( $this->currentModule && !empty($this->currentModule->group) )
|
51
|
|
|
$this->currentGroup = $this->currentModule->group;
|
52
|
|
|
|
53
|
|
|
$this->buildStructure(Yii::app()->getModules());
|
54
|
|
|
|
55
|
|
|
$this->sort();
|
56
|
|
|
}
|
57
|
|
|
|
58
|
|
|
public function getGroups()
|
59
|
|
|
{
|
60
|
|
|
return $this->groups;
|
61
|
|
|
}
|
62
|
|
|
|
63
|
|
|
public function getModules($hideOneModule = true)
|
64
|
|
|
{
|
65
|
|
|
$modules = Arr::get($this->modules, $this->currentGroup, array());
|
66
|
|
|
return count($modules) < 2 && $hideOneModule ? array() : $modules;
|
67
|
|
|
}
|
68
|
|
|
|
69
|
|
|
/**
|
70
|
|
|
* @param bool $hideOneController - не строим подменю, если контроллер единственный
|
71
|
|
|
*
|
72
|
|
|
* @return array
|
73
|
|
|
*/
|
74
|
|
|
public function getSubmodules($hideOneController = true)
|
75
|
|
|
{
|
76
|
|
|
return count($this->submodules) < 2 && $hideOneController ? array() : $this->submodules;
|
77
|
|
|
}
|
78
|
|
|
|
79
|
|
|
public function getDefaultRout($default = '')
|
80
|
|
|
{
|
81
|
|
|
if( $groups = $this->getGroups() )
|
82
|
|
|
{
|
83
|
|
|
return Arr::reset($groups)['route'];
|
84
|
|
|
}
|
85
|
|
|
|
86
|
|
|
return $default;
|
87
|
|
|
}
|
88
|
|
|
|
89
|
|
|
/**
|
90
|
|
|
* @param array $filteredModules
|
91
|
|
|
* @param $parent
|
92
|
|
|
*
|
93
|
|
|
* @return BModule[]
|
94
|
|
|
* @throws CException
|
95
|
|
|
* @throws ClassNotFoundException
|
96
|
|
|
*/
|
97
|
|
|
private function getAllowedModules(array $filteredModules, $parent)
|
98
|
|
|
{
|
99
|
|
|
$modules = array();
|
100
|
|
|
|
101
|
|
|
foreach($filteredModules as $moduleId => $moduleConfig)
|
102
|
|
|
{
|
103
|
|
|
if( empty($moduleConfig['autoloaded']) || $moduleConfig['autoloaded'] == false )
|
104
|
|
|
continue;
|
105
|
|
|
|
106
|
|
|
Yii::import($moduleConfig['class']);
|
107
|
|
|
$moduleClassName = ucfirst($moduleId).'Module';
|
108
|
|
|
|
109
|
|
|
if( !class_exists($moduleClassName) )
|
110
|
|
|
{
|
111
|
|
|
throw new ClassNotFoundException($moduleClassName, $moduleConfig['class']);
|
112
|
|
|
}
|
113
|
|
|
|
114
|
|
|
$modules[$moduleId] = new $moduleClassName($moduleId, $parent);
|
115
|
|
|
|
116
|
|
|
if( !empty($moduleConfig['modules']) )
|
117
|
|
|
{
|
118
|
|
|
$modules[$moduleId]->setModules($moduleConfig['modules']);
|
119
|
|
|
}
|
120
|
|
|
}
|
121
|
|
|
|
122
|
|
|
return $modules;
|
123
|
|
|
}
|
124
|
|
|
|
125
|
|
|
/**
|
126
|
|
|
* @param array $modules
|
127
|
|
|
* @param BModule $parent|null
|
|
|
|
|
128
|
|
|
* @throws CException
|
129
|
|
|
*/
|
130
|
|
|
private function buildStructure(array $modules, $parent = null)
|
131
|
|
|
{
|
132
|
|
|
$filteredModules = AccessHelper::filterModulesByAccess($modules);
|
133
|
|
|
|
134
|
|
|
$allowedModules = $this->getAllowedModules($filteredModules, $parent);
|
135
|
|
|
|
136
|
|
|
foreach($allowedModules as $moduleId => $module)
|
137
|
|
|
{
|
138
|
|
|
if( !$this->allowedModule($module) )
|
139
|
|
|
continue;
|
140
|
|
|
|
141
|
|
|
if( $this->isModuleActive($module) )
|
142
|
|
|
{
|
143
|
|
|
$this->createSubmodulesMenu($module);
|
144
|
|
|
}
|
145
|
|
|
|
146
|
|
|
if( $this->needCreateModulesMenu($module) )
|
147
|
|
|
{
|
148
|
|
|
if( $this->createGroupsMenu($module) )
|
149
|
|
|
{
|
150
|
|
|
if( !$this->createFakeModulesMenu($module) )
|
|
|
|
|
151
|
|
|
$this->createModulesMenu($module);
|
152
|
|
|
}
|
153
|
|
|
}
|
154
|
|
|
|
155
|
|
|
if( $module->getModules() )
|
156
|
|
|
{
|
157
|
|
|
$this->buildStructure($module->getModules(), $module);
|
158
|
|
|
}
|
159
|
|
|
}
|
160
|
|
|
}
|
161
|
|
|
|
162
|
|
|
/**
|
163
|
|
|
* @param BModule $module
|
164
|
|
|
*
|
165
|
|
|
* @return bool
|
166
|
|
|
*/
|
167
|
|
|
private function isModuleActive(BModule $module)
|
168
|
|
|
{
|
169
|
|
|
if( !$this->currentModule )
|
170
|
|
|
return false;
|
171
|
|
|
|
172
|
|
|
if( $this->currentModule->getName() == $module->getName() )
|
|
|
|
|
173
|
|
|
return true;
|
174
|
|
|
|
175
|
|
|
if( $currentModuleParents = $this->currentModule->getParents() )
|
176
|
|
|
{
|
177
|
|
|
if( isset($currentModuleParents[$module->getName()]) )
|
|
|
|
|
178
|
|
|
return true;
|
179
|
|
|
}
|
180
|
|
|
|
181
|
|
|
if( $parents = $module->getParents() )
|
182
|
|
|
{
|
183
|
|
|
if( isset($parents[$this->currentModule->getName()]) )
|
|
|
|
|
184
|
|
|
return true;
|
185
|
|
|
}
|
186
|
|
|
|
187
|
|
|
if( array_intersect(array_keys($currentModuleParents), array_keys($parents)))
|
188
|
|
|
return true;
|
189
|
|
|
|
190
|
|
|
return false;
|
191
|
|
|
}
|
192
|
|
|
|
193
|
|
|
/**
|
194
|
|
|
* @param BModule $module
|
195
|
|
|
*
|
196
|
|
|
* @return bool
|
197
|
|
|
*/
|
198
|
|
|
private function needCreateModulesMenu(BModule $module)
|
199
|
|
|
{
|
200
|
|
|
return !$module->getParentModule();
|
201
|
|
|
}
|
202
|
|
|
|
203
|
|
|
/**
|
204
|
|
|
* @param BModule $module
|
205
|
|
|
*
|
206
|
|
|
* @return bool
|
207
|
|
|
*/
|
208
|
|
|
private function createGroupsMenu($module)
|
209
|
|
|
{
|
210
|
|
|
if( !isset($this->groups[$module->group]) )
|
211
|
|
|
{
|
212
|
|
|
if( !$controllerClass = $this->getAllowedControllerClass($module) )
|
213
|
|
|
return false;
|
214
|
|
|
|
215
|
|
|
$controllerId = $module->getControllerId($controllerClass);
|
216
|
|
|
|
217
|
|
|
$this->groups[$module->group] = array(
|
218
|
|
|
'label' => Arr::get($this->groupNames, $module->group, $module->group),
|
219
|
|
|
'url' => $module->createUrl($controllerId),
|
220
|
|
|
'route' => implode('/', array($module->getName(), $controllerId)),
|
|
|
|
|
221
|
|
|
'active' => $this->currentGroup == $module->group,
|
222
|
|
|
'itemOptions' => array('class' => $module->group)
|
223
|
|
|
);
|
224
|
|
|
}
|
225
|
|
|
|
226
|
|
|
return true;
|
227
|
|
|
}
|
228
|
|
|
|
229
|
|
|
/**
|
230
|
|
|
* @param BModule $module
|
231
|
|
|
*
|
232
|
|
|
* @return bool
|
233
|
|
|
* @throws CHttpException
|
234
|
|
|
*/
|
235
|
|
|
private function createModulesMenu($module)
|
236
|
|
|
{
|
237
|
|
|
if( !$controllerClass = $this->getAllowedControllerClass($module) )
|
238
|
|
|
return false;
|
239
|
|
|
|
240
|
|
|
$controllerId = $module->getControllerId($controllerClass);
|
241
|
|
|
|
242
|
|
|
$this->modules[$module->group][$module->getName()] = array(
|
|
|
|
|
243
|
|
|
'label' => $module->name,
|
244
|
|
|
'url' => $module->createUrl($controllerId),
|
245
|
|
|
'active' => $this->isModuleActive($module),
|
246
|
|
|
'itemOptions' => array('class' => $module->id),
|
247
|
|
|
'position' => $module->position,
|
248
|
|
|
'module' => $module
|
249
|
|
|
);
|
250
|
|
|
}
|
251
|
|
|
|
252
|
|
|
/**
|
253
|
|
|
* Добавляет в меню виртуальные контроллеры модуля
|
254
|
|
|
*
|
255
|
|
|
* @param BModule $module
|
256
|
|
|
*
|
257
|
|
|
* @return bool
|
258
|
|
|
* @throws CHttpException
|
259
|
|
|
*/
|
260
|
|
|
private function createFakeModulesMenu($module)
|
261
|
|
|
{
|
262
|
|
|
if( $fakeMenu = $module->getMenuControllers() )
|
263
|
|
|
{
|
264
|
|
|
foreach($fakeMenu as $key => $menuItem)
|
265
|
|
|
{
|
266
|
|
|
$subMenu = Arr::cut($menuItem, 'menu', array());
|
|
|
|
|
267
|
|
|
|
268
|
|
|
foreach($subMenu as $controllerName)
|
|
|
|
|
269
|
|
|
{
|
270
|
|
|
$controllerClass = $controllerName.'Controller';
|
271
|
|
|
|
272
|
|
|
if( AccessHelper::checkAccessByNameClasses(get_class($module), $controllerClass) )
|
273
|
|
|
{
|
274
|
|
|
$controllerId = $module->getControllerId($controllerClass);
|
275
|
|
|
|
276
|
|
|
if( !isset($this->modules[$module->group][$key]) )
|
277
|
|
|
{
|
278
|
|
|
$this->modules[$module->group][$key] = $menuItem;
|
279
|
|
|
$this->modules[$module->group][$key]['url'] = $module->createUrl($controllerId);
|
280
|
|
|
}
|
281
|
|
|
|
282
|
|
|
$this->modules[$module->group][$key]['menu'][$controllerId] = $controllerName;
|
283
|
|
|
}
|
284
|
|
|
}
|
285
|
|
|
}
|
286
|
|
|
|
287
|
|
|
if( isset($this->controller->moduleMenu) )
|
288
|
|
|
$this->modules[$module->group][$this->controller->moduleMenu]['active'] = true;
|
289
|
|
|
|
290
|
|
|
return true;
|
291
|
|
|
}
|
292
|
|
|
}
|
293
|
|
|
|
294
|
|
|
/**
|
295
|
|
|
* @param BModule $module
|
296
|
|
|
*
|
297
|
|
|
* @return array
|
298
|
|
|
*/
|
299
|
|
|
private function createSubmodulesMenu($module)
|
300
|
|
|
{
|
301
|
|
|
foreach($module->controllerMap as $id => $controllerClass)
|
302
|
|
|
{
|
303
|
|
|
if( !AccessHelper::checkAccessByNameClasses(get_class($module), $controllerClass) )
|
304
|
|
|
continue;
|
305
|
|
|
|
306
|
|
|
/**
|
307
|
|
|
* @var BController $controller
|
308
|
|
|
*/
|
309
|
|
|
$controller = new $controllerClass($id, null);
|
310
|
|
|
|
311
|
|
|
if( !$controller->showInMenu )
|
312
|
|
|
continue;
|
313
|
|
|
|
314
|
|
|
// Убираем ненужные виртуальные контроллеры, которые уже отобразились в меню
|
315
|
|
|
if( $fakeControllers = $module->getMenuControllers() )
|
316
|
|
|
{
|
317
|
|
|
if( !(isset($controller->moduleMenu, $this->controller->moduleMenu) && in_array(BApplication::CLASS_PREFIX.ucfirst($controller->id), $fakeControllers[$this->controller->moduleMenu]['menu'])) )
|
318
|
|
|
continue;
|
319
|
|
|
}
|
320
|
|
|
|
321
|
|
|
if( !isset($controller->enabled) || $controller->enabled === true )
|
322
|
|
|
{
|
323
|
|
|
$this->submodules[$id] = array(
|
324
|
|
|
'label' => $controller->name,
|
325
|
|
|
'url' => $module->createUrl($controller->id),
|
326
|
|
|
'active' => $this->controller->id === $id || ucfirst($this->controller->id) === BApplication::CLASS_PREFIX.ucfirst($id),
|
327
|
|
|
'position' => $controller->position,
|
328
|
|
|
'itemOptions' => array('class' => $id),
|
329
|
|
|
);
|
330
|
|
|
}
|
331
|
|
|
}
|
332
|
|
|
}
|
333
|
|
|
|
334
|
|
|
private function getAllowedControllerClass(BModule $module)
|
335
|
|
|
{
|
336
|
|
|
$defaultController = $module->defaultController.'Controller';
|
337
|
|
|
|
338
|
|
|
if( !class_exists($defaultController) )
|
339
|
|
|
throw new CHttpException(500, 'Не удалось найти defaultController '.$defaultController);
|
340
|
|
|
|
341
|
|
|
if( AccessHelper::checkAccessByNameClasses(get_class($module), $defaultController) )
|
342
|
|
|
return $defaultController;
|
343
|
|
|
|
344
|
|
|
foreach($module->controllerMap as $controller)
|
345
|
|
|
{
|
346
|
|
|
if( AccessHelper::checkAccessByNameClasses(get_class($module), $controller) )
|
347
|
|
|
return $controller;
|
348
|
|
|
}
|
349
|
|
|
|
350
|
|
|
return null;
|
351
|
|
|
}
|
352
|
|
|
|
353
|
|
|
private function sort()
|
354
|
|
|
{
|
355
|
|
|
$this->sortGroups();
|
|
|
|
|
356
|
|
|
$this->sortModules();
|
357
|
|
|
$this->sortSubmodiles();
|
358
|
|
|
}
|
359
|
|
|
|
360
|
|
|
private function sortGroups()
|
361
|
|
|
{
|
362
|
|
|
}
|
363
|
|
|
|
364
|
|
|
private function sortModules()
|
365
|
|
|
{
|
366
|
|
|
if( empty($this->modules) )
|
367
|
|
|
return;
|
368
|
|
|
|
369
|
|
|
foreach($this->modules as $key => $data)
|
370
|
|
|
{
|
371
|
|
|
uasort($this->modules[$key], function($a, $b) {
|
372
|
|
|
return $a['label'] > $b['label'];
|
373
|
|
|
});
|
374
|
|
|
}
|
375
|
|
|
}
|
376
|
|
|
|
377
|
|
|
private function sortSubmodiles()
|
378
|
|
|
{
|
379
|
|
|
uasort($this->submodules, function($a, $b) {
|
380
|
|
|
return $a['position'] > $b['position'];
|
381
|
|
|
});
|
382
|
|
|
}
|
383
|
|
|
|
384
|
|
|
/**
|
385
|
|
|
* @param BModule $module
|
386
|
|
|
*
|
387
|
|
|
* @return bool
|
388
|
|
|
*/
|
389
|
|
|
private function allowedModule($module)
|
390
|
|
|
{
|
391
|
|
|
return $module instanceof BModule && !empty($module->group) && $module->enabled;
|
392
|
|
|
}
|
393
|
|
|
} |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.