Dotclear

source: inc/admin/lib.moduleslist.php @ 2158:13b49921f64a

Revision 2158:13b49921f64a, 16.1 KB checked in by Anne Kozlika <kozlika@…>, 12 years ago (diff)

Déplacement du bouton reset, meilleure vue des messages et résumés de résultats.

Line 
1<?php
2
3class adminModulesList
4{
5     public $core;
6     public $modules;
7
8     public static  $allow_multi_install;
9
10     protected $list_id = 'unknow';
11
12     protected $path = false;
13     protected $path_writable = false;
14     protected $path_pattern = false;
15
16     protected $page_url = 'plugins.php';
17     protected $page_qs = '?';
18     protected $page_tab = '';
19
20     public static $nav_indexes = 'abcdefghijklmnopqrstuvwxyz0123456789';
21     protected $nav_list = array();
22     protected $nav_special = 'other';
23
24     protected $sort_field = 'sname';
25     protected $sort_asc = true;
26
27     public function __construct($core, $root, $allow_multi_install=false)
28     {
29          $this->core = $core;
30          self::$allow_multi_install = (boolean) $allow_multi_install;
31          $this->setPathInfo($root);
32          $this->setNavSpecial(__('other'));
33
34          $this->init();
35     }
36
37     protected function init()
38     {
39          return null;
40     }
41
42     public function newList($list_id)
43     {
44          $this->modules = array();
45          $this->page_tab = '';
46          $this->list_id = $list_id;
47
48          return $this;
49     }
50
51     protected function setPathInfo($root)
52     {
53          $paths = explode(PATH_SEPARATOR, $root);
54          $path = array_pop($paths);
55          unset($paths);
56
57          $this->path = $path;
58          if (is_dir($path) && is_writeable($path)) {
59               $this->path_writable = true;
60               $this->path_pattern = preg_quote($path,'!');
61          }
62
63          return $this;
64     }
65
66     public function getPath()
67     {
68          return $this->path;
69     }
70
71     public function isPathWritable()
72     {
73          return $this->path_writable;
74     }
75
76     public function isPathDeletable($root)
77     {
78          return $this->path_writable 
79               && preg_match('!^'.$this->path_pattern.'!', $root) 
80               && $this->core->auth->isSuperAdmin();
81     }
82
83     public function setPageURL($url)
84     {
85          $this->page_qs = strpos('?', $url) ? '&' : '?';
86          $this->page_url = $url;
87
88          return $this;
89     }
90
91     public function getPageURL($queries='', $with_tab=true)
92     {
93          return $this->page_url.
94               (!empty($queries) ? $this->page_qs : '').
95               (is_array($queries) ? http_build_query($queries) : $queries).
96               ($with_tab && !empty($this->page_tab) ? '#'.$this->page_tab : '');
97     }
98
99     public function setPageTab($tab)
100     {
101          $this->page_tab = $tab;
102
103          return $this;
104     }
105
106     public function getPageTab()
107     {
108          return $this->page_tab;
109     }
110
111     public function getSearchQuery()
112     {
113          $query = !empty($_REQUEST['m_search']) ? trim($_REQUEST['m_search']) : null;
114          return strlen($query) > 1 ? $query : null;
115     }
116
117     public function displaySearchForm()
118     {
119          if (empty($this->modules)) {
120               return $this;
121          }
122
123          $query = $this->getSearchQuery();
124
125          echo 
126          '<form action="'.$this->getPageURL().'" method="get" class="fieldset">'.
127          '<p><label for="m_search" class="classic">'.__('Search in repository:').'&nbsp;</label><br />'.
128          form::field(array('m_search','m_search'), 30, 255, html::escapeHTML($query)).
129          '<input type="submit" value="'.__('Search').'" /> ';
130          if ($query) { echo ' <a href="'.$this->getPageURL().'" class="button">'.__('Reset search').'</a>'; }
131          echo '</p>'.
132          '</form>';
133
134          if ($query) {
135               echo 
136               '<p class="message">'.sprintf(
137                    __('Found %d result for search "%s":', 'Found %d results for search "%s":', count($this->modules)), 
138                    count($this->modules), html::escapeHTML($query)
139                    ).
140               '</p>';
141          }
142          return $this;
143     }
144
145     public function setNavSpecial($str)
146     {
147          $this->nav_special = (string) $str;
148          $this->nav_list = array_merge(str_split(self::$nav_indexes), array($this->nav_special));
149
150          return $this;
151     }
152
153     public function getNavQuery()
154     {
155          return isset($_REQUEST['m_nav']) && in_array($_REQUEST['m_nav'], $this->nav_list) ? $_REQUEST['m_nav'] : $this->nav_list[0];
156     }
157
158     public function displayNavMenu()
159     {
160          if (empty($this->modules) || $this->getSearchQuery() !== null) {
161               return $this;
162          }
163
164          # Fetch modules required field
165          $indexes = array();
166          foreach ($this->modules as $id => $module) {
167               if (!isset($module[$this->sort_field])) {
168                    continue;
169               }
170               $char = substr($module[$this->sort_field], 0, 1);
171               if (!in_array($char, $this->nav_list)) {
172                    $char = $this->nav_special;
173               }
174               if (!isset($indexes[$char])) {
175                    $indexes[$char] = 0;
176               }
177               $indexes[$char]++;
178          }
179
180          $buttons = array();
181          foreach($this->nav_list as $char) {
182               # Selected letter
183               if ($this->getNavQuery() == $char) {
184                    $buttons[] = '<li class="active" title="'.__('current selection').'"><strong> '.$char.' </strong></li>';
185               }
186               # Letter having modules
187               elseif (!empty($indexes[$char])) {
188                    $title = sprintf(__('%d module', '%d modules', $indexes[$char]), $indexes[$char]);
189                    $buttons[] = '<li class="btn" title="'.$title.'"><a href="'.$this->getPageURL('m_nav='.$char).'" title="'.$title.'"> '.$char.' </a></li>';
190               }
191               # Letter without modules
192               else {
193                    $buttons[] = '<li class="btn no-link" title="'.__('no module').'"> '.$char.' </li>';
194               }
195          }
196          # Parse navigation menu
197          echo '<div class="pager">'.__('Browse index:').' <ul>'.implode('',$buttons).'</ul></div>';
198
199          return $this;
200     }
201
202     public function setSortField($field, $asc=true)
203     {
204          $this->sort_field = $field;
205          $this->sort_asc = (boolean) $asc;
206
207          return $this;
208     }
209
210     public function getSortQuery()
211     {
212          return !empty($_REQUEST['m_sort']) ? $_REQUEST['m_sort'] : $this->sort_field;
213     }
214
215     public function displaySortForm()
216     {
217          //not yet implemented
218     }
219
220     public function displayMessage($action)
221     {
222          switch($action) {
223               case 'activate': 
224                    $str = __('Module successfully activated.'); break;
225               case 'deactivate': 
226                    $str = __('Module successfully deactivated.'); break;
227               case 'delete': 
228                    $str = __('Module successfully deleted.'); break;
229               case 'install': 
230                    $str = __('Module successfully installed.'); break;
231               case 'update': 
232                    $str = __('Module successfully updated.'); break;
233               default:
234                    $str = ''; break;
235          }
236          if (!empty($str)) {
237               dcPage::success($str);
238          }
239     }
240
241     public function setModules($modules)
242     {
243          $this->modules = array();
244          foreach($modules as $id => $module) {
245               $this->modules[$id] = self::setModuleInfo($id, $module);
246          }
247
248          return $this;
249     }
250
251     public function getModules()
252     {
253          return $this->modules;
254     }
255
256     public static function setModuleInfo($id, $module)
257     {
258          $label = empty($module['label']) ? $id : $module['label'];
259          $name = __(empty($module['name']) ? $label : $module['name']);
260         
261          return array_merge(
262               # Default values
263               array(
264                    'desc'                   => '',
265                    'author'            => '',
266                    'version'                => 0,
267                    'current_version'   => 0,
268                    'root'                   => '',
269                    'root_writable'     => false,
270                    'permissions'       => null,
271                    'parent'            => null,
272                    'priority'               => 1000,
273                    'standalone_config' => false,
274                    'support'                => '',
275                    'section'                => '',
276                    'tags'                   => '',
277                    'details'                => ''
278               ),
279               # Module's values
280               $module,
281               # Clean up values
282               array(
283                    'id'                     => $id,
284                    'sid'                    => self::sanitizeString($id),
285                    'label'             => $label,
286                    'name'                   => $name,
287                    'sname'             => self::sanitizeString($name)
288               )
289          );
290     }
291
292     public static function isDistributedModule($module)
293     {
294          return in_array($module, array(
295               'aboutConfig',
296               'akismet',
297               'antispam',
298               'attachments',
299               'blogroll',
300               'blowupConfig',
301               'daInstaller',
302               'fairTrackbacks',
303               'importExport',
304               'maintenance',
305               'pages',
306               'pings',
307               'simpleMenu',
308               'tags',
309               'themeEditor',
310               'userPref',
311               'widgets'
312          ));
313     }
314
315     public static function sortModules($modules, $field, $asc=true)
316     {
317          $sorter = array();
318          foreach($modules as $id => $module) {
319               $sorter[$id] = isset($module[$field]) ? $module[$field] : $field;
320          }
321          array_multisort($sorter, $asc ? SORT_ASC : SORT_DESC, $modules);
322
323          return $modules;
324     }
325
326     public function displayModulesList($cols=array('name', 'config', 'version', 'desc'), $actions=array(), $nav_limit=false)
327     {
328          echo 
329          '<div class="table-outer">'.
330          '<table id="'.html::escapeHTML($this->list_id).'" class="modules'.(in_array('expander', $cols) ? ' expandable' : '').'">'.
331          '<caption class="hidden">'.html::escapeHTML(__('Modules list')).'</caption><tr>';
332/*
333          if (in_array('expander', $cols)) {
334               echo
335               '<th class="minimal"></th>';
336          }
337//*/
338/*
339          if ($this->getSearchQuery() !== null) {
340               echo
341               '<th class="nowrap">'.__('Accuracy').'</th>';
342          }
343//*/
344          if (in_array('name', $cols)) {
345               echo 
346               '<th class="first nowrap"'.(in_array('icon', $cols) ? ' colspan="2"' : '').'>'.__('Name').'</th>';
347          }
348
349          if (in_array('version', $cols)) {
350               echo 
351               '<th class="nowrap count" scope="col">'.__('Version').'</th>';
352          }
353
354          if (in_array('current_version', $cols)) {
355               echo 
356               '<th class="nowrap count" scope="col">'.__('Current version').'</th>';
357          }
358
359          if (in_array('desc', $cols)) {
360               echo 
361               '<th class="nowrap" scope="col">'.__('Details').'</th>';
362          }
363
364          if (in_array('distrib', $cols)) {
365               echo '<th'.(in_array('desc', $cols) ? '' : ' class="maximal"').'></th>';
366          }
367
368          if (!empty($actions) && $this->core->auth->isSuperAdmin()) {
369               echo 
370               '<th class="minimal nowrap">'.__('Action').'</th>';
371          }
372
373          echo 
374          '</tr>';
375
376          $sort_field = $this->getSortQuery();
377
378          # Sort modules by id
379          $modules = $this->getSearchQuery() === null ?
380               self::sortModules($this->modules, $sort_field, $this->sort_asc) :
381               $this->modules;
382
383          $count = 0;
384          foreach ($modules as $id => $module)
385          {
386               # Show only requested modules
387               if ($nav_limit && $this->getSearchQuery() === null) {
388                    $char = substr($module[$sort_field], 0, 1);
389                    if (!in_array($char, $this->nav_list)) {
390                         $char = $this->nav_special;
391                    }
392                    if ($this->getNavQuery() != $char) {
393                         continue;
394                    }
395               }
396
397               echo 
398               '<tr class="line" id="'.html::escapeHTML($this->list_id).'_m_'.html::escapeHTML($id).'" title="plop">';
399/*
400               if (in_array('expander', $cols)) {
401                    echo
402                    '<td class="minimal expander" title="'.html::escapeHTML($id).'"></td>';
403               }
404//*/
405/*
406               if ($this->getSearchQuery() !== null) {
407                    echo
408                    '<td class="nowrap count">'.$module['accuracy'].'</td>';
409               }
410//*/
411               if (in_array('icon', $cols)) {
412                    echo 
413                    '<td class="nowrap icon">'.sprintf(
414                         '<img alt="%1$s" title="%1$s" src="%2$s" />', 
415                         html::escapeHTML($id), file_exists($module['root'].'/icon.png') ? 'index.php?pf='.$id.'/icon.png' : 'images/module.png'
416                    ).'</td>';
417               }
418
419               # Link to config file
420               $config = in_array('config', $cols) && !empty($module['root']) && file_exists(path::real($module['root'].'/_config.php'));
421
422               echo 
423               '<td class="nowrap" scope="row">'.($config ? 
424                    '<a href="'.$this->getPageURL('module='.$id.'&conf=1').'">'.html::escapeHTML($module['name']).'</a>' : 
425                    html::escapeHTML($module['name'])
426               ).'</td>';
427
428               if (in_array('version', $cols)) {
429                    echo 
430                    '<td class="nowrap count">'.html::escapeHTML($module['version']).'</td>';
431               }
432
433               if (in_array('current_version', $cols)) {
434                    echo 
435                    '<td class="nowrap count">'.html::escapeHTML($module['current_version']).'</td>';
436               }
437
438               if (in_array('desc', $cols)) {
439                    echo 
440                    '<td class="maximal">'.html::escapeHTML($module['desc']).'</td>';
441               }
442
443               if (in_array('distrib', $cols)) {
444                    echo 
445                    '<td class="distrib">'.(self::isDistributedModule($id) ? 
446                         '<img src="images/dotclear_pw.png" alt="'.
447                         __('Module from official distribution').'" title="'.
448                         __('module from official distribution').'" />' 
449                    : '').'</td>';
450               }
451
452               if (!empty($actions) && $this->core->auth->isSuperAdmin()) {
453                    echo 
454                    '<td class="nowrap">';
455
456                    $this->displayLineActions($id, $module, $actions);
457
458                    echo
459                    '</td>';
460               }
461
462               echo 
463               '</tr>';
464
465               $count++;
466          }
467          echo 
468          '</table></div>';
469
470          if(!$count) {
471               echo 
472               '<p class="message">'.__('No module matches your search.').'</p>';
473          }
474     }
475
476     protected function displayLineActions($id, $module, $actions)
477     {
478          $submits = array();
479
480          # Activate
481          if (in_array('deactivate', $actions) && $module['root_writable']) {
482               $submits[] = '<input type="submit" name="deactivate" value="'.__('Deactivate').'" />';
483          }
484
485          # Deactivate
486          if (in_array('activate', $actions) && $module['root_writable']) {
487               $submits[] = '<input type="submit" name="activate" value="'.__('Activate').'" />';
488          }
489
490          # Delete
491          if (in_array('delete', $actions) && $this->isPathDeletable($module['root'])) {
492               $submits[] = '<input type="submit" class="delete" name="delete" value="'.__('Delete').'" />';
493          }
494
495          # Install (form repository)
496          if (in_array('install', $actions) && $this->path_writable) {
497               $submits[] = '<input type="submit" name="install" value="'.__('Install').'" />';
498          }
499
500          # Update (from repository)
501          if (in_array('update', $actions) && $this->path_writable) {
502               $submits[] = '<input type="submit" name="update" value="'.__('Update').'" />';
503          }
504
505          # Parse form
506          if (!empty($submits)) {
507               echo 
508               '<form action="'.$this->getPageURL().'" method="post">'.
509               '<div>'.
510               $this->core->formNonce().
511               form::hidden(array('module'), html::escapeHTML($id)).
512               form::hidden(array('tab'), $this->page_tab).
513               implode(' ', $submits).
514               '</div>'.
515               '</form>';
516          }
517     }
518
519     public function executeAction($prefix, dcModules $modules, dcRepository $repository)
520     {
521          if (empty($_POST['module'])   || !$this->core->auth->isSuperAdmin() || !$this->isPathWritable()) {
522               return null;
523          }
524
525          $id = $_POST['module'];
526
527          if (!empty($_POST['activate'])) {
528
529               $enabled = $modules->getDisabledModules();
530               if (!isset($enabled[$id])) {
531                    throw new Exception(__('No such module.'));
532               }
533
534               # --BEHAVIOR-- moduleBeforeActivate
535               $this->core->callBehavior($type.'BeforeActivate', $id);
536
537               $modules->activateModule($id);
538
539               # --BEHAVIOR-- moduleAfterActivate
540               $this->core->callBehavior($type.'AfterActivate', $id);
541
542               http::redirect($this->getPageURL('msg=activate'));
543          }
544
545          if (!empty($_POST['deactivate'])) {
546
547               if (!$modules->moduleExists($id)) {
548                    throw new Exception(__('No such module.'));
549               }
550
551               $module = $modules->getModules($id);
552               $module['id'] = $id;
553
554               if (!$module['root_writable']) {
555                    throw new Exception(__('You don\'t have permissions to deactivate this module.'));
556               }
557
558               # --BEHAVIOR-- moduleBeforeDeactivate
559               $this->core->callBehavior($prefix.'BeforeDeactivate', $module);
560
561               $modules->deactivateModule($id);
562
563               # --BEHAVIOR-- moduleAfterDeactivate
564               $this->core->callBehavior($prefix.'AfterDeactivate', $module);
565
566               http::redirect($this->getPageURL('msg=deactivate'));
567          }
568
569          if (!empty($_POST['delete'])) {
570
571               $disabled = $modules->getDisabledModules();
572               if (!isset($disabled[$id])) {
573
574                    if (!$modules->moduleExists($id)) {
575                         throw new Exception(__('No such module.'));
576                    }
577
578                    $module = $modules->getModules($id);
579                    $module['id'] = $id;
580
581                    if (!$this->isPathDeletable($module['root'])) {
582                         throw new Exception(__("You don't have permissions to delete this module."));
583                    }
584
585                    # --BEHAVIOR-- moduleBeforeDelete
586                    $this->core->callBehavior($prefix.'BeforeDelete', $module);
587
588                    $modules->deleteModule($id);
589
590                    # --BEHAVIOR-- moduleAfterDelete
591                    $this->core->callBehavior($prefix.'AfterDelete', $module);
592               }
593               else {
594                    $modules->deleteModule($id, true);
595               }
596
597               http::redirect($this->getPageURL('msg=delete'));
598          }
599
600          if (!empty($_POST['update'])) {
601
602               $updated = $repository->get();
603               if (!isset($updated[$id])) {
604                    throw new Exception(__('No such module.'));
605               }
606
607               if (!$modules->moduleExists($id)) {
608                    throw new Exception(__('No such module.'));
609               }
610
611               $module = $updated[$id];
612               $module['id'] = $id;
613         
614               if (!self::$allow_multi_install) {
615                    $dest = $module['root'].'/../'.basename($module['file']);
616               }
617               else {
618                    $dest = $this->getPath().'/'.basename($module['file']);
619                    if ($module['root'] != $dest) {
620                         @file_put_contents($module['root'].'/_disabled', '');
621                    }
622               }
623
624               # --BEHAVIOR-- moduleBeforeUpdate
625               $this->core->callBehavior($type.'BeforeUpdate', $module);
626
627               $repository->process($module['file'], $dest);
628
629               # --BEHAVIOR-- moduleAfterUpdate
630               $this->core->callBehavior($type.'AfterUpdate', $module);
631
632               http::redirect($this->getPageURL('msg=upadte'));
633          }
634
635          if (!empty($_POST['install'])) {
636
637               $updated = $repository->get();
638               if (!isset($updated[$id])) {
639                    throw new Exception(__('No such module.'));
640               }
641
642               $module = $updated[$id];
643               $module['id'] = $id;
644
645               $dest = $this->getPath().'/'.basename($module['file']);
646
647               # --BEHAVIOR-- moduleBeforeAdd
648               $this->core->callBehavior($type.'BeforeAdd', $module);
649
650               $ret_code = $repository->process($module['file'], $dest);
651
652               # --BEHAVIOR-- moduleAfterAdd
653               $this->core->callBehavior($type.'AfterAdd', $module);
654
655               http::redirect($this->getPageURL('msg='.($ret_code == 2 ? 'update' : 'install')));
656          }
657     }
658
659     public static function sanitizeString($str)
660     {
661          return preg_replace('/[^A-Za-z0-9\@\#+_-]/', '', strtolower($str));
662     }
663}
664
665class adminThemesList extends adminModulesList
666{
667
668}
Note: See TracBrowser for help on using the repository browser.

Sites map