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

RevLine 
[2147]1<?php
[2215]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; }
[2147]13
[2227]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 */
[2147]21class adminModulesList
22{
[2227]23     public $core;       /**< @var object    dcCore instance */
24     public $modules;    /**< @var object    dcModules instance */
25     public $store;      /**< @var object    dcStore instance */
[2147]26
[2227]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 */
[2149]29
[2227]30     protected $list_id = 'unknow';     /**< @var string    Current list ID */
31     protected $data = array();         /**< @var array     Current modules */
[2156]32
[2227]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 */
[2182]36
[2227]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 */
[2149]40
[2817]41     protected $page_url = '';                    /**< @var string    Page URL */
[2227]42     protected $page_qs = '?';                    /**< @var string    Page query string */
43     protected $page_tab = '';                    /**< @var string    Page tab */
[2259]44     protected $page_redir = '';                  /**< @var string    Page redirection */
[2147]45
[2227]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 */
[2147]49
[2227]50     protected $sort_field = 'sname';   /**< @var string    Field used to sort modules */
51     protected $sort_asc = true;             /**< @var boolean   Sort order asc */
[2147]52
[2227]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      */
[2215]62     public function __construct(dcModules $modules, $modules_root, $xml_url)
[2147]63     {
[2215]64          $this->core = $modules->core;
65          $this->modules = $modules;
[2216]66          $this->store = new dcStore($modules, $xml_url);
[2215]67
[2817]68          $this->page_url = $this->core->adminurl->get('admin.plugins');
69
[2227]70          $this->setPath($modules_root);
71          $this->setIndex(__('other'));
[2147]72     }
73
[2227]74     /**
75      * Begin a new list.
76      *
77      * @param string    $id       New list ID
78      * @return     adminModulesList self instance
79      */
[2241]80     public function setList($id)
[2156]81     {
[2215]82          $this->data = array();
[2156]83          $this->page_tab = '';
[2227]84          $this->list_id = $id;
[2156]85
86          return $this;
87     }
88
[2241]89     /**
90      * Get list ID.
91      *
92      * @return     List ID
93      */
94     public function getList()
95     {
96          return $this->list_id;
97     }
98
[2227]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)
[2149]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
[2227]122     /**
123      * Get modules root directory.
124      *
125      * @return     Path to work on
126      */
[2149]127     public function getPath()
128     {
129          return $this->path;
130     }
131
[2227]132     /**
133      * Check if modules root directory is writable.
134      *
135      * @return     True if directory is writable
136      */
137     public function isWritablePath()
[2149]138     {
139          return $this->path_writable;
140     }
141
[2227]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)
[2149]149     {
[2431]150          return $this->path_writable
151               && (preg_match('!^'.$this->path_pattern.'!', $root) || defined('DC_DEV') && DC_DEV)
[2149]152               && $this->core->auth->isSuperAdmin();
153     }
[2227]154     //@}
[2149]155
[2227]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)
[2147]165     {
[2327]166          $this->page_qs = strpos('?', $url) ? '&amp;' : '?';
[2147]167          $this->page_url = $url;
168
169          return $this;
170     }
171
[2227]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)
[2147]180     {
181          return $this->page_url.
[2149]182               (!empty($queries) ? $this->page_qs : '').
[2147]183               (is_array($queries) ? http_build_query($queries) : $queries).
[2149]184               ($with_tab && !empty($this->page_tab) ? '#'.$this->page_tab : '');
[2147]185     }
186
[2227]187     /**
188      * Set page tab.
189      *
190      * @param string    $tab      Page tab
191      * @return     adminModulesList self instance
192      */
193     public function setTab($tab)
[2147]194     {
195          $this->page_tab = $tab;
196
197          return $this;
198     }
199
[2227]200     /**
201      * Get page tab.
202      *
203      * @return     Page tab
204      */
205     public function getTab()
[2147]206     {
207          return $this->page_tab;
208     }
[2259]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     }
[2227]232     //@}
[2147]233
[2227]234     /// @name Search methods
235     //@{
236     /**
237      * Get search query.
238      *
239      * @return     Search query
240      */
241     public function getSearch()
[2147]242     {
243          $query = !empty($_REQUEST['m_search']) ? trim($_REQUEST['m_search']) : null;
[2227]244          return strlen($query) > 2 ? $query : null;
[2147]245     }
246
[2227]247     /**
248      * Display searh form.
249      *
250      * @return     adminModulesList self instance
251      */
252     public function displaySearch()
[2147]253     {
[2227]254          $query = $this->getSearch();
[2163]255
[2215]256          if (empty($this->data) && $query === null) {
[2147]257               return $this;
258          }
259
[2431]260          echo
[2287]261          '<div class="modules-search">'.
[2291]262          '<form action="'.$this->getURL().'" method="get">'.
[2157]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)).
[2291]265          '<input type="submit" value="'.__('OK').'" /> ';
[2287]266
[2431]267          if ($query) {
268               echo
[2287]269               ' <a href="'.$this->getURL().'" class="button">'.__('Reset search').'</a>';
270          }
271
[2431]272          echo
[2287]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>'.
[2158]277          '</form>';
[2147]278
279          if ($query) {
[2431]280               echo
[2158]281               '<p class="message">'.sprintf(
[2431]282                    __('Found %d result for search "%s":', 'Found %d results for search "%s":', count($this->data)),
[2215]283                    count($this->data), html::escapeHTML($query)
[2158]284                    ).
[2147]285               '</p>';
286          }
[2287]287          echo '</div>';
288
[2147]289          return $this;
290     }
[2227]291     //@}
[2147]292
[2227]293     /// @name Navigation menu methods
294     //@{
295     /**
296      * Set navigation special index.
297      *
298      * @return     adminModulesList self instance
299      */
300     public function setIndex($str)
[2147]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
[2227]308     /**
309      * Get index from query.
310      *
311      * @return     Query index or default one
312      */
313     public function getIndex()
[2147]314     {
315          return isset($_REQUEST['m_nav']) && in_array($_REQUEST['m_nav'], $this->nav_list) ? $_REQUEST['m_nav'] : $this->nav_list[0];
316     }
317
[2227]318     /**
319      * Display navigation by index menu.
320      *
321      * @return     adminModulesList self instance
322      */
323     public function displayIndex()
[2147]324     {
[2227]325          if (empty($this->data) || $this->getSearch() !== null) {
[2147]326               return $this;
327          }
328
329          # Fetch modules required field
330          $indexes = array();
[2215]331          foreach ($this->data as $id => $module) {
[2147]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
[2227]348               if ($this->getIndex() == $char) {
[2147]349                    $buttons[] = '<li class="active" title="'.__('current selection').'"><strong> '.$char.' </strong></li>';
350               }
351               # Letter having modules
352               elseif (!empty($indexes[$char])) {
[2377]353                    $title = sprintf(__('%d result', '%d results', $indexes[$char]), $indexes[$char]);
[2227]354                    $buttons[] = '<li class="btn" title="'.$title.'"><a href="'.$this->getURL('m_nav='.$char).'" title="'.$title.'"> '.$char.' </a></li>';
[2147]355               }
356               # Letter without modules
357               else {
[2377]358                    $buttons[] = '<li class="btn no-link" title="'.__('no results').'"> '.$char.' </li>';
[2147]359               }
360          }
361          # Parse navigation menu
[2291]362          echo '<div class="pager">'.__('Browse index:').' <ul class="index">'.implode('',$buttons).'</ul></div>';
[2147]363
364          return $this;
365     }
[2227]366     //@}
[2147]367
[2227]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)
[2147]376     {
377          $this->sort_field = $field;
378          $this->sort_asc = (boolean) $asc;
379
380          return $this;
381     }
382
[2227]383     /**
384      * Get sort field from query.
385      *
386      * @return     Query sort field or default one
387      */
388     public function getSort()
[2147]389     {
390          return !empty($_REQUEST['m_sort']) ? $_REQUEST['m_sort'] : $this->sort_field;
391     }
392
[2227]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()
[2147]400     {
[2227]401          //
402
403          return $this;
[2147]404     }
[2227]405     //@}
[2147]406
[2227]407     /// @name Modules methods
408     //@{
409     /**
410      * Set modules and sanitize them.
411      *
412      * @return     adminModulesList self instance
413      */
[2147]414     public function setModules($modules)
415     {
[2215]416          $this->data = array();
[2174]417          if (!empty($modules) && is_array($modules)) {
418               foreach($modules as $id => $module) {
[2227]419                    $this->data[$id] = self::sanitizeModule($id, $module);
[2174]420               }
[2147]421          }
422          return $this;
423     }
424
[2227]425     /**
426      * Get modules currently set.
427      *
428      * @return     Array of modules
429      */
[2147]430     public function getModules()
431     {
[2215]432          return $this->data;
[2147]433     }
434
[2227]435     /**
436      * Sanitize a module.
437      *
[2431]438      * This clean infos of a module by adding default keys
439      * and clean some of them, sanitize module can safely
[2227]440      * be used in lists.
441      *
442      * @return     Array of the module informations
443      */
444     public static function sanitizeModule($id, $module)
[2147]445     {
446          $label = empty($module['label']) ? $id : $module['label'];
447          $name = __(empty($module['name']) ? $label : $module['name']);
[2431]448
[2147]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,
[2156]461                    'standalone_config' => false,
462                    'support'                => '',
463                    'section'                => '',
464                    'tags'                   => '',
[2171]465                    'details'                => '',
[2222]466                    'sshot'             => '',
[2287]467                    'score'                  => 0,
[3333]468                    'type'                   => null,
469                    'require'           => array(),
470                    'settings'               => array()
[2147]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
[2227]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)
[2148]492     {
[2171]493          $distributed_modules = self::$distributed_modules;
494
[2227]495          return is_array($distributed_modules) && in_array($id, $distributed_modules);
[2148]496     }
497
[2227]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      */
[2147]506     public static function sortModules($modules, $field, $asc=true)
507     {
[2631]508          $origin = $sorter = array();
509
[2147]510          foreach($modules as $id => $module) {
[2631]511               $origin[] = $module;
512               $sorter[] = isset($module[$field]) ? $module[$field] : $field;
[2147]513          }
514
[2631]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;
[2147]522     }
523
[2227]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)
[2147]533     {
[2431]534          echo
[2487]535          '<form action="'.$this->getURL().'" method="post" class="modules-form-actions">'.
[2147]536          '<div class="table-outer">'.
[2156]537          '<table id="'.html::escapeHTML($this->list_id).'" class="modules'.(in_array('expander', $cols) ? ' expandable' : '').'">'.
[2377]538          '<caption class="hidden">'.html::escapeHTML(__('Plugins list')).'</caption><tr>';
[2163]539
[2147]540          if (in_array('name', $cols)) {
[2487]541               $colspan = 1;
542               if (in_array('checkbox', $cols)) {
543                    $colspan++;
544               }
545               if (in_array('icon', $cols)) {
546                    $colspan++;
547               }
[2431]548               echo
[2487]549               '<th class="first nowrap"'.($colspan > 1 ? ' colspan="'.$colspan.'"' : '').'>'.__('Name').'</th>';
[2147]550          }
551
[2227]552          if (in_array('score', $cols) && $this->getSearch() !== null && defined('DC_DEBUG') && DC_DEBUG) {
[2431]553               echo
[2222]554               '<th class="nowrap">'.__('Score').'</th>';
555          }
556
[2147]557          if (in_array('version', $cols)) {
[2431]558               echo
[2147]559               '<th class="nowrap count" scope="col">'.__('Version').'</th>';
560          }
561
562          if (in_array('current_version', $cols)) {
[2431]563               echo
[2147]564               '<th class="nowrap count" scope="col">'.__('Current version').'</th>';
565          }
566
567          if (in_array('desc', $cols)) {
[2431]568               echo
[2147]569               '<th class="nowrap" scope="col">'.__('Details').'</th>';
570          }
571
[2149]572          if (in_array('distrib', $cols)) {
[2431]573               echo
[2354]574               '<th'.(in_array('desc', $cols) ? '' : ' class="maximal"').'></th>';
[2149]575          }
576
[2147]577          if (!empty($actions) && $this->core->auth->isSuperAdmin()) {
[2431]578               echo
[2147]579               '<th class="minimal nowrap">'.__('Action').'</th>';
580          }
581
[2431]582          echo
[2147]583          '</tr>';
584
[2227]585          $sort_field = $this->getSort();
[2147]586
[2215]587          # Sort modules by $sort_field (default sname)
[2227]588          $modules = $this->getSearch() === null ?
[2215]589               self::sortModules($this->data, $sort_field, $this->sort_asc) :
590               $this->data;
[2147]591
592          $count = 0;
593          foreach ($modules as $id => $module)
594          {
595               # Show only requested modules
[2227]596               if ($nav_limit && $this->getSearch() === null) {
[2147]597                    $char = substr($module[$sort_field], 0, 1);
598                    if (!in_array($char, $this->nav_list)) {
599                         $char = $this->nav_special;
600                    }
[2227]601                    if ($this->getIndex() != $char) {
[2147]602                         continue;
603                    }
604               }
605
[2431]606               echo
[2171]607               '<tr class="line" id="'.html::escapeHTML($this->list_id).'_m_'.html::escapeHTML($id).'">';
[2431]608
[2354]609               $tds = 0;
[2163]610
[2487]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
[2156]619               if (in_array('icon', $cols)) {
[2354]620                    $tds++;
[2431]621                    echo
[2171]622                    '<td class="module-icon nowrap">'.sprintf(
[2431]623                         '<img alt="%1$s" title="%1$s" src="%2$s" />',
[2817]624                         html::escapeHTML($id), file_exists($module['root'].'/icon.png') ?
[2849]625                         dcPage::getPF($id.'/icon.png') : 'images/module.png'
[2156]626                    ).'</td>';
627               }
628
[2354]629               $tds++;
[2431]630               echo
[2487]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>';
[2147]652
[2222]653               # Display score only for debug purpose
[2227]654               if (in_array('score', $cols) && $this->getSearch() !== null && defined('DC_DEBUG') && DC_DEBUG) {
[2354]655                    $tds++;
[2431]656                    echo
[2222]657                    '<td class="module-version nowrap count"><span class="debug">'.$module['score'].'</span></td>';
658               }
659
[2147]660               if (in_array('version', $cols)) {
[2354]661                    $tds++;
[2431]662                    echo
[2171]663                    '<td class="module-version nowrap count">'.html::escapeHTML($module['version']).'</td>';
[2147]664               }
665
[2148]666               if (in_array('current_version', $cols)) {
[2354]667                    $tds++;
[2431]668                    echo
[2171]669                    '<td class="module-current-version nowrap count">'.html::escapeHTML($module['current_version']).'</td>';
[2147]670               }
671
672               if (in_array('desc', $cols)) {
[2354]673                    $tds++;
[2431]674                    echo
[2997]675                    '<td class="module-desc maximal">'.html::escapeHTML(__($module['desc']));
[3007]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>';
[2997]690                         }
[3007]691                         echo '</ul>'.
692                         '</span>';
[2997]693                    }
694                    echo '</td>';
695
[2149]696               }
697
698               if (in_array('distrib', $cols)) {
[2354]699                    $tds++;
[2431]700                    echo
701                    '<td class="module-distrib">'.(self::isDistributedModule($id) ?
[2149]702                         '<img src="images/dotclear_pw.png" alt="'.
[2377]703                         __('Plugin from official distribution').'" title="'.
[2431]704                         __('Plugin from official distribution').'" />'
[2149]705                    : '').'</td>';
[2147]706               }
707
708               if (!empty($actions) && $this->core->auth->isSuperAdmin()) {
[2215]709                    $buttons = $this->getActions($id, $module, $actions);
710
[2354]711                    $tds++;
[2431]712                    echo
[2215]713                    '<td class="module-actions nowrap">'.
[2147]714
[2487]715                    '<div>'.implode(' ', $buttons).'</div>'.
[2215]716
[2147]717                    '</td>';
718               }
719
[2431]720               echo
[2147]721               '</tr>';
722
[2354]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'])) {
[2431]729                         echo
[2354]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                         }
[2431]745
[2354]746                         if (!empty($more)) {
747                              echo
[3333]748                              '<li>'.implode(' - ',$more).'</li>';
[2354]749                         }
750
751                         echo
752                         '</ul></div>';
753                    }
754
755                    $config = !empty($module['root']) && file_exists(path::real($module['root'].'/_config.php'));
[3335]756                    $index = !empty($module['root']) && file_exists(path::real($module['root'].'/index.php'));
[2354]757
[3335]758                    if ($config || $index || !empty($module['section']) || !empty($module['tags']) || !empty($module['settings'])) {
[2431]759                         echo
[2354]760                         '<div><ul class="mod-more">';
761
[3335]762                         if ($index && $module['enabled']) {
763                              echo '<li><a href="'.$this->core->adminurl->get('admin.plugin.'.$id).'">'.__('Manage plugin').'</a></li>';
764                         }
765
[3333]766                         $settings = $this->getSettingsUrls($this->core,$id);
[3335]767                         if (!empty($settings) && $module['enabled']) {
[3333]768                              echo '<li>'.implode(' - ',$settings).'</li>';
[2354]769                         }
770
771                         if (!empty($module['section'])) {
772                              echo
773                              '<li class="module-section">'.__('Section:').' '.html::escapeHTML($module['section']).'</li>';
774                         }
775
[3111]776                         if (!empty($module['tags'])) {
[2354]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
[2147]789               $count++;
790          }
[2431]791          echo
[2147]792          '</table></div>';
793
[2227]794          if(!$count && $this->getSearch() === null) {
[2431]795               echo
[2377]796               '<p class="message">'.__('No plugins matched your search.').'</p>';
[2147]797          }
[2227]798
[2487]799          elseif ((in_array('checkbox', $cols) || $count > 1) && !empty($actions) && $this->core->auth->isSuperAdmin()) {
800               $buttons = $this->getGlobalActions($actions, in_array('checkbox', $cols));
[2428]801
[2487]802               if (!empty($buttons)) {
803                    if (in_array('checkbox', $cols)) {
[2566]804                         echo
[2487]805                         '<p class="checkboxes-helpers"></p>';
806                    }
807                    echo
808                    '<div>'.implode(' ', $buttons).'</div>';
809               }
[2428]810          }
[2566]811          echo
[2487]812          '</form>';
[2428]813
[2227]814          return $this;
[2147]815     }
816
[2227]817     /**
[3333]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) {
[3337]835                    if (!$check ||
836                         $core->auth->isSuperAdmin() ||
837                         $core->auth->check($core->plugins->moduleInfo($id,'permissions'),$core->blog->id))
838                    {
[3338]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                         }
[3337]843                         $st[] = '<a class="module-config" href="'.
[3338]844                              $core->adminurl->get('admin.plugins',$params).
[3337]845                              '">'.__('Configure plugin').'</a>';
[3336]846                    }
[3333]847               }
848               if (is_array($settings)) {
849                    foreach ($settings as $sk => $sv) {
850                         switch ($sk) {
851                              case 'blog':
[3337]852                                   if (!$check ||
853                                        $core->auth->isSuperAdmin() ||
854                                        $core->auth->check('admin',$core->blog->id))
855                                   {
[3333]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':
[3337]862                                   if (!$check ||
863                                        $core->auth->isSuperAdmin() ||
864                                        $core->auth->check('usage,contentadmin',$core->blog->id))
865                                   {
[3333]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) {
[3337]873                                        if (!$check ||
874                                             $core->auth->isSuperAdmin() ||
875                                             $core->auth->check($core->plugins->moduleInfo($id,'permissions'),$core->blog->id))
876                                        {
[3333]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     /**
[2227]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      */
[2215]899     protected function getActions($id, $module, $actions)
[2147]900     {
901          $submits = array();
902
[2215]903          # Use loop to keep requested order
904          foreach($actions as $action) {
905               switch($action) {
906
907                    # Deactivate
[3007]908                    case 'activate': if ($this->core->auth->isSuperAdmin() && $module['root_writable'] && !isset($module['cannot_enable'])) {
[2431]909                         $submits[] =
[2487]910                         '<input type="submit" name="activate['.html::escapeHTML($id).']" value="'.__('Activate').'" />';
[2215]911                    } break;
912
913                    # Activate
[3007]914                    case 'deactivate': if ($this->core->auth->isSuperAdmin() && $module['root_writable'] && !isset($module['cannot_disable'])) {
[2431]915                         $submits[] =
[2487]916                         '<input type="submit" name="deactivate['.html::escapeHTML($id).']" value="'.__('Deactivate').'" class="reset" />';
[2215]917                    } break;
918
919                    # Delete
[3007]920                    case 'delete': if ($this->core->auth->isSuperAdmin() && $this->isDeletablePath($module['root'])&& !isset($module['cannot_disable'])) {
[2241]921                         $dev = !preg_match('!^'.$this->path_pattern.'!', $module['root']) && defined('DC_DEV') && DC_DEV ? ' debug' : '';
[2431]922                         $submits[] =
[2487]923                         '<input type="submit" class="delete '.$dev.'" name="delete['.html::escapeHTML($id).']" value="'.__('Delete').'" />';
[2215]924                    } break;
925
[2216]926                    # Install (from store)
[2490]927                    case 'install': if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
[2431]928                         $submits[] =
[2487]929                         '<input type="submit" name="install['.html::escapeHTML($id).']" value="'.__('Install').'" />';
[2215]930                    } break;
931
[2216]932                    # Update (from store)
[2490]933                    case 'update': if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
[2431]934                         $submits[] =
[2487]935                         '<input type="submit" name="update['.html::escapeHTML($id).']" value="'.__('Update').'" />';
[2215]936                    } break;
[2241]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;
[2215]948               }
[2192]949          }
950
[2428]951          return $submits;
952     }
953
954     /**
955      * Get global action buttons to add to modules list.
956      *
[2487]957      * @param array     $actions        Actions keys
958      * @param boolean   $with_selection Limit action to selected modules
[2428]959      * @return     Array of actions buttons
960      */
[2487]961     protected function getGlobalActions($actions, $with_selection=false)
[2428]962     {
963          $submits = array();
964
965          # Use loop to keep requested order
966          foreach($actions as $action) {
967               switch($action) {
968
969                    # Deactivate
[2490]970                    case 'activate': if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
[2431]971                         $submits[] =
[2487]972                         '<input type="submit" name="activate" value="'.($with_selection ?
973                              __('Activate selected plugins') :
974                              __('Activate all plugins from this list')
975                         ).'" />';
[2428]976                    } break;
977
978                    # Activate
[2490]979                    case 'deactivate': if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
[2431]980                         $submits[] =
[2487]981                         '<input type="submit" name="deactivate" value="'.($with_selection ?
982                              __('Deactivate selected plugins') :
983                              __('Deactivate all plugins from this list')
984                         ).'" />';
[2428]985                    } break;
986
987                    # Update (from store)
[2490]988                    case 'update': if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
[2431]989                         $submits[] =
[2487]990                         '<input type="submit" name="update" value="'.($with_selection ?
991                              __('Update selected plugins') :
992                              __('Update all plugins from this list')
993                         ).'" />';
[2428]994                    } break;
995
996                    # Behavior
997                    case 'behavior':
998
999                         # --BEHAVIOR-- adminModulesListGetGlobalActions
[2487]1000                         $tmp = $this->core->callBehavior('adminModulesListGetGlobalActions', $this, $with_selection);
[2428]1001
1002                         if (!empty($tmp)) {
1003                              $submits[] = $tmp;
1004                         }
1005                    break;
1006               }
1007          }
[2241]1008
[2215]1009          return $submits;
[2147]1010     }
1011
[2227]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      */
[2377]1019     public function doActions()
[2192]1020     {
[2431]1021          if (empty($_POST) || !empty($_REQUEST['conf'])
[2490]1022          || !$this->isWritablePath()) {
[2149]1023               return null;
1024          }
1025
[2487]1026          $modules = !empty($_POST['modules']) && is_array($_POST['modules']) ? array_values($_POST['modules']) : array();
[2149]1027
[2490]1028          if ($this->core->auth->isSuperAdmin() && !empty($_POST['delete'])) {
[2149]1029
[2487]1030               if (is_array($_POST['delete'])) {
1031                    $modules = array_keys($_POST['delete']);
[2149]1032               }
1033
[2487]1034               $list = $this->modules->getDisabledModules();
[2149]1035
[2487]1036               $failed = false;
1037               $count = 0;
1038               foreach($modules as $id)
1039               {
1040                    if (!isset($list[$id])) {
[2171]1041
[2215]1042                         if (!$this->modules->moduleExists($id)) {
[2377]1043                              throw new Exception(__('No such plugin.'));
[2171]1044                         }
1045
[2215]1046                         $module = $this->modules->getModules($id);
[2171]1047                         $module['id'] = $id;
1048
[2227]1049                         if (!$this->isDeletablePath($module['root'])) {
[2487]1050                              $failed = true;
1051                              continue;
[2171]1052                         }
1053
1054                         # --BEHAVIOR-- moduleBeforeDelete
[2377]1055                         $this->core->callBehavior('pluginBeforeDelete', $module);
[2171]1056
[2215]1057                         $this->modules->deleteModule($id);
[2171]1058
1059                         # --BEHAVIOR-- moduleAfterDelete
[2377]1060                         $this->core->callBehavior('pluginAfterDelete', $module);
[2171]1061                    }
1062                    else {
[2215]1063                         $this->modules->deleteModule($id, true);
[2171]1064                    }
1065
[2487]1066                    $count++;
[2171]1067               }
[2215]1068
[2487]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
[2490]1083          elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['install'])) {
[2487]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;
[2215]1100                    }
1101
1102                    $dest = $this->getPath().'/'.basename($module['file']);
1103
1104                    # --BEHAVIOR-- moduleBeforeAdd
[2377]1105                    $this->core->callBehavior('pluginBeforeAdd', $module);
[2215]1106
[2487]1107                    $this->store->process($module['file'], $dest);
[2215]1108
1109                    # --BEHAVIOR-- moduleAfterAdd
[2377]1110                    $this->core->callBehavior('pluginAfterAdd', $module);
[2215]1111
[2487]1112                    $count++;
[2215]1113               }
1114
[2487]1115               dcPage::addSuccessNotice(
1116                    __('Plugin has been successfully installed.', 'Plugins have been successuflly installed.', $count)
1117               );
1118               http::redirect($this->getURL());
1119          }
[2171]1120
[2490]1121          elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['activate'])) {
[2487]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;
[2171]1137                    }
1138
[2487]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
[2490]1156          elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['deactivate'])) {
[2487]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;
[2171]1173                    }
1174
[2487]1175                    if (!$module['root_writable']) {
1176                         $failed = true;
1177                         continue;
1178                    }
[2270]1179
[2487]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
[2490]1204          elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['update'])) {
[2487]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                    }
[2227]1221
[2171]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
[2377]1233                    $this->core->callBehavior('pluginBeforeUpdate', $module);
[2171]1234
[2216]1235                    $this->store->process($module['file'], $dest);
[2171]1236
1237                    # --BEHAVIOR-- moduleAfterUpdate
[2377]1238                    $this->core->callBehavior('pluginAfterUpdate', $module);
[2171]1239
[2487]1240                    $count++;
[2428]1241               }
1242
[2487]1243               $tab = $count && $count == count($list) ? '#plugins' : '#update';
[2428]1244
[2487]1245               dcPage::addSuccessNotice(
1246                    __('Plugin has been successfully updated.', 'Plugins have been successuflly updated.', $count)
1247               );
1248               http::redirect($this->getURL().$tab);
1249          }
[2428]1250
[2171]1251          # Manual actions
[2431]1252          elseif (!empty($_POST['upload_pkg']) && !empty($_FILES['pkg_file'])
[2171]1253               || !empty($_POST['fetch_pkg']) && !empty($_POST['pkg_url']))
1254          {
[3036]1255               if (empty($_POST['your_pwd']) || !$this->core->auth->checkPassword($this->core->auth->crypt($_POST['your_pwd']))) {
[2171]1256                    throw new Exception(__('Password verification failed'));
1257               }
1258
1259               if (!empty($_POST['upload_pkg'])) {
1260                    files::uploadStatus($_FILES['pkg_file']);
[2431]1261
[2171]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                    }
[2149]1266               }
1267               else {
[2171]1268                    $url = urldecode($_POST['pkg_url']);
1269                    $dest = $this->getPath().'/'.basename($url);
[2216]1270                    $this->store->download($url, $dest);
[2149]1271               }
1272
[2171]1273               # --BEHAVIOR-- moduleBeforeAdd
[2377]1274               $this->core->callBehavior('pluginBeforeAdd', null);
[2171]1275
[2216]1276               $ret_code = $this->store->install($dest);
[2171]1277
1278               # --BEHAVIOR-- moduleAfterAdd
[2377]1279               $this->core->callBehavior('pluginAfterAdd', null);
[2171]1280
[2219]1281               dcPage::addSuccessNotice($ret_code == 2 ?
[2377]1282                    __('Plugin has been successfully updated.') :
1283                    __('Plugin has been successfully installed.')
[2219]1284               );
[2377]1285               http::redirect($this->getURL().'#plugins');
[2171]1286          }
[2192]1287
[2487]1288          else {
1289
1290               # --BEHAVIOR-- adminModulesListDoActions
1291               $this->core->callBehavior('adminModulesListDoActions', $this, $modules, 'plugin');
1292
1293          }
1294
[2171]1295          return null;
1296     }
1297
[2227]1298     /**
1299      * Display tab for manual installation.
1300      *
1301      * @return     adminModulesList self instance
1302      */
[2171]1303     public function displayManualForm()
1304     {
[2227]1305          if (!$this->core->auth->isSuperAdmin() || !$this->isWritablePath()) {
[2171]1306               return null;
[2149]1307          }
1308
[2171]1309          # 'Upload module' form
1310          echo
[2227]1311          '<form method="post" action="'.$this->getURL().'" id="uploadpkg" enctype="multipart/form-data" class="fieldset">'.
[2171]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>';
[2227]1320
[2171]1321          # 'Fetch module' form
1322          echo
[2227]1323          '<form method="post" action="'.$this->getURL().'" id="fetchpkg" class="fieldset">'.
[2171]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>';
[2227]1332
1333          return $this;
[2149]1334     }
[2227]1335     //@}
[2149]1336
[2227]1337     /// @name Module configuration methods
1338     //@{
[2182]1339     /**
[2227]1340      * Prepare module configuration.
[2182]1341      *
1342      * We need to get configuration content in three steps
1343      * and out of this class to keep backward compatibility.
1344      *
[2227]1345      * if ($xxx->setConfiguration()) {
1346      *   include $xxx->includeConfiguration();
[2182]1347      * }
[2227]1348      * $xxx->getConfiguration();
[2182]1349      * ... [put here page headers and other stuff]
[2227]1350      * $xxx->displayConfiguration();
[2182]1351      *
[2227]1352      * @param string    $id       Module to work on or it gather through REQUEST
1353      * @return     True if config set
[2182]1354      */
[2227]1355     public function setConfiguration($id=null)
[2182]1356     {
1357          if (empty($_REQUEST['conf']) || empty($_REQUEST['module']) && !$id) {
1358               return false;
1359          }
[2431]1360
[2182]1361          if (!empty($_REQUEST['module']) && empty($id)) {
1362               $id = $_REQUEST['module'];
1363          }
1364
[2215]1365          if (!$this->modules->moduleExists($id)) {
[2377]1366               $this->core->error->add(__('Unknow plugin ID'));
[2182]1367               return false;
1368          }
1369
[2215]1370          $module = $this->modules->getModules($id);
[2227]1371          $module = self::sanitizeModule($id, $module);
[2182]1372          $file = path::real($module['root'].'/_config.php');
1373
1374          if (!file_exists($file)) {
[2377]1375               $this->core->error->add(__('This plugin has no configuration file.'));
[2182]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
[2227]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()
[2182]1397     {
1398          if (!$this->config_file) {
1399               return null;
1400          }
[2259]1401          $this->setRedir($this->getURL().'#plugins');
[2182]1402
1403          ob_start();
1404
1405          return $this->config_file;
1406     }
1407
[2227]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()
[2182]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
[2227]1425     /**
1426      * Display module configuration form.
1427      *
1428      * @note Required previously gathered content
1429      * @return     adminModulesList self instance
1430      */
1431     public function displayConfiguration()
[2182]1432     {
[2227]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">'.
[2377]1438                    '<h3>'.sprintf(__('Configure "%s"'), html::escapeHTML($this->config_module['name'])).'</h3>'.
[2259]1439                    '<p><a class="back" href="'.$this->getRedir().'">'.__('Back').'</a></p>';
[2227]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']).
[2259]1448                    form::hidden('redir', $this->getRedir()).
[2227]1449                    $this->core->formNonce().'</p>'.
1450                    '</form>';
1451               }
[2182]1452          }
1453
[2227]1454          return $this;
1455     }
1456     //@}
[2182]1457
[2227]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      */
[2147]1466     public static function sanitizeString($str)
1467     {
1468          return preg_replace('/[^A-Za-z0-9\@\#+_-]/', '', strtolower($str));
1469     }
1470}
1471
[2227]1472/**
1473 * @ingroup DC_CORE
1474 * @brief Helper to manage list of themes.
1475 * @since 2.6
1476 */
[2147]1477class adminThemesList extends adminModulesList
1478{
[2817]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     }
[2147]1493
[2227]1494     public function displayModules($cols=array('name', 'config', 'version', 'desc'), $actions=array(), $nav_limit=false)
[2171]1495     {
[2431]1496          echo
[2487]1497          '<form action="'.$this->getURL().'" method="post" class="modules-form-actions">'.
[2171]1498          '<div id="'.html::escapeHTML($this->list_id).'" class="modules'.(in_array('expander', $cols) ? ' expandable' : '').' one-box">';
1499
[2227]1500          $sort_field = $this->getSort();
[2171]1501
1502          # Sort modules by id
[2227]1503          $modules = $this->getSearch() === null ?
[2215]1504               self::sortModules($this->data, $sort_field, $this->sort_asc) :
1505               $this->data;
[2171]1506
1507          $res = '';
1508          $count = 0;
1509          foreach ($modules as $id => $module)
1510          {
1511               # Show only requested modules
[2227]1512               if ($nav_limit && $this->getSearch() === null) {
[2171]1513                    $char = substr($module[$sort_field], 0, 1);
1514                    if (!in_array($char, $this->nav_list)) {
1515                         $char = $this->nav_special;
1516                    }
[2227]1517                    if ($this->getIndex() != $char) {
[2171]1518                         continue;
1519                    }
1520               }
1521
[2227]1522               $current = $this->core->blog->settings->system->theme == $id && $this->modules->moduleExists($id);
[2215]1523               $distrib = self::isDistributedModule($id) ? ' dc-box' : '';
[2171]1524
[2431]1525               $line =
[2351]1526               '<div class="box '.($current ? 'medium current-theme' : 'theme').$distrib.'">';
[2171]1527
[2347]1528               if (in_array('name', $cols) && !$current) {
[2431]1529                    $line .=
[2487]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>';
[2171]1549               }
1550
[2222]1551               # Display score only for debug purpose
[2227]1552               if (in_array('score', $cols) && $this->getSearch() !== null && defined('DC_DEBUG') && DC_DEBUG) {
[2431]1553                    $line .=
[2222]1554                    '<p class="module-score debug">'.sprintf(__('Score: %s'), $module['score']).'</p>';
1555               }
1556
[2195]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')) {
[2227]1564                         $sshot = $this->getURL('shot='.rawurlencode($id));
[2195]1565                    }
1566                    # Default screenshot
1567                    else {
1568                         $sshot = 'images/noscreenshot.png';
1569                    }
1570
[2431]1571                    $line .=
[2215]1572                    '<div class="module-sshot"><img src="'.$sshot.'" alt="'.
1573                    sprintf(__('%s screenshot.'), html::escapeHTML($module['name'])).'" /></div>';
[2195]1574               }
1575
[2431]1576               $line .=
[2347]1577               '<div class="module-infos toggle-bloc">';
1578
1579               if (in_array('name', $cols) && $current) {
[2431]1580                    $line .=
[2487]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>';
[2347]1598               }
1599
1600               $line .=
[2171]1601               '<p>';
1602
1603               if (in_array('desc', $cols)) {
[2431]1604                    $line .=
[2469]1605                    '<span class="module-desc">'.html::escapeHTML(__($module['desc'])).'</span> ';
[2171]1606               }
1607
1608               if (in_array('author', $cols)) {
[2431]1609                    $line .=
[2171]1610                    '<span class="module-author">'.sprintf(__('by %s'),html::escapeHTML($module['author'])).'</span> ';
1611               }
1612
1613               if (in_array('version', $cols)) {
[2431]1614                    $line .=
[2171]1615                    '<span class="module-version">'.sprintf(__('version %s'),html::escapeHTML($module['version'])).'</span> ';
1616               }
1617
[2222]1618               if (in_array('current_version', $cols)) {
[2431]1619                    $line .=
[2222]1620                    '<span class="module-current-version">'.sprintf(__('(current version %s)'),html::escapeHTML($module['current_version'])).'</span> ';
1621               }
1622
[2227]1623               if (in_array('parent', $cols) && !empty($module['parent'])) {
1624                    if ($this->modules->moduleExists($module['parent'])) {
[2431]1625                         $line .=
[2227]1626                         '<span class="module-parent-ok">'.sprintf(__('(built on "%s")'),html::escapeHTML($module['parent'])).'</span> ';
[2171]1627                    }
1628                    else {
[2431]1629                         $line .=
[2227]1630                         '<span class="module-parent-missing">'.sprintf(__('(requires "%s")'),html::escapeHTML($module['parent'])).'</span> ';
[2171]1631                    }
1632               }
1633
[2215]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 .=
[2260]1638                    '<span class="mod-more">';
[2215]1639
1640                    if ($has_details) {
[2431]1641                         $line .=
[2215]1642                         '<a class="module-details" href="'.$module['details'].'">'.__('Details').'</a>';
1643                    }
1644
1645                    if ($has_support) {
[2431]1646                         $line .=
[2215]1647                         ' - <a class="module-support" href="'.$module['support'].'">'.__('Support').'</a>';
1648                    }
1649
1650                    $line .=
1651                    '</span>';
1652               }
1653
[2431]1654               $line .=
[2171]1655               '</p>'.
1656               '</div>';
1657
[2431]1658               $line .=
[2195]1659               '<div class="module-actions toggle-bloc">';
[2431]1660
[2171]1661               # Plugins actions
1662               if ($current) {
[2227]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);
[2431]1669                         $line .=
[2227]1670                         '<p><a href="'.$theme_url.'/style.css">'.__('View stylesheet').'</a></p>';
1671                    }
1672
[2260]1673                    $line .= '<div class="current-actions">';
1674
[2227]1675                    if (file_exists(path::real($this->core->blog->themes_path.'/'.$id).'/_config.php')) {
[2431]1676                         $line .=
1677                         '<p><a href="'.$this->getURL('module='.$id.'&amp;conf=1', false).'" class="button submit">'.__('Configure theme').'</a></p>';
[2227]1678                    }
1679
[2171]1680                    # --BEHAVIOR-- adminCurrentThemeDetails
[2431]1681                    $line .=
[2171]1682                    $this->core->callBehavior('adminCurrentThemeDetails', $this->core, $id, $module);
[2260]1683
1684                    $line .= '</div>';
[2171]1685               }
1686
1687               # _POST actions
[2215]1688               if (!empty($actions)) {
[2171]1689                    $line .=
[2487]1690                    '<p>'.implode(' ', $this->getActions($id, $module, $actions)).'</p>';
[2171]1691               }
1692
[2431]1693               $line .=
[2171]1694               '</div>';
1695
1696               $line .=
1697               '</div>';
1698
1699               $count++;
1700
1701               $res = $current ? $line.$res : $res.$line;
1702          }
[2487]1703
[2431]1704          echo
[2171]1705          $res.
1706          '</div>';
1707
[2227]1708          if(!$count && $this->getSearch() === null) {
[2431]1709               echo
[2377]1710               '<p class="message">'.__('No themes matched your search.').'</p>';
[2171]1711          }
[2428]1712
[2487]1713          elseif ((in_array('checkbox', $cols) || $count > 1) && !empty($actions) && $this->core->auth->isSuperAdmin()) {
1714               $buttons = $this->getGlobalActions($actions, in_array('checkbox', $cols));
[2428]1715
[2487]1716               if (!empty($buttons)) {
[2488]1717                    if (in_array('checkbox', $cols)) {
[2566]1718                         echo
[2488]1719                         '<p class="checkboxes-helpers"></p>';
1720                    }
[2487]1721                    echo '<div>'.implode(' ', $buttons).'</div>';
1722               }
1723          }
[2428]1724
[2487]1725          echo
1726          '</form>';
[2428]1727
[2487]1728          return $this;
[2171]1729     }
[2192]1730
[2215]1731     protected function getActions($id, $module, $actions)
[2192]1732     {
1733          $submits = array();
1734
1735          $this->core->blog->settings->addNamespace('system');
[2215]1736          if ($id != $this->core->blog->settings->system->theme) {
1737
1738               # Select theme to use on curent blog
[3166]1739               if (in_array('select', $actions)) {
[2431]1740                    $submits[] =
[2487]1741                    '<input type="submit" name="select['.html::escapeHTML($id).']" value="'.__('Use this one').'" />';
[2215]1742               }
[2192]1743          }
1744
[2215]1745          return array_merge(
1746               $submits,
1747               parent::getActions($id, $module, $actions)
1748          );
1749     }
1750
[2487]1751     protected function getGlobalActions($actions, $with_selection=false)
[2428]1752     {
1753          $submits = array();
1754
1755          foreach($actions as $action) {
1756               switch($action) {
1757
1758                    # Update (from store)
[2490]1759                    case 'update': if ($this->core->auth->isSuperAdmin() && $this->path_writable) {
[2431]1760                         $submits[] =
[2487]1761                         '<input type="submit" name="update" value="'.($with_selection ?
1762                              __('Update selected themes') :
1763                              __('Update all themes from this list')
[3327]1764                         ).'" />'.$this->core->formNonce();
[2428]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
[2377]1783     public function doActions()
[2215]1784     {
[3221]1785          if (empty($_POST) || !empty($_REQUEST['conf'])) {
[2377]1786               return null;
1787          }
[2215]1788
[2487]1789          $modules = !empty($_POST['modules']) && is_array($_POST['modules']) ? array_values($_POST['modules']) : array();
[2377]1790
[2487]1791          if (!empty($_POST['select'])) {
[2377]1792
[2487]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];
[2215]1797
1798                    if (!$this->modules->moduleExists($id)) {
[2377]1799                         throw new Exception(__('No such theme.'));
[2215]1800                    }
1801
1802                    $this->core->blog->settings->addNamespace('system');
1803                    $this->core->blog->settings->system->put('theme',$id);
1804                    $this->core->blog->triggerBlog();
1805
[2377]1806                    dcPage::addSuccessNotice(__('Theme has been successfully selected.'));
[2227]1807                    http::redirect($this->getURL().'#themes');
[2215]1808               }
[2487]1809          }
[2377]1810
[3221]1811          else {
1812               if (!$this->isWritablePath()) {
1813                    return null;
[2487]1814               }
1815
[3221]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());
[2487]1849               }
1850
[3221]1851               elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['deactivate'])) {
[2487]1852
[3221]1853                    if (is_array($_POST['deactivate'])) {
1854                         $modules = array_keys($_POST['deactivate']);
[2377]1855                    }
1856
[3221]1857                    $list = $this->modules->getModules();
1858                    if (empty($list)) {
1859                         throw new Exception(__('No such theme.'));
[2377]1860                    }
1861
[3221]1862                    $failed = false;
1863                    $count = 0;
1864                    foreach($list as $id => $module) {
[2377]1865
[3221]1866                         if (!in_array($id, $modules)) {
1867                              continue;
[2377]1868                         }
1869
[3221]1870                         if (!$module['root_writable']) {
[2487]1871                              $failed = true;
1872                              continue;
[2377]1873                         }
1874
[3221]1875                         $module[$id] = $id;
[2377]1876
[3221]1877                         # --BEHAVIOR-- themeBeforeDeactivate
1878                         $this->core->callBehavior('themeBeforeDeactivate', $module);
[2377]1879
[3221]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.'));
[2377]1890                    }
1891                    else {
[3221]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']);
[2377]1903                    }
1904
[3221]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());
[2377]1952               }
1953
[3221]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
[2487]1986                    dcPage::addSuccessNotice(
[3221]1987                         __('Theme has been successfully installed.', 'Themes have been successuflly installed.', $count)
[2487]1988                    );
[3221]1989                    http::redirect($this->getURL());
[2487]1990               }
1991
[3221]1992               elseif ($this->core->auth->isSuperAdmin() && !empty($_POST['update'])) {
[2487]1993
[3221]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);
[2487]2029               }
2030
[3221]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'));
[2377]2037                    }
2038
[3221]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                    }
[2377]2052
2053                    # --BEHAVIOR-- themeBeforeAdd
[3221]2054                    $this->core->callBehavior('themeBeforeAdd', null);
[2377]2055
[3221]2056                    $ret_code = $this->store->install($dest);
[2377]2057
2058                    # --BEHAVIOR-- themeAfterAdd
[3221]2059                    $this->core->callBehavior('themeAfterAdd', null);
[2377]2060
[3221]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');
[2377]2066               }
2067
[3221]2068               else {
[2377]2069
[3221]2070                    # --BEHAVIOR-- adminModulesListDoActions
2071                    $this->core->callBehavior('adminModulesListDoActions', $this, $modules, 'theme');
[2487]2072
2073               }
2074          }
2075
[2377]2076          return null;
[2192]2077     }
[2147]2078}
Note: See TracBrowser for help on using the repository browser.

Sites map