Dotclear

source: inc/admin/lib.moduleslist.php @ 3338:e3eba670c9d0

Revision 3338:e3eba670c9d0, 53.7 KB checked in by franck <carnet.franck.paul@…>, 9 years ago (diff)

Add redir param for _config.php URL if not in plugins page and if it is not a standalone_config module, thanks Gvx for advice

Line 
1<?php
2# -- BEGIN LICENSE BLOCK ---------------------------------------
3#
4# This file is part of Dotclear 2.
5#
6# Copyright (c) 2003-2013 Olivier Meunier & Association Dotclear
7# Licensed under the GPL version 2.0 license.
8# See LICENSE file or
9# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10#
11# -- END LICENSE BLOCK -----------------------------------------
12if (!defined('DC_ADMIN_CONTEXT')) { return; }
13
14/**
15 * @ingroup DC_CORE
16 * @brief Helper for admin list of modules.
17 * @since 2.6
18
19 * Provides an object to parse XML feed of modules from a repository.
20 */
21class adminModulesList
22{
23     public $core;       /**< @var object    dcCore instance */
24     public $modules;    /**< @var object    dcModules instance */
25     public $store;      /**< @var object    dcStore instance */
26
27     public static $allow_multi_install = false;       /**< @var boolean   Work with multiple root directories */
28     public static $distributed_modules = array();     /**< @var array     List of modules distributed with Dotclear */
29
30     protected $list_id = 'unknow';     /**< @var string    Current list ID */
31     protected $data = array();         /**< @var array     Current modules */
32
33     protected $config_module = '';     /**< @var string    Module ID to configure */
34     protected $config_file = '';  /**< @var string    Module path to configure */
35     protected $config_content = '';    /**< @var string    Module configuration page content */
36
37     protected $path = false;           /**< @var string    Modules root directory */
38     protected $path_writable = false;  /**< @var boolean   Indicate if modules root directory is writable */
39     protected $path_pattern = false;   /**< @var string    Directory pattern to work on */
40
41     protected $page_url = '';                    /**< @var string    Page URL */
42     protected $page_qs = '?';                    /**< @var string    Page query string */
43     protected $page_tab = '';                    /**< @var string    Page tab */
44     protected $page_redir = '';                  /**< @var string    Page redirection */
45
46     public static $nav_indexes = 'abcdefghijklmnopqrstuvwxyz0123456789'; /**< @var  string    Index list */
47     protected $nav_list = array();          /**< @var array     Index list with special index */
48     protected $nav_special = 'other';  /**< @var string    Text for other special index */
49
50     protected $sort_field = 'sname';   /**< @var string    Field used to sort modules */
51     protected $sort_asc = true;             /**< @var boolean   Sort order asc */
52
53     /**
54      * Constructor.
55      *
56      * Note that this creates dcStore instance.
57      *
58      * @param object    $modules       dcModules instance
59      * @param string    $modules_root  Modules root directories
60      * @param string    $xml_url       URL of modules feed from repository
61      */
62     public function __construct(dcModules $modules, $modules_root, $xml_url)
63     {
64          $this->core = $modules->core;
65          $this->modules = $modules;
66          $this->store = new dcStore($modules, $xml_url);
67
68          $this->page_url = $this->core->adminurl->get('admin.plugins');
69
70          $this->setPath($modules_root);
71          $this->setIndex(__('other'));
72     }
73
74     /**
75      * Begin a new list.
76      *
77      * @param string    $id       New list ID
78      * @return     adminModulesList self instance
79      */
80     public function setList($id)
81     {
82          $this->data = array();
83          $this->page_tab = '';
84          $this->list_id = $id;
85
86          return $this;
87     }
88
89     /**
90      * Get list ID.
91      *
92      * @return     List ID
93      */
94     public function getList()
95     {
96          return $this->list_id;
97     }
98
99     /// @name Modules root directory methods
100     //@{
101     /**
102      * Set path info.
103      *
104      * @param string    $root          Modules root directories
105      * @return     adminModulesList self instance
106      */
107     protected function setPath($root)
108     {
109          $paths = explode(PATH_SEPARATOR, $root);
110          $path = array_pop($paths);
111          unset($paths);
112
113          $this->path = $path;
114          if (is_dir($path) && is_writeable($path)) {
115               $this->path_writable = true;
116               $this->path_pattern = preg_quote($path,'!');
117          }
118
119          return $this;
120     }
121
122     /**
123      * Get modules root directory.
124      *
125      * @return     Path to work on
126      */
127     public function getPath()
128     {
129          return $this->path;
130     }
131
132     /**
133      * Check if modules root directory is writable.
134      *
135      * @return     True if directory is writable
136      */
137     public function isWritablePath()
138     {
139          return $this->path_writable;
140     }
141
142     /**
143      * Check if root directory of a module is deletable.
144      *
145      * @param string    $root          Module root directory
146      * @return     True if directory is delatable
147      */
148     public function isDeletablePath($root)
149     {
150          return $this->path_writable
151               && (preg_match('!^'.$this->path_pattern.'!', $root) || defined('DC_DEV') && DC_DEV)
152               && $this->core->auth->isSuperAdmin();
153     }
154     //@}
155
156     /// @name Page methods
157     //@{
158     /**
159      * Set page base URL.
160      *
161      * @param string    $url      Page base URL
162      * @return     adminModulesList self instance
163      */
164     public function setURL($url)
165     {
166          $this->page_qs = strpos('?', $url) ? '&amp;' : '?';
167          $this->page_url = $url;
168
169          return $this;
170     }
171
172     /**
173      * Get page URL.
174      *
175      * @param string|array   $queries  Additionnal query string
176      * @param booleany  $with_tab      Add current tab to URL end
177      * @return     Clean page URL
178      */
179     public function getURL($queries='', $with_tab=true)
180     {
181          return $this->page_url.
182               (!empty($queries) ? $this->page_qs : '').
183               (is_array($queries) ? http_build_query($queries) : $queries).
184               ($with_tab && !empty($this->page_tab) ? '#'.$this->page_tab : '');
185     }
186
187     /**
188      * Set page tab.
189      *
190      * @param string    $tab      Page tab
191      * @return     adminModulesList self instance
192      */
193     public function setTab($tab)
194     {
195          $this->page_tab = $tab;
196
197          return $this;
198     }
199
200     /**
201      * Get page tab.
202      *
203      * @return     Page tab
204      */
205     public function getTab()
206     {
207          return $this->page_tab;
208     }
209
210     /**
211      * Set page redirection.
212      *
213      * @param string    $default       Default redirection
214      * @return     adminModulesList self instance
215      */
216     public function setRedir($default='')
217     {
218          $this->page_redir = empty($_REQUEST['redir']) ? $default : $_REQUEST['redir'];
219
220          return $this;
221     }
222
223     /**
224      * Get page redirection.
225      *
226      * @return     Page redirection
227      */
228     public function getRedir()
229     {
230          return empty($this->page_redir) ? $this->getURL() : $this->page_redir;
231     }
232     //@}
233
234     /// @name Search methods
235     //@{
236     /**
237      * Get search query.
238      *
239      * @return     Search query
240      */
241     public function getSearch()
242     {
243          $query = !empty($_REQUEST['m_search']) ? trim($_REQUEST['m_search']) : null;
244          return strlen($query) > 2 ? $query : null;
245     }
246
247     /**
248      * Display searh form.
249      *
250      * @return     adminModulesList self instance
251      */
252     public function displaySearch()
253     {
254          $query = $this->getSearch();
255
256          if (empty($this->data) && $query === null) {
257               return $this;
258          }
259
260          echo
261          '<div class="modules-search">'.
262          '<form action="'.$this->getURL().'" method="get">'.
263          '<p><label for="m_search" class="classic">'.__('Search in repository:').'&nbsp;</label><br />'.
264          form::field(array('m_search','m_search'), 30, 255, html::escapeHTML($query)).
265          '<input type="submit" value="'.__('OK').'" /> ';
266
267          if ($query) {
268               echo
269               ' <a href="'.$this->getURL().'" class="button">'.__('Reset search').'</a>';
270          }
271
272          echo
273          '</p>'.
274          '<p class="form-note">'.
275          __('Search is allowed on multiple terms longer than 2 chars, terms must be separated by space.').
276          '</p>'.
277          '</form>';
278
279          if ($query) {
280               echo
281               '<p class="message">'.sprintf(
282                    __('Found %d result for search "%s":', 'Found %d results for search "%s":', count($this->data)),
283                    count($this->data), html::escapeHTML($query)
284                    ).
285               '</p>';
286          }
287          echo '</div>';
288
289          return $this;
290     }
291     //@}
292
293     /// @name Navigation menu methods
294     //@{
295     /**
296      * Set navigation special index.
297      *
298      * @return     adminModulesList self instance
299      */
300     public function setIndex($str)
301     {
302          $this->nav_special = (string) $str;
303          $this->nav_list = array_merge(str_split(self::$nav_indexes), array($this->nav_special));
304
305          return $this;
306     }
307
308     /**
309      * Get index from query.
310      *
311      * @return     Query index or default one
312      */
313     public function getIndex()
314     {
315          return isset($_REQUEST['m_nav']) && in_array($_REQUEST['m_nav'], $this->nav_list) ? $_REQUEST['m_nav'] : $this->nav_list[0];
316     }
317
318     /**
319      * Display navigation by index menu.
320      *
321      * @return     adminModulesList self instance
322      */
323     public function displayIndex()
324     {
325          if (empty($this->data) || $this->getSearch() !== null) {
326               return $this;
327          }
328
329          # Fetch modules required field
330          $indexes = array();
331          foreach ($this->data as $id => $module) {
332               if (!isset($module[$this->sort_field])) {
333                    continue;
334               }
335               $char = substr($module[$this->sort_field], 0, 1);
336               if (!in_array($char, $this->nav_list)) {
337                    $char = $this->nav_special;
338               }
339               if (!isset($indexes[$char])) {
340                    $indexes[$char] = 0;
341               }
342               $indexes[$char]++;
343          }
344
345          $buttons = array();
346          foreach($this->nav_list as $char) {
347               # Selected letter
348               if ($this->getIndex() == $char) {
349                    $buttons[] = '<li class="active" title="'.__('current selection').'"><strong> '.$char.' </strong></li>';
350               }
351               # Letter having modules
352               elseif (!empty($indexes[$char])) {
353                    $title = sprintf(__('%d result', '%d results', $indexes[$char]), $indexes[$char]);
354                    $buttons[] = '<li class="btn" title="'.$title.'"><a href="'.$this->getURL('m_nav='.$char).'" title="'.$title.'"> '.$char.' </a></li>';
355               }
356               # Letter without modules
357               else {
358                    $buttons[] = '<li class="btn no-link" title="'.__('no results').'"> '.$char.' </li>';
359               }
360          }
361          # Parse navigation menu
362          echo '<div class="pager">'.__('Browse index:').' <ul class="index">'.implode('',$buttons).'</ul></div>';
363
364          return $this;
365     }
366     //@}
367
368     /// @name Sort methods
369     //@{
370     /**
371      * Set default sort field.
372      *
373      * @return     adminModulesList self instance
374      */
375     public function setSort($field, $asc=true)
376     {
377          $this->sort_field = $field;
378          $this->sort_asc = (boolean) $asc;
379
380          return $this;
381     }
382
383     /**
384      * Get sort field from query.
385      *
386      * @return     Query sort field or default one
387      */
388     public function getSort()
389     {
390          return !empty($_REQUEST['m_sort']) ? $_REQUEST['m_sort'] : $this->sort_field;
391     }
392
393     /**
394      * Display sort field form.
395      *
396      * @note  This method is not implemented yet
397      * @return     adminModulesList self instance
398      */
399     public function displaySort()
400     {
401          //
402
403          return $this;
404     }
405     //@}
406
407     /// @name Modules methods
408     //@{
409     /**
410      * Set modules and sanitize them.
411      *
412      * @return     adminModulesList self instance
413      */
414     public function setModules($modules)
415     {
416          $this->data = array();
417          if (!empty($modules) && is_array($modules)) {
418               foreach($modules as $id => $module) {
419                    $this->data[$id] = self::sanitizeModule($id, $module);
420               }
421          }
422          return $this;
423     }
424
425     /**
426      * Get modules currently set.
427      *
428      * @return     Array of modules
429      */
430     public function getModules()
431     {
432          return $this->data;
433     }
434
435     /**
436      * Sanitize a module.
437      *
438      * This clean infos of a module by adding default keys
439      * and clean some of them, sanitize module can safely
440      * be used in lists.
441      *
442      * @return     Array of the module informations
443      */
444     public static function sanitizeModule($id, $module)
445     {
446          $label = empty($module['label']) ? $id : $module['label'];
447          $name = __(empty($module['name']) ? $label : $module['name']);
448
449          return array_merge(
450               # Default values
451               array(
452                    'desc'                   => '',
453                    'author'            => '',
454                    'version'                => 0,
455                    'current_version'   => 0,
456                    'root'                   => '',
457                    'root_writable'     => false,
458                    'permissions'       => null,
459                    'parent'            => null,
460                    'priority'               => 1000,
461                    'standalone_config' => false,
462                    'support'                => '',
463                    'section'                => '',
464                    'tags'                   => '',
465                    'details'                => '',
466                    'sshot'             => '',
467                    'score'                  => 0,
468                    'type'                   => null,
469                    'require'           => array(),
470                    'settings'               => array()
471               ),
472               # Module's values
473               $module,
474               # Clean up values
475               array(
476                    'id'                     => $id,
477                    'sid'                    => self::sanitizeString($id),
478                    'label'             => $label,
479                    'name'                   => $name,
480                    'sname'             => self::sanitizeString($name)
481               )
482          );
483     }
484
485     /**
486      * Check if a module is part of the distribution.
487      *
488      * @param string    $id       Module root directory
489      * @return     True if module is part of the distribution
490      */
491     public static function isDistributedModule($id)
492     {
493          $distributed_modules = self::$distributed_modules;
494
495          return is_array($distributed_modules) && in_array($id, $distributed_modules);
496     }
497
498     /**
499      * Sort modules list by specific field.
500      *
501      * @param string    $module        Array of modules
502      * @param string    $field         Field to sort from
503      * @param bollean   $asc      Sort asc if true, else decs
504      * @return     Array of sorted modules
505      */
506     public static function sortModules($modules, $field, $asc=true)
507     {
508          $origin = $sorter = array();
509
510          foreach($modules as $id => $module) {
511               $origin[] = $module;
512               $sorter[] = isset($module[$field]) ? $module[$field] : $field;
513          }
514
515          array_multisort($sorter, $asc ? SORT_ASC : SORT_DESC, $origin);
516
517          foreach($origin as $module) {
518               $final[$module['id']] = $module;
519          }
520
521          return $final;
522     }
523
524     /**
525      * Display list of modules.
526      *
527      * @param array     $cols          List of colones (module field) to display
528      * @param array     $actions  List of predefined actions to show on form
529      * @param boolean   $nav_limit     Limit list to previously selected index
530      * @return     adminModulesList self instance
531      */
532     public function displayModules($cols=array('name', 'version', 'desc'), $actions=array(), $nav_limit=false)
533     {
534          echo
535          '<form action="'.$this->getURL().'" method="post" class="modules-form-actions">'.
536          '<div class="table-outer">'.
537          '<table id="'.html::escapeHTML($this->list_id).'" class="modules'.(in_array('expander', $cols) ? ' expandable' : '').'">'.
538          '<caption class="hidden">'.html::escapeHTML(__('Plugins list')).'</caption><tr>';
539
540          if (in_array('name', $cols)) {
541               $colspan = 1;
542               if (in_array('checkbox', $cols)) {
543                    $colspan++;
544               }
545               if (in_array('icon', $cols)) {
546                    $colspan++;
547               }
548               echo
549               '<th class="first nowrap"'.($colspan > 1 ? ' colspan="'.$colspan.'"' : '').'>'.__('Name').'</th>';
550          }
551
552          if (in_array('score', $cols) && $this->getSearch() !== null && defined('DC_DEBUG') && DC_DEBUG) {
553               echo
554               '<th class="nowrap">'.__('Score').'</th>';
555          }
556
557          if (in_array('version', $cols)) {
558               echo
559               '<th class="nowrap count" scope="col">'.__('Version').'</th>';
560          }
561
562          if (in_array('current_version', $cols)) {
563               echo
564               '<th class="nowrap count" scope="col">'.__('Current version').'</th>';
565          }
566
567          if (in_array('desc', $cols)) {
568               echo
569               '<th class="nowrap" scope="col">'.__('Details').'</th>';
570          }
571
572          if (in_array('distrib', $cols)) {
573               echo
574               '<th'.(in_array('desc', $cols) ? '' : ' class="maximal"').'></th>';
575          }
576
577          if (!empty($actions) && $this->core->auth->isSuperAdmin()) {
578               echo
579               '<th class="minimal nowrap">'.__('Action').'</th>';
580          }
581
582          echo
583          '</tr>';
584
585          $sort_field = $this->getSort();
586
587          # Sort modules by $sort_field (default sname)
588          $modules = $this->getSearch() === null ?
589               self::sortModules($this->data, $sort_field, $this->sort_asc) :
590               $this->data;
591
592          $count = 0;
593          foreach ($modules as $id => $module)
594          {
595               # Show only requested modules
596               if ($nav_limit && $this->getSearch() === null) {
597                    $char = substr($module[$sort_field], 0, 1);
598                    if (!in_array($char, $this->nav_list)) {
599                         $char = $this->nav_special;
600                    }
601                    if ($this->getIndex() != $char) {
602                         continue;
603                    }
604               }
605
606               echo
607               '<tr class="line" id="'.html::escapeHTML($this->list_id).'_m_'.html::escapeHTML($id).'">';
608
609               $tds = 0;
610
611               if (in_array('checkbox', $cols)) {
612                    $tds++;
613                    echo
614                    '<td class="module-icon nowrap">'.
615                    form::checkbox(array('modules['.$count.']', html::escapeHTML($this->list_id).'_modules_'.html::escapeHTML($id)), html::escapeHTML($id)).
616                    '</td>';
617               }
618
619               if (in_array('icon', $cols)) {
620                    $tds++;
621                    echo
622                    '<td class="module-icon nowrap">'.sprintf(
623                         '<img alt="%1$s" title="%1$s" src="%2$s" />',
624                         html::escapeHTML($id), file_exists($module['root'].'/icon.png') ?
625                         dcPage::getPF($id.'/icon.png') : 'images/module.png'
626                    ).'</td>';
627               }
628
629               $tds++;
630               echo
631               '<td class="module-name nowrap" scope="row">';
632               if (in_array('checkbox', $cols)) {
633                    if (in_array('expander', $cols)) {
634                         echo
635                         html::escapeHTML($module['name']);
636                    }
637                    else {
638                         echo
639                         '<label for="'.html::escapeHTML($this->list_id).'_modules_'.html::escapeHTML($id).'">'.
640                         html::escapeHTML($module['name']).
641                         '</label>';
642                    }
643               }
644               else {
645                    echo
646                    html::escapeHTML($module['name']).
647                    form::hidden(array('modules['.$count.']'), html::escapeHTML($id));
648               }
649               echo
650               $this->core->formNonce().
651               '</td>';
652
653               # Display score only for debug purpose
654               if (in_array('score', $cols) && $this->getSearch() !== null && defined('DC_DEBUG') && DC_DEBUG) {
655                    $tds++;
656                    echo
657                    '<td class="module-version nowrap count"><span class="debug">'.$module['score'].'</span></td>';
658               }
659
660               if (in_array('version', $cols)) {
661                    $tds++;
662                    echo
663                    '<td class="module-version nowrap count">'.html::escapeHTML($module['version']).'</td>';
664               }
665
666               if (in_array('current_version', $cols)) {
667                    $tds++;
668                    echo
669                    '<td class="module-current-version nowrap count">'.html::escapeHTML($module['current_version']).'</td>';
670               }
671
672               if (in_array('desc', $cols)) {
673                    $tds++;
674                    echo
675                    '<td class="module-desc maximal">'.html::escapeHTML(__($module['desc']));
676                    if (isset($module['cannot_disable']) && $module['enabled']) {
677                         echo
678                         '<br/><span class="info">'.
679                         sprintf(__('This module cannot be disabled nor deleted, since the following modules are also enabled : %s'),
680                                   join(',',$module['cannot_disable'])).
681                         '</span>';
682                    }
683                    if (isset($module['cannot_enable']) && !$module['enabled']) {
684                         echo
685                         '<br/><span class="info">'.
686                         __('This module cannot be enabled, because of the following reasons :').
687                         '<ul>';
688                         foreach ($module['cannot_enable'] as $m=>$reason) {
689                              echo '<li>'.$reason.'</li>';
690                         }
691                         echo '</ul>'.
692                         '</span>';
693                    }
694                    echo '</td>';
695
696               }
697
698               if (in_array('distrib', $cols)) {
699                    $tds++;
700                    echo
701                    '<td class="module-distrib">'.(self::isDistributedModule($id) ?
702                         '<img src="images/dotclear_pw.png" alt="'.
703                         __('Plugin from official distribution').'" title="'.
704                         __('Plugin from official distribution').'" />'
705                    : '').'</td>';
706               }
707
708               if (!empty($actions) && $this->core->auth->isSuperAdmin()) {
709                    $buttons = $this->getActions($id, $module, $actions);
710
711                    $tds++;
712                    echo
713                    '<td class="module-actions nowrap">'.
714
715                    '<div>'.implode(' ', $buttons).'</div>'.
716
717                    '</td>';
718               }
719
720               echo
721               '</tr>';
722
723               # Other informations
724               if (in_array('expander', $cols)) {
725                    echo
726                    '<tr class="module-more"><td colspan="'.$tds.'" class="expand">';
727
728                    if (!empty($module['author']) || !empty($module['details']) || !empty($module['support'])) {
729                         echo
730                         '<div><ul class="mod-more">';
731
732                         if (!empty($module['author'])) {
733                              echo
734                              '<li class="module-author">'.__('Author:').' '.html::escapeHTML($module['author']).'</li>';
735                         }
736
737                         $more = array();
738                         if (!empty($module['details'])) {
739                              $more[] = '<a class="module-details" href="'.$module['details'].'">'.__('Details').'</a>';
740                         }
741
742                         if (!empty($module['support'])) {
743                              $more[] = '<a class="module-support" href="'.$module['support'].'">'.__('Support').'</a>';
744                         }
745
746                         if (!empty($more)) {
747                              echo
748                              '<li>'.implode(' - ',$more).'</li>';
749                         }
750
751                         echo
752                         '</ul></div>';
753                    }
754
755                    $config = !empty($module['root']) && file_exists(path::real($module['root'].'/_config.php'));
756                    $index = !empty($module['root']) && file_exists(path::real($module['root'].'/index.php'));
757
758                    if ($config || $index || !empty($module['section']) || !empty($module['tags']) || !empty($module['settings'])) {
759                         echo
760                         '<div><ul class="mod-more">';
761
762                         if ($index && $module['enabled']) {
763                              echo '<li><a href="'.$this->core->adminurl->get('admin.plugin.'.$id).'">'.__('Manage plugin').'</a></li>';
764                         }
765
766                         $settings = $this->getSettingsUrls($this->core,$id);
767                         if (!empty($settings) && $module['enabled']) {
768                              echo '<li>'.implode(' - ',$settings).'</li>';
769                         }
770
771                         if (!empty($module['section'])) {
772                              echo
773                              '<li class="module-section">'.__('Section:').' '.html::escapeHTML($module['section']).'</li>';
774                         }
775
776                         if (!empty($module['tags'])) {
777                              echo
778                              '<li class="module-tags">'.__('Tags:').' '.html::escapeHTML($module['tags']).'</li>';
779                         }
780
781                         echo
782                         '</ul></div>';
783                    }
784
785                    echo
786                    '</td></tr>';
787               }
788
789               $count++;
790          }
791          echo
792          '</table></div>';
793
794          if(!$count && $this->getSearch() === null) {
795               echo
796               '<p class="message">'.__('No plugins matched your search.').'</p>';
797          }
798
799          elseif ((in_array('checkbox', $cols) || $count > 1) && !empty($actions) && $this->core->auth->isSuperAdmin()) {
800               $buttons = $this->getGlobalActions($actions, in_array('checkbox', $cols));
801
802               if (!empty($buttons)) {
803                    if (in_array('checkbox', $cols)) {
804                         echo
805                         '<p class="checkboxes-helpers"></p>';
806                    }
807                    echo
808                    '<div>'.implode(' ', $buttons).'</div>';
809               }
810          }
811          echo
812          '</form>';
813
814          return $this;
815     }
816
817     /**
818      * Get settings URLs if any
819      *
820      * @param object $core
821      * @param string $id module ID
822      * @param boolean $check check permission
823      * @param boolean $self include self URL (→ plugin index.php URL)
824      * @return Array of settings URLs
825      */
826     public static function getSettingsUrls($core,$id,$check=false,$self=true)
827     {
828          $st = array();
829
830          $config = !empty($core->plugins->moduleRoot($id)) &&
831               file_exists(path::real($core->plugins->moduleRoot($id).'/_config.php'));
832          $settings = $core->plugins->moduleInfo($id,'settings');
833          if ($config || !empty($settings)) {
834               if ($config) {
835                    if (!$check ||
836                         $core->auth->isSuperAdmin() ||
837                         $core->auth->check($core->plugins->moduleInfo($id,'permissions'),$core->blog->id))
838                    {
839                         $params = array('module' => $id,'conf' => '1');
840                         if (!$core->plugins->moduleInfo($id,'standalone_config') && !$self) {
841                              $params['redir'] = $core->adminurl->get('admin.plugin.'.$id);
842                         }
843                         $st[] = '<a class="module-config" href="'.
844                              $core->adminurl->get('admin.plugins',$params).
845                              '">'.__('Configure plugin').'</a>';
846                    }
847               }
848               if (is_array($settings)) {
849                    foreach ($settings as $sk => $sv) {
850                         switch ($sk) {
851                              case 'blog':
852                                   if (!$check ||
853                                        $core->auth->isSuperAdmin() ||
854                                        $core->auth->check('admin',$core->blog->id))
855                                   {
856                                        $st[] = '<a class="module-config" href="'.
857                                             $core->adminurl->get('admin.blog.pref').$sv.
858                                             '">'.__('Plugin settings (in blog parameters)').'</a>';
859                                   }
860                                   break;
861                              case 'pref':
862                                   if (!$check ||
863                                        $core->auth->isSuperAdmin() ||
864                                        $core->auth->check('usage,contentadmin',$core->blog->id))
865                                   {
866                                        $st[] = '<a class="module-config" href="'.
867                                             $core->adminurl->get('admin.user.preferences').$sv.
868                                             '">'.__('Plugin settings (in user preferences)').'</a>';
869                                   }
870                                   break;
871                              case 'self':
872                                   if ($self) {
873                                        if (!$check ||
874                                             $core->auth->isSuperAdmin() ||
875                                             $core->auth->check($core->plugins->moduleInfo($id,'permissions'),$core->blog->id))
876                                        {
877                                             $st[] = '<a class="module-config" href="'.
878                                                  $core->adminurl->get('admin.plugin.'.$id).$sv.
879                                                  '">'.__('Plugin settings').'</a>';
880                                        }
881                                   }
882                                   break;
883                         }
884                    }
885               }
886          }
887
888          return $st;
889     }
890
891     /**
892      * Get action buttons to add to modules list.
893      *
894      * @param string    $id            Module ID
895      * @param array     $module        Module info
896      * @param array     $actions  Actions keys
897      * @return     Array of actions buttons
898      */
899     protected function getActions($id, $module, $actions)
900     {
901          $submits = array();
902
903          # Use loop to keep requested order
904          foreach($actions as $action) {
905               switch($action) {
906
907                    # Deactivate
908                    case 'activate': if ($this->core->auth->isSuperAdmin() && $module['root_writable'] && !isset($module['cannot_enable'])) {
909                         $submits[] =
910                         '<input type="submit" name="activate['.html::escapeHTML($id).']" value="'.__('Activate').'" />';
911                    } break;
912
913                    # Activate
914                    case 'deactivate': if ($this->core->auth->isSuperAdmin() && $module['root_writable'] && !isset($module['cannot_disable'])) {
915                         $submits[] =
916                         '<input type="submit" name="deactivate['.html::escapeHTML($id).']" value="'.__('Deactivate').'" class="reset" />';
917                    } break;
918
919                    # Delete
920                    case 'delete': if ($this->core->auth->isSuperAdmin() && $this->isDeletablePath($module['root'])&& !isset($module['cannot_disable'])) {
921                         $dev = !preg_match('!^'.$this->path_pattern.'!', $module['root']) && defined('DC_DEV') && DC_DEV ? ' debug' : '';
922                         $submits[] =
923                         '<input type="submit" class="delete '.$dev.'" name="delete['.html::escapeHTML($id).']" value="'.__('Delete').'" />';
924                    } break;
925
926                    # Install (from store)
927                    case 'install': if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
928                         $submits[] =
929                         '<input type="submit" name="install['.html::escapeHTML($id).']" value="'.__('Install').'" />';
930                    } break;
931
932                    # Update (from store)
933                    case 'update': if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
934                         $submits[] =
935                         '<input type="submit" name="update['.html::escapeHTML($id).']" value="'.__('Update').'" />';
936                    } break;
937
938                    # Behavior
939                    case 'behavior':
940
941                         # --BEHAVIOR-- adminModulesListGetActions
942                         $tmp = $this->core->callBehavior('adminModulesListGetActions', $this, $id, $module);
943
944                         if (!empty($tmp)) {
945                              $submits[] = $tmp;
946                         }
947                    break;
948               }
949          }
950
951          return $submits;
952     }
953
954     /**
955      * Get global action buttons to add to modules list.
956      *
957      * @param array     $actions        Actions keys
958      * @param boolean   $with_selection Limit action to selected modules
959      * @return     Array of actions buttons
960      */
961     protected function getGlobalActions($actions, $with_selection=false)
962     {
963          $submits = array();
964
965          # Use loop to keep requested order
966          foreach($actions as $action) {
967               switch($action) {
968
969                    # Deactivate
970                    case 'activate': if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
971                         $submits[] =
972                         '<input type="submit" name="activate" value="'.($with_selection ?
973                              __('Activate selected plugins') :
974                              __('Activate all plugins from this list')
975                         ).'" />';
976                    } break;
977
978                    # Activate
979                    case 'deactivate': if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
980                         $submits[] =
981                         '<input type="submit" name="deactivate" value="'.($with_selection ?
982                              __('Deactivate selected plugins') :
983                              __('Deactivate all plugins from this list')
984                         ).'" />';
985                    } break;
986
987                    # Update (from store)
988                    case 'update': if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
989                         $submits[] =
990                         '<input type="submit" name="update" value="'.($with_selection ?
991                              __('Update selected plugins') :
992                              __('Update all plugins from this list')
993                         ).'" />';
994                    } break;
995
996                    # Behavior
997                    case 'behavior':
998
999                         # --BEHAVIOR-- adminModulesListGetGlobalActions
1000                         $tmp = $this->core->callBehavior('adminModulesListGetGlobalActions', $this, $with_selection);
1001
1002                         if (!empty($tmp)) {
1003                              $submits[] = $tmp;
1004                         }
1005                    break;
1006               }
1007          }
1008
1009          return $submits;
1010     }
1011
1012     /**
1013      * Execute POST action.
1014      *
1015      * @note  Set a notice on success through dcPage::addSuccessNotice
1016      * @throw Exception Module not find or command failed
1017      * @return     Null
1018      */
1019     public function doActions()
1020     {
1021          if (empty($_POST) || !empty($_REQUEST['conf'])
1022          || !$this->isWritablePath()) {
1023               return null;
1024          }
1025
1026          $modules = !empty($_POST['modules']) && is_array($_POST['modules']) ? array_values($_POST['modules']) : array();
1027
1028          if ($this->core->auth->isSuperAdmin() && !empty($_POST['delete'])) {
1029
1030               if (is_array($_POST['delete'])) {
1031                    $modules = array_keys($_POST['delete']);
1032               }
1033
1034               $list = $this->modules->getDisabledModules();
1035
1036               $failed = false;
1037               $count = 0;
1038               foreach($modules as $id)
1039               {
1040                    if (!isset($list[$id])) {
1041
1042                         if (!$this->modules->moduleExists($id)) {
1043                              throw new Exception(__('No such plugin.'));
1044                         }
1045
1046                         $module = $this->modules->getModules($id);
1047                         $module['id'] = $id;
1048
1049                         if (!$this->isDeletablePath($module['root'])) {
1050                              $failed = true;
1051                              continue;
1052                         }
1053
1054                         # --BEHAVIOR-- moduleBeforeDelete
1055                         $this->core->callBehavior('pluginBeforeDelete', $module);
1056
1057                         $this->modules->deleteModule($id);
1058
1059                         # --BEHAVIOR-- moduleAfterDelete
1060                         $this->core->callBehavior('pluginAfterDelete', $module);
1061                    }
1062                    else {
1063                         $this->modules->deleteModule($id, true);
1064                    }
1065
1066                    $count++;
1067               }
1068
1069               if (!$count && $failed) {
1070                    throw new Exception(__("You don't have permissions to delete this plugin."));
1071               }
1072               elseif ($failed) {
1073                    dcPage::addWarningNotice(__('Some plugins have not been delete.'));
1074               }
1075               else {
1076                    dcPage::addSuccessNotice(
1077                         __('Plugin has been successfully deleted.', 'Plugins have been successuflly deleted.', $count)
1078                    );
1079               }
1080               http::redirect($this->getURL());
1081          }
1082
1083          elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['install'])) {
1084
1085               if (is_array($_POST['install'])) {
1086                    $modules = array_keys($_POST['install']);
1087               }
1088
1089               $list = $this->store->get();
1090
1091               if (empty($list)) {
1092                    throw new Exception(__('No such plugin.'));
1093               }
1094
1095               $count = 0;
1096               foreach($list as $id => $module) {
1097
1098                    if (!in_array($id, $modules)) {
1099                         continue;
1100                    }
1101
1102                    $dest = $this->getPath().'/'.basename($module['file']);
1103
1104                    # --BEHAVIOR-- moduleBeforeAdd
1105                    $this->core->callBehavior('pluginBeforeAdd', $module);
1106
1107                    $this->store->process($module['file'], $dest);
1108
1109                    # --BEHAVIOR-- moduleAfterAdd
1110                    $this->core->callBehavior('pluginAfterAdd', $module);
1111
1112                    $count++;
1113               }
1114
1115               dcPage::addSuccessNotice(
1116                    __('Plugin has been successfully installed.', 'Plugins have been successuflly installed.', $count)
1117               );
1118               http::redirect($this->getURL());
1119          }
1120
1121          elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['activate'])) {
1122
1123               if (is_array($_POST['activate'])) {
1124                    $modules = array_keys($_POST['activate']);
1125               }
1126
1127               $list = $this->modules->getDisabledModules();
1128               if (empty($list)) {
1129                    throw new Exception(__('No such plugin.'));
1130               }
1131
1132               $count = 0;
1133               foreach($list as $id => $module) {
1134
1135                    if (!in_array($id, $modules)) {
1136                         continue;
1137                    }
1138
1139                    # --BEHAVIOR-- moduleBeforeActivate
1140                    $this->core->callBehavior('pluginBeforeActivate', $id);
1141
1142                    $this->modules->activateModule($id);
1143
1144                    # --BEHAVIOR-- moduleAfterActivate
1145                    $this->core->callBehavior('pluginAfterActivate', $id);
1146
1147                    $count++;
1148               }
1149
1150               dcPage::addSuccessNotice(
1151                    __('Plugin has been successfully activated.', 'Plugins have been successuflly activated.', $count)
1152               );
1153               http::redirect($this->getURL());
1154          }
1155
1156          elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['deactivate'])) {
1157
1158               if (is_array($_POST['deactivate'])) {
1159                    $modules = array_keys($_POST['deactivate']);
1160               }
1161
1162               $list = $this->modules->getModules();
1163               if (empty($list)) {
1164                    throw new Exception(__('No such plugin.'));
1165               }
1166
1167               $failed = false;
1168               $count = 0;
1169               foreach($list as $id => $module) {
1170
1171                    if (!in_array($id, $modules)) {
1172                         continue;
1173                    }
1174
1175                    if (!$module['root_writable']) {
1176                         $failed = true;
1177                         continue;
1178                    }
1179
1180                    $module[$id] = $id;
1181
1182                    # --BEHAVIOR-- moduleBeforeDeactivate
1183                    $this->core->callBehavior('pluginBeforeDeactivate', $module);
1184
1185                    $this->modules->deactivateModule($id);
1186
1187                    # --BEHAVIOR-- moduleAfterDeactivate
1188                    $this->core->callBehavior('pluginAfterDeactivate', $module);
1189
1190                    $count++;
1191               }
1192
1193               if ($failed) {
1194                    dcPage::addWarningNotice(__('Some plugins have not been deactivated.'));
1195               }
1196               else {
1197                    dcPage::addSuccessNotice(
1198                         __('Plugin has been successfully deactivated.', 'Plugins have been successuflly deactivated.', $count)
1199                    );
1200               }
1201               http::redirect($this->getURL());
1202          }
1203
1204          elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['update'])) {
1205
1206               if (is_array($_POST['update'])) {
1207                    $modules = array_keys($_POST['update']);
1208               }
1209
1210               $list = $this->store->get(true);
1211               if (empty($list)) {
1212                    throw new Exception(__('No such plugin.'));
1213               }
1214
1215               $count = 0;
1216               foreach($list as $module) {
1217
1218                    if (!in_array($module['id'], $modules)) {
1219                         continue;
1220                    }
1221
1222                    if (!self::$allow_multi_install) {
1223                         $dest = $module['root'].'/../'.basename($module['file']);
1224                    }
1225                    else {
1226                         $dest = $this->getPath().'/'.basename($module['file']);
1227                         if ($module['root'] != $dest) {
1228                              @file_put_contents($module['root'].'/_disabled', '');
1229                         }
1230                    }
1231
1232                    # --BEHAVIOR-- moduleBeforeUpdate
1233                    $this->core->callBehavior('pluginBeforeUpdate', $module);
1234
1235                    $this->store->process($module['file'], $dest);
1236
1237                    # --BEHAVIOR-- moduleAfterUpdate
1238                    $this->core->callBehavior('pluginAfterUpdate', $module);
1239
1240                    $count++;
1241               }
1242
1243               $tab = $count && $count == count($list) ? '#plugins' : '#update';
1244
1245               dcPage::addSuccessNotice(
1246                    __('Plugin has been successfully updated.', 'Plugins have been successuflly updated.', $count)
1247               );
1248               http::redirect($this->getURL().$tab);
1249          }
1250
1251          # Manual actions
1252          elseif (!empty($_POST['upload_pkg']) && !empty($_FILES['pkg_file'])
1253               || !empty($_POST['fetch_pkg']) && !empty($_POST['pkg_url']))
1254          {
1255               if (empty($_POST['your_pwd']) || !$this->core->auth->checkPassword($this->core->auth->crypt($_POST['your_pwd']))) {
1256                    throw new Exception(__('Password verification failed'));
1257               }
1258
1259               if (!empty($_POST['upload_pkg'])) {
1260                    files::uploadStatus($_FILES['pkg_file']);
1261
1262                    $dest = $this->getPath().'/'.$_FILES['pkg_file']['name'];
1263                    if (!move_uploaded_file($_FILES['pkg_file']['tmp_name'], $dest)) {
1264                         throw new Exception(__('Unable to move uploaded file.'));
1265                    }
1266               }
1267               else {
1268                    $url = urldecode($_POST['pkg_url']);
1269                    $dest = $this->getPath().'/'.basename($url);
1270                    $this->store->download($url, $dest);
1271               }
1272
1273               # --BEHAVIOR-- moduleBeforeAdd
1274               $this->core->callBehavior('pluginBeforeAdd', null);
1275
1276               $ret_code = $this->store->install($dest);
1277
1278               # --BEHAVIOR-- moduleAfterAdd
1279               $this->core->callBehavior('pluginAfterAdd', null);
1280
1281               dcPage::addSuccessNotice($ret_code == 2 ?
1282                    __('Plugin has been successfully updated.') :
1283                    __('Plugin has been successfully installed.')
1284               );
1285               http::redirect($this->getURL().'#plugins');
1286          }
1287
1288          else {
1289
1290               # --BEHAVIOR-- adminModulesListDoActions
1291               $this->core->callBehavior('adminModulesListDoActions', $this, $modules, 'plugin');
1292
1293          }
1294
1295          return null;
1296     }
1297
1298     /**
1299      * Display tab for manual installation.
1300      *
1301      * @return     adminModulesList self instance
1302      */
1303     public function displayManualForm()
1304     {
1305          if (!$this->core->auth->isSuperAdmin() || !$this->isWritablePath()) {
1306               return null;
1307          }
1308
1309          # 'Upload module' form
1310          echo
1311          '<form method="post" action="'.$this->getURL().'" id="uploadpkg" enctype="multipart/form-data" class="fieldset">'.
1312          '<h4>'.__('Upload a zip file').'</h4>'.
1313          '<p class="field"><label for="pkg_file" class="classic required"><abbr title="'.__('Required field').'">*</abbr> '.__('Zip file path:').'</label> '.
1314          '<input type="file" name="pkg_file" id="pkg_file" /></p>'.
1315          '<p class="field"><label for="your_pwd1" class="classic required"><abbr title="'.__('Required field').'">*</abbr> '.__('Your password:').'</label> '.
1316          form::password(array('your_pwd','your_pwd1'),20,255).'</p>'.
1317          '<p><input type="submit" name="upload_pkg" value="'.__('Upload').'" />'.
1318          $this->core->formNonce().'</p>'.
1319          '</form>';
1320
1321          # 'Fetch module' form
1322          echo
1323          '<form method="post" action="'.$this->getURL().'" id="fetchpkg" class="fieldset">'.
1324          '<h4>'.__('Download a zip file').'</h4>'.
1325          '<p class="field"><label for="pkg_url" class="classic required"><abbr title="'.__('Required field').'">*</abbr> '.__('Zip file URL:').'</label> '.
1326          form::field(array('pkg_url','pkg_url'),40,255).'</p>'.
1327          '<p class="field"><label for="your_pwd2" class="classic required"><abbr title="'.__('Required field').'">*</abbr> '.__('Your password:').'</label> '.
1328          form::password(array('your_pwd','your_pwd2'),20,255).'</p>'.
1329          '<p><input type="submit" name="fetch_pkg" value="'.__('Download').'" />'.
1330          $this->core->formNonce().'</p>'.
1331          '</form>';
1332
1333          return $this;
1334     }
1335     //@}
1336
1337     /// @name Module configuration methods
1338     //@{
1339     /**
1340      * Prepare module configuration.
1341      *
1342      * We need to get configuration content in three steps
1343      * and out of this class to keep backward compatibility.
1344      *
1345      * if ($xxx->setConfiguration()) {
1346      *   include $xxx->includeConfiguration();
1347      * }
1348      * $xxx->getConfiguration();
1349      * ... [put here page headers and other stuff]
1350      * $xxx->displayConfiguration();
1351      *
1352      * @param string    $id       Module to work on or it gather through REQUEST
1353      * @return     True if config set
1354      */
1355     public function setConfiguration($id=null)
1356     {
1357          if (empty($_REQUEST['conf']) || empty($_REQUEST['module']) && !$id) {
1358               return false;
1359          }
1360
1361          if (!empty($_REQUEST['module']) && empty($id)) {
1362               $id = $_REQUEST['module'];
1363          }
1364
1365          if (!$this->modules->moduleExists($id)) {
1366               $this->core->error->add(__('Unknow plugin ID'));
1367               return false;
1368          }
1369
1370          $module = $this->modules->getModules($id);
1371          $module = self::sanitizeModule($id, $module);
1372          $file = path::real($module['root'].'/_config.php');
1373
1374          if (!file_exists($file)) {
1375               $this->core->error->add(__('This plugin has no configuration file.'));
1376               return false;
1377          }
1378
1379          $this->config_module = $module;
1380          $this->config_file = $file;
1381          $this->config_content = '';
1382
1383          if (!defined('DC_CONTEXT_MODULE')) {
1384               define('DC_CONTEXT_MODULE', true);
1385          }
1386
1387          return true;
1388     }
1389
1390     /**
1391      * Get path of module configuration file.
1392      *
1393      * @note Required previously set file info
1394      * @return Full path of config file or null
1395      */
1396     public function includeConfiguration()
1397     {
1398          if (!$this->config_file) {
1399               return null;
1400          }
1401          $this->setRedir($this->getURL().'#plugins');
1402
1403          ob_start();
1404
1405          return $this->config_file;
1406     }
1407
1408     /**
1409      * Gather module configuration file content.
1410      *
1411      * @note Required previously file inclusion
1412      * @return True if content has been captured
1413      */
1414     public function getConfiguration()
1415     {
1416          if ($this->config_file) {
1417               $this->config_content = ob_get_contents();
1418          }
1419
1420          ob_end_clean();
1421
1422          return !empty($this->file_content);
1423     }
1424
1425     /**
1426      * Display module configuration form.
1427      *
1428      * @note Required previously gathered content
1429      * @return     adminModulesList self instance
1430      */
1431     public function displayConfiguration()
1432     {
1433          if ($this->config_file) {
1434
1435               if (!$this->config_module['standalone_config']) {
1436                    echo
1437                    '<form id="module_config" action="'.$this->getURL('conf=1').'" method="post" enctype="multipart/form-data">'.
1438                    '<h3>'.sprintf(__('Configure "%s"'), html::escapeHTML($this->config_module['name'])).'</h3>'.
1439                    '<p><a class="back" href="'.$this->getRedir().'">'.__('Back').'</a></p>';
1440               }
1441
1442               echo $this->config_content;
1443
1444               if (!$this->config_module['standalone_config']) {
1445                    echo
1446                    '<p class="clear"><input type="submit" name="save" value="'.__('Save').'" />'.
1447                    form::hidden('module', $this->config_module['id']).
1448                    form::hidden('redir', $this->getRedir()).
1449                    $this->core->formNonce().'</p>'.
1450                    '</form>';
1451               }
1452          }
1453
1454          return $this;
1455     }
1456     //@}
1457
1458     /**
1459      * Helper to sanitize a string.
1460      *
1461      * Used for search or id.
1462      *
1463      * @param string    $str      String to sanitize
1464      * @return     Sanitized string
1465      */
1466     public static function sanitizeString($str)
1467     {
1468          return preg_replace('/[^A-Za-z0-9\@\#+_-]/', '', strtolower($str));
1469     }
1470}
1471
1472/**
1473 * @ingroup DC_CORE
1474 * @brief Helper to manage list of themes.
1475 * @since 2.6
1476 */
1477class adminThemesList extends adminModulesList
1478{
1479     /**
1480      * Constructor.
1481      *
1482      * Note that this creates dcStore instance.
1483      *
1484      * @param object    $modules       dcModules instance
1485      * @param string    $modules_root  Modules root directories
1486      * @param string    $xml_url       URL of modules feed from repository
1487      */
1488     public function __construct(dcModules $modules, $modules_root, $xml_url)
1489     {
1490          parent::__construct($modules, $modules_root, $xml_url);
1491          $this->page_url = $this->core->adminurl->get('admin.blog.theme');
1492     }
1493
1494     public function displayModules($cols=array('name', 'config', 'version', 'desc'), $actions=array(), $nav_limit=false)
1495     {
1496          echo
1497          '<form action="'.$this->getURL().'" method="post" class="modules-form-actions">'.
1498          '<div id="'.html::escapeHTML($this->list_id).'" class="modules'.(in_array('expander', $cols) ? ' expandable' : '').' one-box">';
1499
1500          $sort_field = $this->getSort();
1501
1502          # Sort modules by id
1503          $modules = $this->getSearch() === null ?
1504               self::sortModules($this->data, $sort_field, $this->sort_asc) :
1505               $this->data;
1506
1507          $res = '';
1508          $count = 0;
1509          foreach ($modules as $id => $module)
1510          {
1511               # Show only requested modules
1512               if ($nav_limit && $this->getSearch() === null) {
1513                    $char = substr($module[$sort_field], 0, 1);
1514                    if (!in_array($char, $this->nav_list)) {
1515                         $char = $this->nav_special;
1516                    }
1517                    if ($this->getIndex() != $char) {
1518                         continue;
1519                    }
1520               }
1521
1522               $current = $this->core->blog->settings->system->theme == $id && $this->modules->moduleExists($id);
1523               $distrib = self::isDistributedModule($id) ? ' dc-box' : '';
1524
1525               $line =
1526               '<div class="box '.($current ? 'medium current-theme' : 'theme').$distrib.'">';
1527
1528               if (in_array('name', $cols) && !$current) {
1529                    $line .=
1530                    '<h4 class="module-name">';
1531
1532                    if (in_array('checkbox', $cols)) {
1533                         $line .=
1534                         '<label for="'.html::escapeHTML($this->list_id).'_modules_'.html::escapeHTML($id).'">'.
1535                         form::checkbox(array('modules['.$count.']', html::escapeHTML($this->list_id).'_modules_'.html::escapeHTML($id)), html::escapeHTML($id)).
1536                         html::escapeHTML($module['name']).
1537                         '</label>';
1538
1539                    }
1540                    else {
1541                         $line .=
1542                         form::hidden(array('modules['.$count.']'), html::escapeHTML($id)).
1543                         html::escapeHTML($module['name']);
1544                    }
1545
1546                    $line .=
1547                    $this->core->formNonce().
1548                    '</h4>';
1549               }
1550
1551               # Display score only for debug purpose
1552               if (in_array('score', $cols) && $this->getSearch() !== null && defined('DC_DEBUG') && DC_DEBUG) {
1553                    $line .=
1554                    '<p class="module-score debug">'.sprintf(__('Score: %s'), $module['score']).'</p>';
1555               }
1556
1557               if (in_array('sshot', $cols)) {
1558                    # Screenshot from url
1559                    if (preg_match('#^http(s)?://#', $module['sshot'])) {
1560                         $sshot = $module['sshot'];
1561                    }
1562                    # Screenshot from installed module
1563                    elseif (file_exists($this->core->blog->themes_path.'/'.$id.'/screenshot.jpg')) {
1564                         $sshot = $this->getURL('shot='.rawurlencode($id));
1565                    }
1566                    # Default screenshot
1567                    else {
1568                         $sshot = 'images/noscreenshot.png';
1569                    }
1570
1571                    $line .=
1572                    '<div class="module-sshot"><img src="'.$sshot.'" alt="'.
1573                    sprintf(__('%s screenshot.'), html::escapeHTML($module['name'])).'" /></div>';
1574               }
1575
1576               $line .=
1577               '<div class="module-infos toggle-bloc">';
1578
1579               if (in_array('name', $cols) && $current) {
1580                    $line .=
1581                    '<h4 class="module-name">';
1582
1583                    if (in_array('checkbox', $cols)) {
1584                         $line .=
1585                         '<label for="'.html::escapeHTML($this->list_id).'_modules_'.html::escapeHTML($id).'">'.
1586                         form::checkbox(array('modules['.$count.']', html::escapeHTML($this->list_id).'_modules_'.html::escapeHTML($id)), html::escapeHTML($id)).
1587                         html::escapeHTML($module['name']).
1588                         '</label>';
1589                    }
1590                    else {
1591                         $line .=
1592                         form::hidden(array('modules['.$count.']'), html::escapeHTML($id)).
1593                         html::escapeHTML($module['name']);
1594                    }
1595
1596                    $line .=
1597                    '</h4>';
1598               }
1599
1600               $line .=
1601               '<p>';
1602
1603               if (in_array('desc', $cols)) {
1604                    $line .=
1605                    '<span class="module-desc">'.html::escapeHTML(__($module['desc'])).'</span> ';
1606               }
1607
1608               if (in_array('author', $cols)) {
1609                    $line .=
1610                    '<span class="module-author">'.sprintf(__('by %s'),html::escapeHTML($module['author'])).'</span> ';
1611               }
1612
1613               if (in_array('version', $cols)) {
1614                    $line .=
1615                    '<span class="module-version">'.sprintf(__('version %s'),html::escapeHTML($module['version'])).'</span> ';
1616               }
1617
1618               if (in_array('current_version', $cols)) {
1619                    $line .=
1620                    '<span class="module-current-version">'.sprintf(__('(current version %s)'),html::escapeHTML($module['current_version'])).'</span> ';
1621               }
1622
1623               if (in_array('parent', $cols) && !empty($module['parent'])) {
1624                    if ($this->modules->moduleExists($module['parent'])) {
1625                         $line .=
1626                         '<span class="module-parent-ok">'.sprintf(__('(built on "%s")'),html::escapeHTML($module['parent'])).'</span> ';
1627                    }
1628                    else {
1629                         $line .=
1630                         '<span class="module-parent-missing">'.sprintf(__('(requires "%s")'),html::escapeHTML($module['parent'])).'</span> ';
1631                    }
1632               }
1633
1634               $has_details = in_array('details', $cols) && !empty($module['details']);
1635               $has_support = in_array('support', $cols) && !empty($module['support']);
1636               if ($has_details || $has_support) {
1637                    $line .=
1638                    '<span class="mod-more">';
1639
1640                    if ($has_details) {
1641                         $line .=
1642                         '<a class="module-details" href="'.$module['details'].'">'.__('Details').'</a>';
1643                    }
1644
1645                    if ($has_support) {
1646                         $line .=
1647                         ' - <a class="module-support" href="'.$module['support'].'">'.__('Support').'</a>';
1648                    }
1649
1650                    $line .=
1651                    '</span>';
1652               }
1653
1654               $line .=
1655               '</p>'.
1656               '</div>';
1657
1658               $line .=
1659               '<div class="module-actions toggle-bloc">';
1660
1661               # Plugins actions
1662               if ($current) {
1663
1664                    # _GET actions
1665                    if (file_exists(path::real($this->core->blog->themes_path.'/'.$id).'/style.css')) {
1666                         $theme_url = preg_match('#^http(s)?://#', $this->core->blog->settings->system->themes_url) ?
1667                              http::concatURL($this->core->blog->settings->system->themes_url, '/'.$id) :
1668                              http::concatURL($this->core->blog->url, $this->core->blog->settings->system->themes_url.'/'.$id);
1669                         $line .=
1670                         '<p><a href="'.$theme_url.'/style.css">'.__('View stylesheet').'</a></p>';
1671                    }
1672
1673                    $line .= '<div class="current-actions">';
1674
1675                    if (file_exists(path::real($this->core->blog->themes_path.'/'.$id).'/_config.php')) {
1676                         $line .=
1677                         '<p><a href="'.$this->getURL('module='.$id.'&amp;conf=1', false).'" class="button submit">'.__('Configure theme').'</a></p>';
1678                    }
1679
1680                    # --BEHAVIOR-- adminCurrentThemeDetails
1681                    $line .=
1682                    $this->core->callBehavior('adminCurrentThemeDetails', $this->core, $id, $module);
1683
1684                    $line .= '</div>';
1685               }
1686
1687               # _POST actions
1688               if (!empty($actions)) {
1689                    $line .=
1690                    '<p>'.implode(' ', $this->getActions($id, $module, $actions)).'</p>';
1691               }
1692
1693               $line .=
1694               '</div>';
1695
1696               $line .=
1697               '</div>';
1698
1699               $count++;
1700
1701               $res = $current ? $line.$res : $res.$line;
1702          }
1703
1704          echo
1705          $res.
1706          '</div>';
1707
1708          if(!$count && $this->getSearch() === null) {
1709               echo
1710               '<p class="message">'.__('No themes matched your search.').'</p>';
1711          }
1712
1713          elseif ((in_array('checkbox', $cols) || $count > 1) && !empty($actions) && $this->core->auth->isSuperAdmin()) {
1714               $buttons = $this->getGlobalActions($actions, in_array('checkbox', $cols));
1715
1716               if (!empty($buttons)) {
1717                    if (in_array('checkbox', $cols)) {
1718                         echo
1719                         '<p class="checkboxes-helpers"></p>';
1720                    }
1721                    echo '<div>'.implode(' ', $buttons).'</div>';
1722               }
1723          }
1724
1725          echo
1726          '</form>';
1727
1728          return $this;
1729     }
1730
1731     protected function getActions($id, $module, $actions)
1732     {
1733          $submits = array();
1734
1735          $this->core->blog->settings->addNamespace('system');
1736          if ($id != $this->core->blog->settings->system->theme) {
1737
1738               # Select theme to use on curent blog
1739               if (in_array('select', $actions)) {
1740                    $submits[] =
1741                    '<input type="submit" name="select['.html::escapeHTML($id).']" value="'.__('Use this one').'" />';
1742               }
1743          }
1744
1745          return array_merge(
1746               $submits,
1747               parent::getActions($id, $module, $actions)
1748          );
1749     }
1750
1751     protected function getGlobalActions($actions, $with_selection=false)
1752     {
1753          $submits = array();
1754
1755          foreach($actions as $action) {
1756               switch($action) {
1757
1758                    # Update (from store)
1759                    case 'update': if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
1760                         $submits[] =
1761                         '<input type="submit" name="update" value="'.($with_selection ?
1762                              __('Update selected themes') :
1763                              __('Update all themes from this list')
1764                         ).'" />'.$this->core->formNonce();
1765                    } break;
1766
1767                    # Behavior
1768                    case 'behavior':
1769
1770                         # --BEHAVIOR-- adminModulesListGetGlobalActions
1771                         $tmp = $this->core->callBehavior('adminModulesListGetGlobalActions', $this);
1772
1773                         if (!empty($tmp)) {
1774                              $submits[] = $tmp;
1775                         }
1776                    break;
1777               }
1778          }
1779
1780          return $submits;
1781     }
1782
1783     public function doActions()
1784     {
1785          if (empty($_POST) || !empty($_REQUEST['conf'])) {
1786               return null;
1787          }
1788
1789          $modules = !empty($_POST['modules']) && is_array($_POST['modules']) ? array_values($_POST['modules']) : array();
1790
1791          if (!empty($_POST['select'])) {
1792
1793               # Can select only one theme at a time!
1794               if (is_array($_POST['select'])) {
1795                    $modules = array_keys($_POST['select']);
1796                    $id = $modules[0];
1797
1798                    if (!$this->modules->moduleExists($id)) {
1799                         throw new Exception(__('No such theme.'));
1800                    }
1801
1802                    $this->core->blog->settings->addNamespace('system');
1803                    $this->core->blog->settings->system->put('theme',$id);
1804                    $this->core->blog->triggerBlog();
1805
1806                    dcPage::addSuccessNotice(__('Theme has been successfully selected.'));
1807                    http::redirect($this->getURL().'#themes');
1808               }
1809          }
1810
1811          else {
1812               if (!$this->isWritablePath()) {
1813                    return null;
1814               }
1815
1816               if ($this->core->auth->isSuperAdmin() && !empty($_POST['activate'])) {
1817
1818                    if (is_array($_POST['activate'])) {
1819                         $modules = array_keys($_POST['activate']);
1820                    }
1821
1822                    $list = $this->modules->getDisabledModules();
1823                    if (empty($list)) {
1824                         throw new Exception(__('No such theme.'));
1825                    }
1826
1827                    $count = 0;
1828                    foreach($list as $id => $module) {
1829
1830                         if (!in_array($id, $modules)) {
1831                              continue;
1832                         }
1833
1834                         # --BEHAVIOR-- themeBeforeActivate
1835                         $this->core->callBehavior('themeBeforeActivate', $id);
1836
1837                         $this->modules->activateModule($id);
1838
1839                         # --BEHAVIOR-- themeAfterActivate
1840                         $this->core->callBehavior('themeAfterActivate', $id);
1841
1842                         $count++;
1843                    }
1844
1845                    dcPage::addSuccessNotice(
1846                         __('Theme has been successfully activated.', 'Themes have been successuflly activated.', $count)
1847                    );
1848                    http::redirect($this->getURL());
1849               }
1850
1851               elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['deactivate'])) {
1852
1853                    if (is_array($_POST['deactivate'])) {
1854                         $modules = array_keys($_POST['deactivate']);
1855                    }
1856
1857                    $list = $this->modules->getModules();
1858                    if (empty($list)) {
1859                         throw new Exception(__('No such theme.'));
1860                    }
1861
1862                    $failed = false;
1863                    $count = 0;
1864                    foreach($list as $id => $module) {
1865
1866                         if (!in_array($id, $modules)) {
1867                              continue;
1868                         }
1869
1870                         if (!$module['root_writable']) {
1871                              $failed = true;
1872                              continue;
1873                         }
1874
1875                         $module[$id] = $id;
1876
1877                         # --BEHAVIOR-- themeBeforeDeactivate
1878                         $this->core->callBehavior('themeBeforeDeactivate', $module);
1879
1880                         $this->modules->deactivateModule($id);
1881
1882                         # --BEHAVIOR-- themeAfterDeactivate
1883                         $this->core->callBehavior('themeAfterDeactivate', $module);
1884
1885                         $count++;
1886                    }
1887
1888                    if ($failed) {
1889                         dcPage::addWarningNotice(__('Some themes have not been deactivated.'));
1890                    }
1891                    else {
1892                         dcPage::addSuccessNotice(
1893                              __('Theme has been successfully deactivated.', 'Themes have been successuflly deactivated.', $count)
1894                         );
1895                    }
1896                    http::redirect($this->getURL());
1897               }
1898
1899               elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['delete'])) {
1900
1901                    if (is_array($_POST['delete'])) {
1902                         $modules = array_keys($_POST['delete']);
1903                    }
1904
1905                    $list = $this->modules->getDisabledModules();
1906
1907                    $failed = false;
1908                    $count = 0;
1909                    foreach($modules as $id)
1910                    {
1911                         if (!isset($list[$id])) {
1912
1913                              if (!$this->modules->moduleExists($id)) {
1914                                   throw new Exception(__('No such theme.'));
1915                              }
1916
1917                              $module = $this->modules->getModules($id);
1918                              $module['id'] = $id;
1919
1920                              if (!$this->isDeletablePath($module['root'])) {
1921                                   $failed = true;
1922                                   continue;
1923                              }
1924
1925                              # --BEHAVIOR-- themeBeforeDelete
1926                              $this->core->callBehavior('themeBeforeDelete', $module);
1927
1928                              $this->modules->deleteModule($id);
1929
1930                              # --BEHAVIOR-- themeAfterDelete
1931                              $this->core->callBehavior('themeAfterDelete', $module);
1932                         }
1933                         else {
1934                              $this->modules->deleteModule($id, true);
1935                         }
1936
1937                         $count++;
1938                    }
1939
1940                    if (!$count && $failed) {
1941                         throw new Exception(__("You don't have permissions to delete this theme."));
1942                    }
1943                    elseif ($failed) {
1944                         dcPage::addWarningNotice(__('Some themes have not been delete.'));
1945                    }
1946                    else {
1947                         dcPage::addSuccessNotice(
1948                              __('Theme has been successfully deleted.', 'Themes have been successuflly deleted.', $count)
1949                         );
1950                    }
1951                    http::redirect($this->getURL());
1952               }
1953
1954               elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['install'])) {
1955
1956                    if (is_array($_POST['install'])) {
1957                         $modules = array_keys($_POST['install']);
1958                    }
1959
1960                    $list = $this->store->get();
1961
1962                    if (empty($list)) {
1963                         throw new Exception(__('No such theme.'));
1964                    }
1965
1966                    $count = 0;
1967                    foreach($list as $id => $module) {
1968
1969                         if (!in_array($id, $modules)) {
1970                              continue;
1971                         }
1972
1973                         $dest = $this->getPath().'/'.basename($module['file']);
1974
1975                         # --BEHAVIOR-- themeBeforeAdd
1976                         $this->core->callBehavior('themeBeforeAdd', $module);
1977
1978                         $this->store->process($module['file'], $dest);
1979
1980                         # --BEHAVIOR-- themeAfterAdd
1981                         $this->core->callBehavior('themeAfterAdd', $module);
1982
1983                         $count++;
1984                    }
1985
1986                    dcPage::addSuccessNotice(
1987                         __('Theme has been successfully installed.', 'Themes have been successuflly installed.', $count)
1988                    );
1989                    http::redirect($this->getURL());
1990               }
1991
1992               elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['update'])) {
1993
1994                    if (is_array($_POST['update'])) {
1995                         $modules = array_keys($_POST['update']);
1996                    }
1997
1998                    $list = $this->store->get(true);
1999                    if (empty($list)) {
2000                         throw new Exception(__('No such theme.'));
2001                    }
2002
2003                    $count = 0;
2004                    foreach($list as $module) {
2005
2006                         if (!in_array($module['id'], $modules)) {
2007                              continue;
2008                         }
2009
2010                         $dest = $module['root'].'/../'.basename($module['file']);
2011
2012                         # --BEHAVIOR-- themeBeforeUpdate
2013                         $this->core->callBehavior('themeBeforeUpdate', $module);
2014
2015                         $this->store->process($module['file'], $dest);
2016
2017                         # --BEHAVIOR-- themeAfterUpdate
2018                         $this->core->callBehavior('themeAfterUpdate', $module);
2019
2020                         $count++;
2021                    }
2022
2023                    $tab = $count && $count == count($list) ? '#themes' : '#update';
2024
2025                    dcPage::addSuccessNotice(
2026                         __('Theme has been successfully updated.', 'Themes have been successuflly updated.', $count)
2027                    );
2028                    http::redirect($this->getURL().$tab);
2029               }
2030
2031               # Manual actions
2032               elseif (!empty($_POST['upload_pkg']) && !empty($_FILES['pkg_file'])
2033                    || !empty($_POST['fetch_pkg']) && !empty($_POST['pkg_url']))
2034               {
2035                    if (empty($_POST['your_pwd']) || !$this->core->auth->checkPassword($this->core->auth->crypt($_POST['your_pwd']))) {
2036                         throw new Exception(__('Password verification failed'));
2037                    }
2038
2039                    if (!empty($_POST['upload_pkg'])) {
2040                         files::uploadStatus($_FILES['pkg_file']);
2041
2042                         $dest = $this->getPath().'/'.$_FILES['pkg_file']['name'];
2043                         if (!move_uploaded_file($_FILES['pkg_file']['tmp_name'], $dest)) {
2044                              throw new Exception(__('Unable to move uploaded file.'));
2045                         }
2046                    }
2047                    else {
2048                         $url = urldecode($_POST['pkg_url']);
2049                         $dest = $this->getPath().'/'.basename($url);
2050                         $this->store->download($url, $dest);
2051                    }
2052
2053                    # --BEHAVIOR-- themeBeforeAdd
2054                    $this->core->callBehavior('themeBeforeAdd', null);
2055
2056                    $ret_code = $this->store->install($dest);
2057
2058                    # --BEHAVIOR-- themeAfterAdd
2059                    $this->core->callBehavior('themeAfterAdd', null);
2060
2061                    dcPage::addSuccessNotice($ret_code == 2 ?
2062                         __('Theme has been successfully updated.') :
2063                         __('Theme has been successfully installed.')
2064                    );
2065                    http::redirect($this->getURL().'#themes');
2066               }
2067
2068               else {
2069
2070                    # --BEHAVIOR-- adminModulesListDoActions
2071                    $this->core->callBehavior('adminModulesListDoActions', $this, $modules, 'theme');
2072
2073               }
2074          }
2075
2076          return null;
2077     }
2078}
Note: See TracBrowser for help on using the repository browser.

Sites map