1 <?php
2
3 4 5 6 7 8 9 10
11
12 namespace Icybee\Modules\Modules;
13
14 use ICanBoogie\I18n;
15 use ICanBoogie\I18n\FormattedString;
16 use ICanBoogie\Operation;
17 use ICanBoogie\Route;
18
19 use Brickrouge\A;
20 use Brickrouge\Button;
21 use Brickrouge\Element;
22 use Brickrouge\Form;
23 use Brickrouge\ListView;
24
25 use Icybee\Element\ActionbarToolbar;
26
27 class ManageBlock extends ListView
28 {
29 static protected function add_assets(\Brickrouge\Document $document)
30 {
31 parent::add_assets($document);
32
33 $document->css->add(\Icybee\ASSETS . 'css/manage.css');
34 $document->css->add(DIR . 'public/admin.css');
35 }
36
37 protected $module;
38
39 public function __construct(Module $module, array $attributes=array())
40 {
41 global $core;
42
43 $this->module = $module;
44
45 if (!$core->user->has_permission(Module::PERMISSION_ADMINISTER, $module))
46 {
47 throw new HTTPException("You don't have permission to administer modules.", array(), 403);
48 }
49
50 parent::__construct
51 (
52 $attributes + array
53 (
54 self::ENTRIES => array_values($core->modules->enabled_modules_descriptors),
55 self::COLUMNS => array
56 (
57 'key' => __CLASS__ . '\KeyColumn',
58 'title' => __CLASS__ . '\TitleColumn',
59 'version' => __CLASS__ . '\VersionColumn',
60 'dependency' => __CLASS__ . '\DependencyColumn',
61 'install' => __CLASS__ . '\InstallColumn',
62 'configure' => __CLASS__ . '\ConfigureColumn'
63 ),
64
65 'class' => 'form-primary'
66 )
67 );
68
69 $this->attach_buttons();
70 }
71
72 73 74 75 76
77 protected function get_entries()
78 {
79 $entries = parent::get_entries();
80
81 foreach ($entries as &$descriptor)
82 {
83 $descriptor['__i18n_title'] = self::resolve_module_title($descriptor[Module::T_ID]);
84 $descriptor['__i18n_category'] = self::translate_module_category($descriptor);
85 }
86
87 unset($descriptor);
88
89 usort($entries, function($a, $b) {
90
91 return \ICanBoogie\unaccent_compare_ci($a['__i18n_title'], $b['__i18n_title']);
92
93 });
94
95 return $entries;
96 }
97
98 protected function render_rows(array $rows)
99 {
100 $rendered_rows = parent::render_rows($rows);
101 $entries = $this->entries;
102 $grouped = array();
103
104 foreach ($rendered_rows as $i => $row)
105 {
106 $descriptor = $entries[$i];
107 $grouped[$descriptor['__i18n_category']][$i] = $row;
108 }
109
110 uksort($grouped, 'ICanBoogie\unaccent_compare_ci');
111
112 $span = count($rendered_rows) - 2;
113 $rendered_rows = array();
114
115 foreach ($grouped as $group_title => $rows)
116 {
117 $rendered_rows[] = <<<EOT
118 <tr class="listview-divider">
119 <td> </td>
120 <td>$group_title</td>
121 <td colspan="$span"> </td>
122 </tr>
123 EOT;
124 foreach ($rows as $row)
125 {
126 $rendered_rows[] = $row;
127 }
128 }
129
130 return $rendered_rows;
131 }
132
133 protected function decorate($html)
134 {
135 $operation_destination_name = \ICanBoogie\Operation::DESTINATION;
136 $operation_destination_value = $this->module->id;
137 $operation_name = \ICanBoogie\Operation::NAME;
138
139 $operation_value = ($this instanceof InactivesBlock) ? Module::OPERATION_ACTIVATE : Module::OPERATION_DEACTIVATE;
140
141 return <<<EOT
142 <form action="" method="POST" class="form-primary">
143 <input type="hidden" name="{$operation_destination_name}" value="$operation_destination_value" />
144 <input type="hidden" name="{$operation_name}" value="$operation_value" />
145
146 $html
147 </form>
148 EOT;
149 }
150
151 protected function attach_buttons()
152 {
153 global $core;
154
155 $core->events->attach(function(ActionbarToolbar\CollectEvent $event, ActionbarToolbar $target) {
156
157 $event->buttons[] = new Button
158 (
159 'Disable selected modules', array
160 (
161 'class' => 'btn-primary btn-danger',
162 'type' => 'submit',
163 'data-target' => '.form-primary'
164 )
165 );
166
167 });
168 }
169
170 static public function resolve_module_title($module_id)
171 {
172 global $core;
173
174 return I18n\t
175 (
176 'module_title.' . strtr($module_id, '.', '_'), array(), array
177 (
178 'default' => isset($core->modules->descriptors[$module_id]) ? $core->modules->descriptors[$module_id][Module::T_TITLE] : $module_id
179 )
180 );
181 }
182
183 static public function translate_module_category(array $descriptor)
184 {
185 $category = $descriptor[Module::T_CATEGORY];
186
187 if (!$category)
188 {
189 list($category) = explode('.', $descriptor[Module::T_ID]);
190 }
191
192 return I18n\t($category, array(), array('scope' => 'module_category', 'default' => ucfirst($category)));
193 }
194 }
195
196 197 198
199
200 namespace Icybee\Modules\Modules\ManageBlock;
201
202 use ICanBoogie\I18n;
203 use ICanBoogie\I18n\FormattedString;
204 use ICanBoogie\Module;
205 use ICanBoogie\Operation;
206
207 use Brickrouge\A;
208 use Brickrouge\Element;
209 use Brickrouge\ListViewColumn;
210
211 use Icybee\Modules\Modules\ManageBlock;
212
213 214 215
216 class KeyColumn extends ListViewColumn
217 {
218 public function __construct(ManageBlock $listview, $id, array $options=array())
219 {
220 parent::__construct
221 (
222 $listview, $id, $options + array
223 (
224 'title' => null
225 )
226 );
227 }
228
229 public function render_cell($descriptor)
230 {
231 global $core;
232
233 $module_id = $descriptor[Module::T_ID];
234 $disabled = $descriptor[Module::T_REQUIRED];
235
236 if ($core->modules->usage($module_id))
237 {
238 $disabled = true;
239 }
240
241 return new Element
242 (
243 Element::TYPE_CHECKBOX, array
244 (
245 'name' => Operation::KEY . '[' . $module_id . ']',
246 'disabled' => $disabled
247 )
248 );
249 }
250 }
251
252 253 254
255 class TitleColumn extends ListViewColumn
256 {
257 public function __construct(ManageBlock $listview, $id, array $options=array())
258 {
259 parent::__construct
260 (
261 $listview, $id, $options + array
262 (
263 'title' => 'Module'
264 )
265 );
266 }
267
268 public function render_cell($descriptor)
269 {
270 $module_id = $descriptor[Module::T_ID];
271 $title = $descriptor['__i18n_title'];
272
273 $html = \ICanBoogie\Routes::get()->find('/admin/' . $module_id) ? '<a href="' . \ICanBoogie\Routing\contextualize('/admin/' . $module_id) . '">' . $title . '</a>' : $title;
274
275 $description = I18n\t
276 (
277 'module_description.' . strtr($module_id, '.', '_'), array(), array
278 (
279 'default' => I18n\t($descriptor[Module::T_DESCRIPTION]) ?: '<em class="light">' . I18n\t('No description') . '</em>'
280 )
281 );
282
283 if ($description)
284 {
285 $html .= '<div class="small">' . $description . '</div>';
286 }
287
288 return $html;
289 }
290 }
291
292 293 294
295 class VersionColumn extends ListViewColumn
296 {
297 public function __construct(ManageBlock $listview, $id, array $options=array())
298 {
299 parent::__construct
300 (
301 $listview, $id, $options + array
302 (
303 'title' => 'Version'
304 )
305 );
306 }
307
308 public function render_cell($descriptor)
309 {
310 $version = $descriptor[Module::T_VERSION];
311
312 if (!$version)
313 {
314 return;
315 }
316
317 return '<span class="small lighter">' . $version . '</span>';
318 }
319 }
320
321 322 323
324 class DependencyColumn extends ListViewColumn
325 {
326 public function __construct(ManageBlock $listview, $id, array $options=array())
327 {
328 parent::__construct
329 (
330 $listview, $id, $options + array
331 (
332 'title' => 'Dependency'
333 )
334 );
335 }
336
337 public function render_cell($descriptor)
338 {
339 global $core;
340
341 $html = '';
342 $extends = $descriptor[Module::T_EXTENDS];
343 $module_id = $descriptor[Module::T_ID];
344
345 if ($extends)
346 {
347 $label = ManageBlock::resolve_module_title($extends);
348 $class = isset($core->modules[$extends]) ? 'success' : 'warning';
349
350 $html .= '<div class="extends">Extends: ';
351 $html .= '<span class="label label-' . $class . '">' . $label . '</span>';
352 $html .= '</div>';
353 }
354
355 $requires = $descriptor[Module::T_REQUIRES];
356
357 if ($requires)
358 {
359 $html .= '<div class="requires">Requires: ';
360
361 foreach ($requires as $require_id => $version)
362 {
363 $label = ManageBlock::resolve_module_title($require_id);
364 $label_class = isset($core->modules[$require_id]) ? 'success' : 'warning';
365
366 $html .= <<<EOT
367 <span class="label label-{$label_class}" title="Version $version">$label</span>
368 EOT;
369
370 $html .= ' ';
371 }
372
373 $html .= '</div>';
374 }
375
376 $usage = $core->modules->usage($module_id);
377
378 if ($usage)
379 {
380 $html .= '<div class="usage light">' . I18n\t('Used by :count modules', array(':count' => $usage)) . '</div>';
381 }
382
383 return $html;
384 }
385 }
386
387 388 389
390 class InstallColumn extends ListViewColumn
391 {
392 public function __construct(ManageBlock $listview, $id, array $options=array())
393 {
394 parent::__construct
395 (
396 $listview, $id, $options + array
397 (
398 'title' => 'Installed'
399 )
400 );
401 }
402
403 public function render_cell($descriptor)
404 {
405 global $core;
406
407 $module_id = $descriptor[Module::T_ID];
408
409 try
410 {
411 $module = $core->modules[$module_id];
412 }
413 catch (\Exception $e)
414 {
415 return '<div class="alert alert-error">' . $e->getMessage() . '</div>';
416 }
417
418 $html = '';
419 $is_installed = false;
420
421
422
423 $errors = new \ICanBoogie\Errors;
424 $extends_errors = new \ICanBoogie\Errors;
425 $n_errors = count($errors);
426
427 while ($descriptor[Module::T_EXTENDS])
428 {
429 $extends = $descriptor[Module::T_EXTENDS];
430
431 if (empty($core->modules->descriptors[$extends]))
432 {
433 $errors[$module_id] = I18n\t('Requires the %module module which is missing.', array('%module' => $extends));
434
435 break;
436 }
437 else if (!isset($core->modules[$extends]))
438 {
439 $errors[$module_id] = I18n\t('Requires the %module module which is disabled.', array('%module' => $extends));
440
441 break;
442 }
443 else
444 {
445 $extends_errors->clear();
446 $extends_module = $core->modules[$extends];
447 $extends_is_installed = $extends_module->is_installed($extends_errors);
448
449 if (count($extends_errors))
450 {
451 $extends_is_installed = false;
452 }
453
454 if (!$extends_is_installed)
455 {
456 $errors[$module_id] = I18n\t('Requires the %module module which is disabled.', array('%module' => $extends));
457
458 break;
459 }
460 }
461
462 $descriptor = $core->modules->descriptors[$extends];
463 }
464
465 if ($n_errors != count($errors))
466 {
467 $html .= '<div class="alert alert-error">' . implode('<br />', (array) $errors[$module_id]) . '</div>';
468 }
469 else
470 {
471 try
472 {
473 $n_errors = count($errors);
474 $is_installed = $module->is_installed($errors);
475
476 if (count($errors) != $n_errors)
477 {
478 $is_installed = false;
479 }
480 }
481 catch (\Exception $e)
482 {
483 $errors[$module->id] = I18n\t
484 (
485 'Exception with module %module: :message', array
486 (
487 '%module' => (string) $module,
488 ':message' => $e->getMessage()
489 )
490 );
491 }
492
493 if ($is_installed)
494 {
495 $html .= I18n\t('Installed');
496 }
497 else if ($is_installed === false)
498 {
499 $btn = '<a class="btn btn-danger" href="'
500 . \ICanBoogie\Routing\contextualize("/admin/modules/{$module}/install")
501 . '">' . I18n\t('Install module') . '</a>';
502
503 $title = new FormattedString('The module %title is not properly installed', array('title' => $module->title));
504
505 \ICanBoogie\log_error("$title.");
506
507 if (isset($errors[$module_id]))
508 {
509 $error = $errors[$module_id];
510
511 if (is_array($error))
512 {
513 $error = implode('</p><p>', $error);
514 }
515
516 $html .= <<<EOT
517 <div class="alert alert-error alert-block undissmissable">
518 <h4 class="alert-heading">$title</h4>
519 <div class="content">
520 <p>$error</p>
521 </div>
522
523 <div class="alert-actions">$btn</div>
524 </div>
525 EOT;
526 }
527 else
528 {
529 $html .= $btn;
530 }
531 }
532 else
533 {
534 $html .= '<em class="not-applicable light">Not applicable</em>';
535 }
536 }
537
538 return $html;
539 }
540 }
541
542 543 544
545 class ConfigureColumn extends ListViewColumn
546 {
547 public function __construct(ManageBlock $listview, $id, array $options=array())
548 {
549 parent::__construct
550 (
551 $listview, $id, $options + array
552 (
553 'title' => null
554 )
555 );
556 }
557
558 public function render_cell($descriptor)
559 {
560 global $core;
561
562 $module_id = $descriptor[Module::T_ID];
563
564 if (empty($core->routes["admin:$module_id/config"]))
565 {
566 return;
567 }
568
569 $route = $core->routes["admin:$module_id/config"];
570
571 return new A('Configure', \ICanBoogie\Routing\contextualize($route->pattern));
572 }
573 }