Dotclear

source: inc/admin/lib.moduleslist.php @ 2469:41832d1dd000

Revision 2469:41832d1dd000, 47.6 KB checked in by Denis Jean-Chirstian <contact@…>, 12 years ago (diff)

Try to translate modules description

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

Sites map