Dotclear

source: inc/admin/lib.moduleslist.php @ 2259:6aaea2c2e0f7

Revision 2259:6aaea2c2e0f7, 34.9 KB checked in by Denis Jean-Chirstian <contact@…>, 12 years ago (diff)

better this way, redir ok, noisy friday

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
[2227]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 */
[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
[2227]68          $this->setPath($modules_root);
69          $this->setIndex(__('other'));
[2147]70     }
71
[2227]72     /**
73      * Begin a new list.
74      *
75      * @param string    $id       New list ID
76      * @return     adminModulesList self instance
77      */
[2241]78     public function setList($id)
[2156]79     {
[2215]80          $this->data = array();
[2156]81          $this->page_tab = '';
[2227]82          $this->list_id = $id;
[2156]83
84          return $this;
85     }
86
[2241]87     /**
88      * Get list ID.
89      *
90      * @return     List ID
91      */
92     public function getList()
93     {
94          return $this->list_id;
95     }
96
[2227]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)
[2149]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
[2227]120     /**
121      * Get modules root directory.
122      *
123      * @return     Path to work on
124      */
[2149]125     public function getPath()
126     {
127          return $this->path;
128     }
129
[2227]130     /**
131      * Check if modules root directory is writable.
132      *
133      * @return     True if directory is writable
134      */
135     public function isWritablePath()
[2149]136     {
137          return $this->path_writable;
138     }
139
[2227]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)
[2149]147     {
148          return $this->path_writable 
[2241]149               && (preg_match('!^'.$this->path_pattern.'!', $root) || defined('DC_DEV') && DC_DEV) 
[2149]150               && $this->core->auth->isSuperAdmin();
151     }
[2227]152     //@}
[2149]153
[2227]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)
[2147]163     {
164          $this->page_qs = strpos('?', $url) ? '&' : '?';
165          $this->page_url = $url;
166
167          return $this;
168     }
169
[2227]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)
[2147]178     {
179          return $this->page_url.
[2149]180               (!empty($queries) ? $this->page_qs : '').
[2147]181               (is_array($queries) ? http_build_query($queries) : $queries).
[2149]182               ($with_tab && !empty($this->page_tab) ? '#'.$this->page_tab : '');
[2147]183     }
184
[2227]185     /**
186      * Set page tab.
187      *
188      * @param string    $tab      Page tab
189      * @return     adminModulesList self instance
190      */
191     public function setTab($tab)
[2147]192     {
193          $this->page_tab = $tab;
194
195          return $this;
196     }
197
[2227]198     /**
199      * Get page tab.
200      *
201      * @return     Page tab
202      */
203     public function getTab()
[2147]204     {
205          return $this->page_tab;
206     }
[2259]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     }
[2227]230     //@}
[2147]231
[2227]232     /// @name Search methods
233     //@{
234     /**
235      * Get search query.
236      *
237      * @return     Search query
238      */
239     public function getSearch()
[2147]240     {
241          $query = !empty($_REQUEST['m_search']) ? trim($_REQUEST['m_search']) : null;
[2227]242          return strlen($query) > 2 ? $query : null;
[2147]243     }
244
[2227]245     /**
246      * Display searh form.
247      *
248      * @return     adminModulesList self instance
249      */
250     public function displaySearch()
[2147]251     {
[2227]252          $query = $this->getSearch();
[2163]253
[2215]254          if (empty($this->data) && $query === null) {
[2147]255               return $this;
256          }
257
258          echo 
[2227]259          '<form action="'.$this->getURL().'" method="get" class="fieldset">'.
[2157]260          '<p><label for="m_search" class="classic">'.__('Search in repository:').'&nbsp;</label><br />'.
261          form::field(array('m_search','m_search'), 30, 255, html::escapeHTML($query)).
[2158]262          '<input type="submit" value="'.__('Search').'" /> ';
[2227]263          if ($query) { echo ' <a href="'.$this->getURL().'" class="button">'.__('Reset search').'</a>'; }
[2158]264          echo '</p>'.
265          '</form>';
[2147]266
267          if ($query) {
268               echo 
[2158]269               '<p class="message">'.sprintf(
[2215]270                    __('Found %d result for search "%s":', 'Found %d results for search "%s":', count($this->data)), 
271                    count($this->data), html::escapeHTML($query)
[2158]272                    ).
[2147]273               '</p>';
274          }
275          return $this;
276     }
[2227]277     //@}
[2147]278
[2227]279     /// @name Navigation menu methods
280     //@{
281     /**
282      * Set navigation special index.
283      *
284      * @return     adminModulesList self instance
285      */
286     public function setIndex($str)
[2147]287     {
288          $this->nav_special = (string) $str;
289          $this->nav_list = array_merge(str_split(self::$nav_indexes), array($this->nav_special));
290
291          return $this;
292     }
293
[2227]294     /**
295      * Get index from query.
296      *
297      * @return     Query index or default one
298      */
299     public function getIndex()
[2147]300     {
301          return isset($_REQUEST['m_nav']) && in_array($_REQUEST['m_nav'], $this->nav_list) ? $_REQUEST['m_nav'] : $this->nav_list[0];
302     }
303
[2227]304     /**
305      * Display navigation by index menu.
306      *
307      * @return     adminModulesList self instance
308      */
309     public function displayIndex()
[2147]310     {
[2227]311          if (empty($this->data) || $this->getSearch() !== null) {
[2147]312               return $this;
313          }
314
315          # Fetch modules required field
316          $indexes = array();
[2215]317          foreach ($this->data as $id => $module) {
[2147]318               if (!isset($module[$this->sort_field])) {
319                    continue;
320               }
321               $char = substr($module[$this->sort_field], 0, 1);
322               if (!in_array($char, $this->nav_list)) {
323                    $char = $this->nav_special;
324               }
325               if (!isset($indexes[$char])) {
326                    $indexes[$char] = 0;
327               }
328               $indexes[$char]++;
329          }
330
331          $buttons = array();
332          foreach($this->nav_list as $char) {
333               # Selected letter
[2227]334               if ($this->getIndex() == $char) {
[2147]335                    $buttons[] = '<li class="active" title="'.__('current selection').'"><strong> '.$char.' </strong></li>';
336               }
337               # Letter having modules
338               elseif (!empty($indexes[$char])) {
339                    $title = sprintf(__('%d module', '%d modules', $indexes[$char]), $indexes[$char]);
[2227]340                    $buttons[] = '<li class="btn" title="'.$title.'"><a href="'.$this->getURL('m_nav='.$char).'" title="'.$title.'"> '.$char.' </a></li>';
[2147]341               }
342               # Letter without modules
343               else {
344                    $buttons[] = '<li class="btn no-link" title="'.__('no module').'"> '.$char.' </li>';
345               }
346          }
347          # Parse navigation menu
[2157]348          echo '<div class="pager">'.__('Browse index:').' <ul>'.implode('',$buttons).'</ul></div>';
[2147]349
350          return $this;
351     }
[2227]352     //@}
[2147]353
[2227]354     /// @name Sort methods
355     //@{
356     /**
357      * Set default sort field.
358      *
359      * @return     adminModulesList self instance
360      */
361     public function setSort($field, $asc=true)
[2147]362     {
363          $this->sort_field = $field;
364          $this->sort_asc = (boolean) $asc;
365
366          return $this;
367     }
368
[2227]369     /**
370      * Get sort field from query.
371      *
372      * @return     Query sort field or default one
373      */
374     public function getSort()
[2147]375     {
376          return !empty($_REQUEST['m_sort']) ? $_REQUEST['m_sort'] : $this->sort_field;
377     }
378
[2227]379     /**
380      * Display sort field form.
381      *
382      * @note  This method is not implemented yet
383      * @return     adminModulesList self instance
384      */
385     public function displaySort()
[2147]386     {
[2227]387          //
388
389          return $this;
[2147]390     }
[2227]391     //@}
[2147]392
[2227]393     /// @name Modules methods
394     //@{
395     /**
396      * Set modules and sanitize them.
397      *
398      * @return     adminModulesList self instance
399      */
[2147]400     public function setModules($modules)
401     {
[2215]402          $this->data = array();
[2174]403          if (!empty($modules) && is_array($modules)) {
404               foreach($modules as $id => $module) {
[2227]405                    $this->data[$id] = self::sanitizeModule($id, $module);
[2174]406               }
[2147]407          }
408          return $this;
409     }
410
[2227]411     /**
412      * Get modules currently set.
413      *
414      * @return     Array of modules
415      */
[2147]416     public function getModules()
417     {
[2215]418          return $this->data;
[2147]419     }
420
[2227]421     /**
422      * Sanitize a module.
423      *
424      * This clean infos of a module by adding default keys
425      * and clean some of them, sanitize module can safely
426      * be used in lists.
427      *
428      * @return     Array of the module informations
429      */
430     public static function sanitizeModule($id, $module)
[2147]431     {
432          $label = empty($module['label']) ? $id : $module['label'];
433          $name = __(empty($module['name']) ? $label : $module['name']);
434         
435          return array_merge(
436               # Default values
437               array(
438                    'desc'                   => '',
439                    'author'            => '',
440                    'version'                => 0,
441                    'current_version'   => 0,
442                    'root'                   => '',
443                    'root_writable'     => false,
444                    'permissions'       => null,
445                    'parent'            => null,
446                    'priority'               => 1000,
[2156]447                    'standalone_config' => false,
448                    'support'                => '',
449                    'section'                => '',
450                    'tags'                   => '',
[2171]451                    'details'                => '',
[2222]452                    'sshot'             => '',
453                    'score'                  => 0
[2147]454               ),
455               # Module's values
456               $module,
457               # Clean up values
458               array(
459                    'id'                     => $id,
460                    'sid'                    => self::sanitizeString($id),
461                    'label'             => $label,
462                    'name'                   => $name,
463                    'sname'             => self::sanitizeString($name)
464               )
465          );
466     }
467
[2227]468     /**
469      * Check if a module is part of the distribution.
470      *
471      * @param string    $id       Module root directory
472      * @return     True if module is part of the distribution
473      */
474     public static function isDistributedModule($id)
[2148]475     {
[2171]476          $distributed_modules = self::$distributed_modules;
477
[2227]478          return is_array($distributed_modules) && in_array($id, $distributed_modules);
[2148]479     }
480
[2227]481     /**
482      * Sort modules list by specific field.
483      *
484      * @param string    $module        Array of modules
485      * @param string    $field         Field to sort from
486      * @param bollean   $asc      Sort asc if true, else decs
487      * @return     Array of sorted modules
488      */
[2147]489     public static function sortModules($modules, $field, $asc=true)
490     {
491          $sorter = array();
492          foreach($modules as $id => $module) {
493               $sorter[$id] = isset($module[$field]) ? $module[$field] : $field;
494          }
495          array_multisort($sorter, $asc ? SORT_ASC : SORT_DESC, $modules);
496
497          return $modules;
498     }
499
[2227]500     /**
501      * Display list of modules.
502      *
503      * @param array     $cols          List of colones (module field) to display
504      * @param array     $actions  List of predefined actions to show on form
505      * @param boolean   $nav_limit     Limit list to previously selected index
506      * @return     adminModulesList self instance
507      */
508     public function displayModules($cols=array('name', 'version', 'desc'), $actions=array(), $nav_limit=false)
[2147]509     {
510          echo 
511          '<div class="table-outer">'.
[2156]512          '<table id="'.html::escapeHTML($this->list_id).'" class="modules'.(in_array('expander', $cols) ? ' expandable' : '').'">'.
513          '<caption class="hidden">'.html::escapeHTML(__('Modules list')).'</caption><tr>';
[2163]514
[2147]515          if (in_array('name', $cols)) {
516               echo 
[2149]517               '<th class="first nowrap"'.(in_array('icon', $cols) ? ' colspan="2"' : '').'>'.__('Name').'</th>';
[2147]518          }
519
[2227]520          if (in_array('score', $cols) && $this->getSearch() !== null && defined('DC_DEBUG') && DC_DEBUG) {
[2222]521               echo 
522               '<th class="nowrap">'.__('Score').'</th>';
523          }
524
[2147]525          if (in_array('version', $cols)) {
526               echo 
527               '<th class="nowrap count" scope="col">'.__('Version').'</th>';
528          }
529
530          if (in_array('current_version', $cols)) {
531               echo 
532               '<th class="nowrap count" scope="col">'.__('Current version').'</th>';
533          }
534
535          if (in_array('desc', $cols)) {
536               echo 
537               '<th class="nowrap" scope="col">'.__('Details').'</th>';
538          }
539
[2149]540          if (in_array('distrib', $cols)) {
541               echo '<th'.(in_array('desc', $cols) ? '' : ' class="maximal"').'></th>';
542          }
543
[2147]544          if (!empty($actions) && $this->core->auth->isSuperAdmin()) {
545               echo 
546               '<th class="minimal nowrap">'.__('Action').'</th>';
547          }
548
549          echo 
550          '</tr>';
551
[2227]552          $sort_field = $this->getSort();
[2147]553
[2215]554          # Sort modules by $sort_field (default sname)
[2227]555          $modules = $this->getSearch() === null ?
[2215]556               self::sortModules($this->data, $sort_field, $this->sort_asc) :
557               $this->data;
[2147]558
559          $count = 0;
560          foreach ($modules as $id => $module)
561          {
562               # Show only requested modules
[2227]563               if ($nav_limit && $this->getSearch() === null) {
[2147]564                    $char = substr($module[$sort_field], 0, 1);
565                    if (!in_array($char, $this->nav_list)) {
566                         $char = $this->nav_special;
567                    }
[2227]568                    if ($this->getIndex() != $char) {
[2147]569                         continue;
570                    }
571               }
572
573               echo 
[2171]574               '<tr class="line" id="'.html::escapeHTML($this->list_id).'_m_'.html::escapeHTML($id).'">';
[2163]575
[2156]576               if (in_array('icon', $cols)) {
577                    echo 
[2171]578                    '<td class="module-icon nowrap">'.sprintf(
[2156]579                         '<img alt="%1$s" title="%1$s" src="%2$s" />', 
580                         html::escapeHTML($id), file_exists($module['root'].'/icon.png') ? 'index.php?pf='.$id.'/icon.png' : 'images/module.png'
581                    ).'</td>';
582               }
583
[2147]584               # Link to config file
585               $config = in_array('config', $cols) && !empty($module['root']) && file_exists(path::real($module['root'].'/_config.php'));
586
587               echo 
[2171]588               '<td class="module-name nowrap" scope="row">'.($config ? 
[2227]589                    '<a href="'.$this->getURL('module='.$id.'&conf=1').'" title"'.sprintf(__('Configure module "%s"'), html::escapeHTML($module['name'])).'">'.html::escapeHTML($module['name']).'</a>' : 
[2149]590                    html::escapeHTML($module['name'])
[2147]591               ).'</td>';
592
[2222]593               # Display score only for debug purpose
[2227]594               if (in_array('score', $cols) && $this->getSearch() !== null && defined('DC_DEBUG') && DC_DEBUG) {
[2222]595                    echo 
596                    '<td class="module-version nowrap count"><span class="debug">'.$module['score'].'</span></td>';
597               }
598
[2147]599               if (in_array('version', $cols)) {
600                    echo 
[2171]601                    '<td class="module-version nowrap count">'.html::escapeHTML($module['version']).'</td>';
[2147]602               }
603
[2148]604               if (in_array('current_version', $cols)) {
[2147]605                    echo 
[2171]606                    '<td class="module-current-version nowrap count">'.html::escapeHTML($module['current_version']).'</td>';
[2147]607               }
608
609               if (in_array('desc', $cols)) {
610                    echo 
[2171]611                    '<td class="module-desc maximal">'.html::escapeHTML($module['desc']).'</td>';
[2149]612               }
613
614               if (in_array('distrib', $cols)) {
615                    echo 
[2171]616                    '<td class="module-distrib">'.(self::isDistributedModule($id) ? 
[2149]617                         '<img src="images/dotclear_pw.png" alt="'.
618                         __('Module from official distribution').'" title="'.
619                         __('module from official distribution').'" />' 
620                    : '').'</td>';
[2147]621               }
622
623               if (!empty($actions) && $this->core->auth->isSuperAdmin()) {
[2215]624                    $buttons = $this->getActions($id, $module, $actions);
625
[2147]626                    echo 
[2215]627                    '<td class="module-actions nowrap">'.
[2147]628
[2227]629                    '<form action="'.$this->getURL().'" method="post">'.
[2215]630                    '<div>'.
631                    $this->core->formNonce().
632                    form::hidden(array('module'), html::escapeHTML($id)).
[2147]633
[2215]634                    implode(' ', $buttons).
635
636                    '</div>'.
637                    '</form>'.
638
[2147]639                    '</td>';
640               }
641
642               echo 
643               '</tr>';
644
645               $count++;
646          }
647          echo 
648          '</table></div>';
649
[2227]650          if(!$count && $this->getSearch() === null) {
[2147]651               echo 
[2158]652               '<p class="message">'.__('No module matches your search.').'</p>';
[2147]653          }
[2227]654
655          return $this;
[2147]656     }
657
[2227]658     /**
659      * Get action buttons to add to modules list.
660      *
661      * @param string    $id            Module ID
662      * @param array     $module        Module info
663      * @param array     $actions  Actions keys
664      * @return     Array of actions buttons
665      */
[2215]666     protected function getActions($id, $module, $actions)
[2147]667     {
668          $submits = array();
669
[2215]670          # Use loop to keep requested order
671          foreach($actions as $action) {
672               switch($action) {
673
674                    # Deactivate
675                    case 'activate': if ($module['root_writable']) {
676                         $submits[] = 
677                         '<input type="submit" name="activate" value="'.__('Activate').'" />';
678                    } break;
679
680                    # Activate
681                    case 'deactivate': if ($module['root_writable']) {
682                         $submits[] = 
683                         '<input type="submit" name="deactivate" value="'.__('Deactivate').'" class="reset" />';
684                    } break;
685
686                    # Delete
[2227]687                    case 'delete': if ($this->isDeletablePath($module['root'])) {
[2241]688                         $dev = !preg_match('!^'.$this->path_pattern.'!', $module['root']) && defined('DC_DEV') && DC_DEV ? ' debug' : '';
[2215]689                         $submits[] = 
[2241]690                         '<input type="submit" class="delete '.$dev.'" name="delete" value="'.__('Delete').'" />';
[2215]691                    } break;
692
[2216]693                    # Install (from store)
[2215]694                    case 'install': if ($this->path_writable) {
695                         $submits[] = 
696                         '<input type="submit" name="install" value="'.__('Install').'" />';
697                    } break;
698
[2216]699                    # Update (from store)
[2215]700                    case 'update': if ($this->path_writable) {
701                         $submits[] = 
702                         '<input type="submit" name="update" value="'.__('Update').'" />';
703                    } break;
[2241]704
705                    # Behavior
706                    case 'behavior':
707
708                         # --BEHAVIOR-- adminModulesListGetActions
709                         $tmp = $this->core->callBehavior('adminModulesListGetActions', $this, $id, $module);
710
711                         if (!empty($tmp)) {
712                              $submits[] = $tmp;
713                         }
714                    break;
[2215]715               }
[2192]716          }
717
[2241]718
[2215]719          return $submits;
[2147]720     }
721
[2227]722     /**
723      * Execute POST action.
724      *
725      * @note  Set a notice on success through dcPage::addSuccessNotice
726      * @throw Exception Module not find or command failed
727      * @param string    $prefix        Prefix used on behaviors
728      * @return     Null
729      */
[2215]730     public function doActions($prefix)
[2192]731     {
[2215]732          if (empty($_POST) || !empty($_REQUEST['conf']) 
[2227]733          || !$this->core->auth->isSuperAdmin() || !$this->isWritablePath()) {
[2149]734               return null;
735          }
736
[2171]737          # List actions
738          if (!empty($_POST['module'])) {
[2149]739
[2171]740               $id = $_POST['module'];
[2149]741
[2171]742               if (!empty($_POST['activate'])) {
743
[2215]744                    $enabled = $this->modules->getDisabledModules();
[2171]745                    if (!isset($enabled[$id])) {
746                         throw new Exception(__('No such module.'));
747                    }
748
749                    # --BEHAVIOR-- moduleBeforeActivate
[2192]750                    $this->core->callBehavior($prefix.'BeforeActivate', $id);
[2171]751
[2215]752                    $this->modules->activateModule($id);
[2171]753
754                    # --BEHAVIOR-- moduleAfterActivate
[2192]755                    $this->core->callBehavior($prefix.'AfterActivate', $id);
[2171]756
[2219]757                    dcPage::addSuccessNotice(__('Module has been successfully activated.'));
[2227]758                    http::redirect($this->getURL());
[2149]759               }
760
[2192]761               elseif (!empty($_POST['deactivate'])) {
[2149]762
[2215]763                    if (!$this->modules->moduleExists($id)) {
[2149]764                         throw new Exception(__('No such module.'));
765                    }
766
[2215]767                    $module = $this->modules->getModules($id);
[2149]768                    $module['id'] = $id;
769
[2171]770                    if (!$module['root_writable']) {
771                         throw new Exception(__('You don\'t have permissions to deactivate this module.'));
[2149]772                    }
773
[2171]774                    # --BEHAVIOR-- moduleBeforeDeactivate
775                    $this->core->callBehavior($prefix.'BeforeDeactivate', $module);
[2149]776
[2215]777                    $this->modules->deactivateModule($id);
[2149]778
[2171]779                    # --BEHAVIOR-- moduleAfterDeactivate
780                    $this->core->callBehavior($prefix.'AfterDeactivate', $module);
781
[2219]782                    dcPage::addSuccessNotice(__('Module has been successfully deactivated.'));
[2227]783                    http::redirect($this->getURL());
[2171]784               }
785
[2192]786               elseif (!empty($_POST['delete'])) {
[2171]787
[2215]788                    $disabled = $this->modules->getDisabledModules();
[2171]789                    if (!isset($disabled[$id])) {
790
[2215]791                         if (!$this->modules->moduleExists($id)) {
[2171]792                              throw new Exception(__('No such module.'));
793                         }
794
[2215]795                         $module = $this->modules->getModules($id);
[2171]796                         $module['id'] = $id;
797
[2227]798                         if (!$this->isDeletablePath($module['root'])) {
[2171]799                              throw new Exception(__("You don't have permissions to delete this module."));
800                         }
801
802                         # --BEHAVIOR-- moduleBeforeDelete
803                         $this->core->callBehavior($prefix.'BeforeDelete', $module);
804
[2215]805                         $this->modules->deleteModule($id);
[2171]806
807                         # --BEHAVIOR-- moduleAfterDelete
808                         $this->core->callBehavior($prefix.'AfterDelete', $module);
809                    }
810                    else {
[2215]811                         $this->modules->deleteModule($id, true);
[2171]812                    }
813
[2219]814                    dcPage::addSuccessNotice(__('Module has been successfully deleted.'));
[2227]815                    http::redirect($this->getURL());
[2171]816               }
817
[2215]818               elseif (!empty($_POST['install'])) {
819
[2216]820                    $updated = $this->store->get();
[2215]821                    if (!isset($updated[$id])) {
822                         throw new Exception(__('No such module.'));
823                    }
824
825                    $module = $updated[$id];
826                    $module['id'] = $id;
827
828                    $dest = $this->getPath().'/'.basename($module['file']);
829
830                    # --BEHAVIOR-- moduleBeforeAdd
831                    $this->core->callBehavior($prefix.'BeforeAdd', $module);
832
[2216]833                    $ret_code = $this->store->process($module['file'], $dest);
[2215]834
835                    # --BEHAVIOR-- moduleAfterAdd
836                    $this->core->callBehavior($prefix.'AfterAdd', $module);
837
[2219]838                    dcPage::addSuccessNotice($ret_code == 2 ?
839                         __('Module has been successfully updated.') :
840                         __('Module has been successfully installed.')
841                    );
[2227]842                    http::redirect($this->getURL());
[2215]843               }
844
[2192]845               elseif (!empty($_POST['update'])) {
[2171]846
[2241]847                    $updated = $this->store->get();
[2171]848                    if (!isset($updated[$id])) {
849                         throw new Exception(__('No such module.'));
850                    }
851
[2215]852                    if (!$this->modules->moduleExists($id)) {
[2171]853                         throw new Exception(__('No such module.'));
854                    }
855
856                    $module = $updated[$id];
857                    $module['id'] = $id;
[2227]858
[2171]859                    if (!self::$allow_multi_install) {
860                         $dest = $module['root'].'/../'.basename($module['file']);
861                    }
862                    else {
863                         $dest = $this->getPath().'/'.basename($module['file']);
864                         if ($module['root'] != $dest) {
865                              @file_put_contents($module['root'].'/_disabled', '');
866                         }
867                    }
868
869                    # --BEHAVIOR-- moduleBeforeUpdate
[2192]870                    $this->core->callBehavior($prefix.'BeforeUpdate', $module);
[2171]871
[2216]872                    $this->store->process($module['file'], $dest);
[2171]873
874                    # --BEHAVIOR-- moduleAfterUpdate
[2192]875                    $this->core->callBehavior($prefix.'AfterUpdate', $module);
[2171]876
[2219]877                    dcPage::addSuccessNotice(__('Module has been successfully updated.'));
[2227]878                    http::redirect($this->getURL());
[2171]879               }
[2241]880               else {
881
882                    # --BEHAVIOR-- adminModulesListDoActions
883                    $this->core->callBehavior('adminModulesListDoActions', $this, $id, $prefix);
884
885               }
[2171]886          }
887          # Manual actions
888          elseif (!empty($_POST['upload_pkg']) && !empty($_FILES['pkg_file']) 
889               || !empty($_POST['fetch_pkg']) && !empty($_POST['pkg_url']))
890          {
891               if (empty($_POST['your_pwd']) || !$this->core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY, $_POST['your_pwd']))) {
892                    throw new Exception(__('Password verification failed'));
893               }
894
895               if (!empty($_POST['upload_pkg'])) {
896                    files::uploadStatus($_FILES['pkg_file']);
897                   
898                    $dest = $this->getPath().'/'.$_FILES['pkg_file']['name'];
899                    if (!move_uploaded_file($_FILES['pkg_file']['tmp_name'], $dest)) {
900                         throw new Exception(__('Unable to move uploaded file.'));
901                    }
[2149]902               }
903               else {
[2171]904                    $url = urldecode($_POST['pkg_url']);
905                    $dest = $this->getPath().'/'.basename($url);
[2216]906                    $this->store->download($url, $dest);
[2149]907               }
908
[2171]909               # --BEHAVIOR-- moduleBeforeAdd
910               $this->core->callBehavior($prefix.'BeforeAdd', null);
911
[2216]912               $ret_code = $this->store->install($dest);
[2171]913
914               # --BEHAVIOR-- moduleAfterAdd
915               $this->core->callBehavior($prefix.'AfterAdd', null);
916
[2219]917               dcPage::addSuccessNotice($ret_code == 2 ?
918                    __('Module has been successfully updated.') :
919                    __('Module has been successfully installed.')
920               );
[2227]921               http::redirect($this->getURL().'#'.$prefix);
[2171]922          }
[2192]923
[2171]924          return null;
925     }
926
[2227]927     /**
928      * Display tab for manual installation.
929      *
930      * @return     adminModulesList self instance
931      */
[2171]932     public function displayManualForm()
933     {
[2227]934          if (!$this->core->auth->isSuperAdmin() || !$this->isWritablePath()) {
[2171]935               return null;
[2149]936          }
937
[2171]938          # 'Upload module' form
939          echo
[2227]940          '<form method="post" action="'.$this->getURL().'" id="uploadpkg" enctype="multipart/form-data" class="fieldset">'.
[2171]941          '<h4>'.__('Upload a zip file').'</h4>'.
942          '<p class="field"><label for="pkg_file" class="classic required"><abbr title="'.__('Required field').'">*</abbr> '.__('Zip file path:').'</label> '.
943          '<input type="file" name="pkg_file" id="pkg_file" /></p>'.
944          '<p class="field"><label for="your_pwd1" class="classic required"><abbr title="'.__('Required field').'">*</abbr> '.__('Your password:').'</label> '.
945          form::password(array('your_pwd','your_pwd1'),20,255).'</p>'.
946          '<p><input type="submit" name="upload_pkg" value="'.__('Upload').'" />'.
947          $this->core->formNonce().'</p>'.
948          '</form>';
[2227]949
[2171]950          # 'Fetch module' form
951          echo
[2227]952          '<form method="post" action="'.$this->getURL().'" id="fetchpkg" class="fieldset">'.
[2171]953          '<h4>'.__('Download a zip file').'</h4>'.
954          '<p class="field"><label for="pkg_url" class="classic required"><abbr title="'.__('Required field').'">*</abbr> '.__('Zip file URL:').'</label> '.
955          form::field(array('pkg_url','pkg_url'),40,255).'</p>'.
956          '<p class="field"><label for="your_pwd2" class="classic required"><abbr title="'.__('Required field').'">*</abbr> '.__('Your password:').'</label> '.
957          form::password(array('your_pwd','your_pwd2'),20,255).'</p>'.
958          '<p><input type="submit" name="fetch_pkg" value="'.__('Download').'" />'.
959          $this->core->formNonce().'</p>'.
960          '</form>';
[2227]961
962          return $this;
[2149]963     }
[2227]964     //@}
[2149]965
[2227]966     /// @name Module configuration methods
967     //@{
[2182]968     /**
[2227]969      * Prepare module configuration.
[2182]970      *
971      * We need to get configuration content in three steps
972      * and out of this class to keep backward compatibility.
973      *
[2227]974      * if ($xxx->setConfiguration()) {
975      *   include $xxx->includeConfiguration();
[2182]976      * }
[2227]977      * $xxx->getConfiguration();
[2182]978      * ... [put here page headers and other stuff]
[2227]979      * $xxx->displayConfiguration();
[2182]980      *
[2227]981      * @param string    $id       Module to work on or it gather through REQUEST
982      * @return     True if config set
[2182]983      */
[2227]984     public function setConfiguration($id=null)
[2182]985     {
986          if (empty($_REQUEST['conf']) || empty($_REQUEST['module']) && !$id) {
987               return false;
988          }
989         
990          if (!empty($_REQUEST['module']) && empty($id)) {
991               $id = $_REQUEST['module'];
992          }
993
[2215]994          if (!$this->modules->moduleExists($id)) {
[2182]995               $core->error->add(__('Unknow module ID'));
996               return false;
997          }
998
[2215]999          $module = $this->modules->getModules($id);
[2227]1000          $module = self::sanitizeModule($id, $module);
[2182]1001          $file = path::real($module['root'].'/_config.php');
1002
1003          if (!file_exists($file)) {
1004               $core->error->add(__('This module has no configuration file.'));
1005               return false;
1006          }
1007
1008          $this->config_module = $module;
1009          $this->config_file = $file;
1010          $this->config_content = '';
1011
1012          if (!defined('DC_CONTEXT_MODULE')) {
1013               define('DC_CONTEXT_MODULE', true);
1014          }
1015
1016          return true;
1017     }
1018
[2227]1019     /**
1020      * Get path of module configuration file.
1021      *
1022      * @note Required previously set file info
1023      * @return Full path of config file or null
1024      */
1025     public function includeConfiguration()
[2182]1026     {
1027          if (!$this->config_file) {
1028               return null;
1029          }
[2259]1030          $this->setRedir($this->getURL().'#plugins');
[2182]1031
1032          ob_start();
1033
1034          return $this->config_file;
1035     }
1036
[2227]1037     /**
1038      * Gather module configuration file content.
1039      *
1040      * @note Required previously file inclusion
1041      * @return True if content has been captured
1042      */
1043     public function getConfiguration()
[2182]1044     {
1045          if ($this->config_file) {
1046               $this->config_content = ob_get_contents();
1047          }
1048
1049          ob_end_clean();
1050
1051          return !empty($this->file_content);
1052     }
1053
[2227]1054     /**
1055      * Display module configuration form.
1056      *
1057      * @note Required previously gathered content
1058      * @return     adminModulesList self instance
1059      */
1060     public function displayConfiguration()
[2182]1061     {
[2227]1062          if ($this->config_file) {
1063
1064               if (!$this->config_module['standalone_config']) {
1065                    echo
1066                    '<form id="module_config" action="'.$this->getURL('conf=1').'" method="post" enctype="multipart/form-data">'.
1067                    '<h3>'.sprintf(__('Configure plugin "%s"'), html::escapeHTML($this->config_module['name'])).'</h3>'.
[2259]1068                    '<p><a class="back" href="'.$this->getRedir().'">'.__('Back').'</a></p>';
[2227]1069               }
1070
1071               echo $this->config_content;
1072
1073               if (!$this->config_module['standalone_config']) {
1074                    echo
1075                    '<p class="clear"><input type="submit" name="save" value="'.__('Save').'" />'.
1076                    form::hidden('module', $this->config_module['id']).
[2259]1077                    form::hidden('redir', $this->getRedir()).
[2227]1078                    $this->core->formNonce().'</p>'.
1079                    '</form>';
1080               }
[2182]1081          }
1082
[2227]1083          return $this;
1084     }
1085     //@}
[2182]1086
[2227]1087     /**
1088      * Helper to sanitize a string.
1089      *
1090      * Used for search or id.
1091      *
1092      * @param string    $str      String to sanitize
1093      * @return     Sanitized string
1094      */
[2147]1095     public static function sanitizeString($str)
1096     {
1097          return preg_replace('/[^A-Za-z0-9\@\#+_-]/', '', strtolower($str));
1098     }
1099}
1100
[2227]1101/**
1102 * @ingroup DC_CORE
1103 * @brief Helper to manage list of themes.
1104 * @since 2.6
1105 */
[2147]1106class adminThemesList extends adminModulesList
1107{
[2171]1108     protected $page_url = 'blog_theme.php';
[2147]1109
[2227]1110     public function displayModules($cols=array('name', 'config', 'version', 'desc'), $actions=array(), $nav_limit=false)
[2171]1111     {
1112          echo 
1113          '<div id="'.html::escapeHTML($this->list_id).'" class="modules'.(in_array('expander', $cols) ? ' expandable' : '').' one-box">';
1114
[2227]1115          $sort_field = $this->getSort();
[2171]1116
1117          # Sort modules by id
[2227]1118          $modules = $this->getSearch() === null ?
[2215]1119               self::sortModules($this->data, $sort_field, $this->sort_asc) :
1120               $this->data;
[2171]1121
1122          $res = '';
1123          $count = 0;
1124          foreach ($modules as $id => $module)
1125          {
1126               # Show only requested modules
[2227]1127               if ($nav_limit && $this->getSearch() === null) {
[2171]1128                    $char = substr($module[$sort_field], 0, 1);
1129                    if (!in_array($char, $this->nav_list)) {
1130                         $char = $this->nav_special;
1131                    }
[2227]1132                    if ($this->getIndex() != $char) {
[2171]1133                         continue;
1134                    }
1135               }
1136
[2227]1137               $current = $this->core->blog->settings->system->theme == $id && $this->modules->moduleExists($id);
[2215]1138               $distrib = self::isDistributedModule($id) ? ' dc-box' : '';
[2171]1139
1140               $line = 
[2215]1141               '<div class="box '.($current ? 'medium current-theme' : 'small theme').$distrib.'">';
[2171]1142
1143               if (in_array('name', $cols)) {
1144                    $line .= 
1145                    '<h4 class="module-name">'.html::escapeHTML($module['name']).'</h4>';
1146               }
1147
[2222]1148               # Display score only for debug purpose
[2227]1149               if (in_array('score', $cols) && $this->getSearch() !== null && defined('DC_DEBUG') && DC_DEBUG) {
[2222]1150                    $line .= 
1151                    '<p class="module-score debug">'.sprintf(__('Score: %s'), $module['score']).'</p>';
1152               }
1153
[2195]1154               if (in_array('sshot', $cols)) {
1155                    # Screenshot from url
1156                    if (preg_match('#^http(s)?://#', $module['sshot'])) {
1157                         $sshot = $module['sshot'];
1158                    }
1159                    # Screenshot from installed module
1160                    elseif (file_exists($this->core->blog->themes_path.'/'.$id.'/screenshot.jpg')) {
[2227]1161                         $sshot = $this->getURL('shot='.rawurlencode($id));
[2195]1162                    }
1163                    # Default screenshot
1164                    else {
1165                         $sshot = 'images/noscreenshot.png';
1166                    }
1167
1168                    $line .= 
[2215]1169                    '<div class="module-sshot"><img src="'.$sshot.'" alt="'.
1170                    sprintf(__('%s screenshot.'), html::escapeHTML($module['name'])).'" /></div>';
[2195]1171               }
1172
[2171]1173               $line .= 
[2195]1174               '<div class="module-infos toggle-bloc">'.
[2171]1175               '<p>';
1176
1177               if (in_array('desc', $cols)) {
1178                    $line .= 
1179                    '<span class="module-desc">'.html::escapeHTML($module['desc']).'</span> ';
1180               }
1181
1182               if (in_array('author', $cols)) {
1183                    $line .= 
1184                    '<span class="module-author">'.sprintf(__('by %s'),html::escapeHTML($module['author'])).'</span> ';
1185               }
1186
1187               if (in_array('version', $cols)) {
1188                    $line .= 
1189                    '<span class="module-version">'.sprintf(__('version %s'),html::escapeHTML($module['version'])).'</span> ';
1190               }
1191
[2222]1192               if (in_array('current_version', $cols)) {
1193                    $line .= 
1194                    '<span class="module-current-version">'.sprintf(__('(current version %s)'),html::escapeHTML($module['current_version'])).'</span> ';
1195               }
1196
[2227]1197               if (in_array('parent', $cols) && !empty($module['parent'])) {
1198                    if ($this->modules->moduleExists($module['parent'])) {
[2171]1199                         $line .= 
[2227]1200                         '<span class="module-parent-ok">'.sprintf(__('(built on "%s")'),html::escapeHTML($module['parent'])).'</span> ';
[2171]1201                    }
1202                    else {
1203                         $line .= 
[2227]1204                         '<span class="module-parent-missing">'.sprintf(__('(requires "%s")'),html::escapeHTML($module['parent'])).'</span> ';
[2171]1205                    }
1206               }
1207
[2215]1208               $has_details = in_array('details', $cols) && !empty($module['details']);
1209               $has_support = in_array('support', $cols) && !empty($module['support']);
1210               if ($has_details || $has_support) {
1211                    $line .=
1212                    '<span class="mod-more">'.__('Help:').' ';
1213
1214                    if ($has_details) {
1215                         $line .= 
1216                         '<a class="module-details" href="'.$module['details'].'">'.__('Details').'</a>';
1217                    }
1218
1219                    if ($has_support) {
1220                         $line .= 
1221                         ' - <a class="module-support" href="'.$module['support'].'">'.__('Support').'</a>';
1222                    }
1223
1224                    $line .=
1225                    '</span>';
1226               }
1227
[2171]1228               $line .= 
1229               '</p>'.
1230               '</div>';
1231
1232               $line .= 
[2195]1233               '<div class="module-actions toggle-bloc">';
[2171]1234               
1235               # Plugins actions
1236               if ($current) {
[2227]1237
1238                    # _GET actions
1239                    if (file_exists(path::real($this->core->blog->themes_path.'/'.$id).'/style.css')) {
1240                         $theme_url = preg_match('#^http(s)?://#', $this->core->blog->settings->system->themes_url) ?
1241                              http::concatURL($this->core->blog->settings->system->themes_url, '/'.$id) :
1242                              http::concatURL($this->core->blog->url, $this->core->blog->settings->system->themes_url.'/'.$id);
1243                         $line .= 
1244                         '<p><a href="'.$theme_url.'/style.css">'.__('View stylesheet').'</a></p>';
1245                    }
1246
1247                    if (file_exists(path::real($this->core->blog->themes_path.'/'.$id).'/_config.php')) {
1248                         $line .= 
1249                         '<p><a href="'.$this->getURL('module='.$id.'&conf=1', false).'" class="button">'.__('Configure theme').'</a></p>';
1250                    }
1251
[2171]1252                    # --BEHAVIOR-- adminCurrentThemeDetails
1253                    $line .= 
1254                    $this->core->callBehavior('adminCurrentThemeDetails', $this->core, $id, $module);
1255               }
1256
1257               # _POST actions
[2215]1258               if (!empty($actions)) {
[2171]1259                    $line .=
[2227]1260                    '<form action="'.$this->getURL().'" method="post">'.
[2215]1261                    '<div>'.
1262                    $this->core->formNonce().
1263                    form::hidden(array('module'), html::escapeHTML($id)).
1264
1265                    implode(' ', $this->getActions($id, $module, $actions)).
1266 
1267                    '</div>'.
1268                    '</form>';
[2171]1269               }
1270
1271               $line .= 
1272               '</div>';
1273
1274               $line .=
1275               '</div>';
1276
1277               $count++;
1278
1279               $res = $current ? $line.$res : $res.$line;
1280          }
1281          echo 
1282          $res.
1283          '</div>';
1284
[2227]1285          if(!$count && $this->getSearch() === null) {
[2171]1286               echo 
1287               '<p class="message">'.__('No module matches your search.').'</p>';
1288          }
1289     }
[2192]1290
[2215]1291     protected function getActions($id, $module, $actions)
[2192]1292     {
1293          $submits = array();
1294
1295          $this->core->blog->settings->addNamespace('system');
[2215]1296          if ($id != $this->core->blog->settings->system->theme) {
1297
1298               # Select theme to use on curent blog
1299               if (in_array('select', $actions) && $this->path_writable) {
[2227]1300                    $submits[] = 
1301                    '<input type="submit" name="select" value="'.__('Use this one').'" />';
[2215]1302               }
[2192]1303          }
1304
[2215]1305          return array_merge(
1306               $submits,
1307               parent::getActions($id, $module, $actions)
1308          );
1309     }
1310
1311     public function doActions($prefix)
1312     {
[2227]1313          if (!empty($_POST) && empty($_REQUEST['conf']) && $this->isWritablePath()) {
[2215]1314
1315               # Select theme to use on curent blog
1316               if (!empty($_POST['module']) && !empty($_POST['select'])) {
1317                    $id = $_POST['module'];
1318
1319                    if (!$this->modules->moduleExists($id)) {
1320                         throw new Exception(__('No such module.'));
1321                    }
1322
1323                    $this->core->blog->settings->addNamespace('system');
1324                    $this->core->blog->settings->system->put('theme',$id);
1325                    $this->core->blog->triggerBlog();
1326
[2219]1327                    dcPage::addSuccessNotice(__('Module has been successfully selected.'));
[2227]1328                    http::redirect($this->getURL().'#themes');
[2215]1329               }
[2192]1330          }
1331
[2215]1332          return parent::doActions($prefix);
[2192]1333     }
[2147]1334}
Note: See TracBrowser for help on using the repository browser.

Sites map