| 1 | <?php | 
|---|
| 2 | # -- BEGIN LICENSE BLOCK --------------------------------------- | 
|---|
| 3 | # | 
|---|
| 4 | # This file is part of Dotclear 2. | 
|---|
| 5 | # | 
|---|
| 6 | # Copyright (c) 2003-2013 Olivier Meunier & Association Dotclear | 
|---|
| 7 | # Licensed under the GPL version 2.0 license. | 
|---|
| 8 | # See LICENSE file or | 
|---|
| 9 | # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html | 
|---|
| 10 | # | 
|---|
| 11 | # -- END LICENSE BLOCK ----------------------------------------- | 
|---|
| 12 | if (!defined('DC_RC_PATH')) {return;} | 
|---|
| 13 |  | 
|---|
| 14 | /** | 
|---|
| 15 | @ingroup DC_CORE | 
|---|
| 16 | @brief Modules handler | 
|---|
| 17 |  | 
|---|
| 18 | Provides an object to handle modules (themes or plugins). | 
|---|
| 19 |  */ | 
|---|
| 20 | class dcModules | 
|---|
| 21 | { | 
|---|
| 22 |     protected $path; | 
|---|
| 23 |     protected $ns; | 
|---|
| 24 |     protected $modules       = array(); | 
|---|
| 25 |     protected $disabled      = array(); | 
|---|
| 26 |     protected $errors        = array(); | 
|---|
| 27 |     protected $modules_names = array(); | 
|---|
| 28 |     protected $all_modules   = array(); | 
|---|
| 29 |     protected $disabled_mode = false; | 
|---|
| 30 |     protected $disabled_meta = array(); | 
|---|
| 31 |     protected $to_disable    = array(); | 
|---|
| 32 |  | 
|---|
| 33 |     protected $id; | 
|---|
| 34 |     protected $mroot; | 
|---|
| 35 |  | 
|---|
| 36 |     # Inclusion variables | 
|---|
| 37 |     protected static $superglobals = array('GLOBALS', '_SERVER', '_GET', '_POST', '_COOKIE', '_FILES', '_ENV', '_REQUEST', '_SESSION'); | 
|---|
| 38 |     protected static $_k; | 
|---|
| 39 |     protected static $_n; | 
|---|
| 40 |  | 
|---|
| 41 |     protected static $type = null; | 
|---|
| 42 |  | 
|---|
| 43 |     public $core; ///< <b>dcCore</b>    dcCore instance | 
|---|
| 44 |  | 
|---|
| 45 |     /** | 
|---|
| 46 |     Object constructor. | 
|---|
| 47 |  | 
|---|
| 48 |     @param    core        <b>dcCore</b>    dcCore instance | 
|---|
| 49 |      */ | 
|---|
| 50 |     public function __construct($core) | 
|---|
| 51 |     { | 
|---|
| 52 |         $this->core = &$core; | 
|---|
| 53 |     } | 
|---|
| 54 |  | 
|---|
| 55 |     /** | 
|---|
| 56 |      * Checks all modules dependencies | 
|---|
| 57 |      *     Fills in the following information in module : | 
|---|
| 58 |      *       * cannot_enable : list reasons why module cannot be enabled. Not set if module can be enabled | 
|---|
| 59 |      *       * cannot_disable : list reasons why module cannot be disabled. Not set if module can be disabled | 
|---|
| 60 |      *       * implies : reverse dependencies | 
|---|
| 61 |      * @return array list of enabled modules with unmet dependencies, and that must be disabled. | 
|---|
| 62 |      */ | 
|---|
| 63 |     public function checkDependencies() | 
|---|
| 64 |     { | 
|---|
| 65 |         $dc_version       = preg_replace('/\-dev$/', '', DC_VERSION); | 
|---|
| 66 |         $this->to_disable = array(); | 
|---|
| 67 |         foreach ($this->all_modules as $k => &$m) { | 
|---|
| 68 |             if (isset($m['requires'])) { | 
|---|
| 69 |                 $missing = array(); | 
|---|
| 70 |                 foreach ($m['requires'] as &$dep) { | 
|---|
| 71 |                     if (!is_array($dep)) { | 
|---|
| 72 |                         $dep = array($dep); | 
|---|
| 73 |                     } | 
|---|
| 74 |                     // grab missing dependencies | 
|---|
| 75 |                     if (!isset($this->all_modules[$dep[0]]) && ($dep[0] != 'core')) { | 
|---|
| 76 |                         // module not present | 
|---|
| 77 |                         $missing[$dep[0]] = sprintf(__("Requires %s module which is not installed"), $dep[0]); | 
|---|
| 78 |                     } elseif ((count($dep) > 1) && | 
|---|
| 79 |                         version_compare(($dep[0] == 'core' ? $dc_version : $this->all_modules[$dep[0]]['version']), $dep[1]) == -1) { | 
|---|
| 80 |                         // module present, but version missing | 
|---|
| 81 |                         if ($dep[0] == 'core') { | 
|---|
| 82 |                             $missing[$dep[0]] = sprintf(__("Requires Dotclear version %s, but version %s is installed"), | 
|---|
| 83 |                                 $dep[1], $dc_version); | 
|---|
| 84 |                         } else { | 
|---|
| 85 |                             $missing[$dep[0]] = sprintf(__("Requires %s module version %s, but version %s is installed"), | 
|---|
| 86 |                                 $dep[0], $dep[1], $this->all_modules[$dep[0]]['version']); | 
|---|
| 87 |                         } | 
|---|
| 88 |                     } elseif (($dep[0] != 'core') && !$this->all_modules[$dep[0]]['enabled']) { | 
|---|
| 89 |                         // module disabled | 
|---|
| 90 |                         $missing[$dep[0]] = sprintf(__("Requires %s module which is disabled"), $dep[0]); | 
|---|
| 91 |                     } | 
|---|
| 92 |                     $this->all_modules[$dep[0]]['implies'][] = $k; | 
|---|
| 93 |                 } | 
|---|
| 94 |                 if (count($missing)) { | 
|---|
| 95 |                     $m['cannot_enable'] = $missing; | 
|---|
| 96 |                     if ($m['enabled']) { | 
|---|
| 97 |                         $this->to_disable[] = array('name' => $k, 'reason' => $missing); | 
|---|
| 98 |                     } | 
|---|
| 99 |                 } | 
|---|
| 100 |             } | 
|---|
| 101 |         } | 
|---|
| 102 |         // Check modules that cannot be disabled | 
|---|
| 103 |         foreach ($this->modules as $k => &$m) { | 
|---|
| 104 |             if (isset($m['implies']) && $m['enabled']) { | 
|---|
| 105 |                 foreach ($m['implies'] as $im) { | 
|---|
| 106 |                     if (isset($this->all_modules[$im]) && $this->all_modules[$im]['enabled']) { | 
|---|
| 107 |                         $m['cannot_disable'][] = $im; | 
|---|
| 108 |                     } | 
|---|
| 109 |                 } | 
|---|
| 110 |             } | 
|---|
| 111 |         } | 
|---|
| 112 |     } | 
|---|
| 113 |  | 
|---|
| 114 |     /** | 
|---|
| 115 |      * Checks all modules dependencies, and disable unmet dependencies | 
|---|
| 116 |      * @param  string $redir_url URL to redirect if modules are to disable | 
|---|
| 117 |      * @return boolean, true if a redirection has been performed | 
|---|
| 118 |      */ | 
|---|
| 119 |     public function disableDepModules($redir_url) | 
|---|
| 120 |     { | 
|---|
| 121 |         if (isset($_GET['dep'])) { | 
|---|
| 122 |             // Avoid infinite redirects | 
|---|
| 123 |             return false; | 
|---|
| 124 |         } | 
|---|
| 125 |         $reason = array(); | 
|---|
| 126 |         foreach ($this->to_disable as $module) { | 
|---|
| 127 |             try { | 
|---|
| 128 |                 $this->deactivateModule($module['name']); | 
|---|
| 129 |                 $reason[] = sprintf("<li>%s : %s</li>", $module['name'], join(',', $module['reason'])); | 
|---|
| 130 |             } catch (Exception $e) { | 
|---|
| 131 |             } | 
|---|
| 132 |         } | 
|---|
| 133 |         if (count($reason)) { | 
|---|
| 134 |             $message = sprintf("<p>%s</p><ul>%s</ul>", | 
|---|
| 135 |                 __('The following extensions have been disabled :'), | 
|---|
| 136 |                 join('', $reason) | 
|---|
| 137 |             ); | 
|---|
| 138 |             dcPage::addWarningNotice($message, array('divtag' => true, 'with_ts' => false)); | 
|---|
| 139 |             $url = $redir_url . (strpos($redir_url, "?") ? '&' : '?') . 'dep=1'; | 
|---|
| 140 |             http::redirect($url); | 
|---|
| 141 |             return true; | 
|---|
| 142 |         } | 
|---|
| 143 |         return false; | 
|---|
| 144 |     } | 
|---|
| 145 |  | 
|---|
| 146 |     /** | 
|---|
| 147 |     Loads modules. <var>$path</var> could be a separated list of paths | 
|---|
| 148 |     (path separator depends on your OS). | 
|---|
| 149 |  | 
|---|
| 150 |     <var>$ns</var> indicates if an additionnal file needs to be loaded on plugin | 
|---|
| 151 |     load, value could be: | 
|---|
| 152 |     - admin (loads module's _admin.php) | 
|---|
| 153 |     - public (loads module's _public.php) | 
|---|
| 154 |     - xmlrpc (loads module's _xmlrpc.php) | 
|---|
| 155 |  | 
|---|
| 156 |     <var>$lang</var> indicates if we need to load a lang file on plugin | 
|---|
| 157 |     loading. | 
|---|
| 158 |      */ | 
|---|
| 159 |     public function loadModules($path, $ns = null, $lang = null) | 
|---|
| 160 |     { | 
|---|
| 161 |         $this->path = explode(PATH_SEPARATOR, $path); | 
|---|
| 162 |         $this->ns   = $ns; | 
|---|
| 163 |  | 
|---|
| 164 |         $disabled = isset($_SESSION['sess_safe_mode']) && $_SESSION['sess_safe_mode']; | 
|---|
| 165 |         $disabled = $disabled && !get_parent_class($this) ? true : false; | 
|---|
| 166 |  | 
|---|
| 167 |         $ignored = array(); | 
|---|
| 168 |  | 
|---|
| 169 |         foreach ($this->path as $root) { | 
|---|
| 170 |             if (!is_dir($root) || !is_readable($root)) { | 
|---|
| 171 |                 continue; | 
|---|
| 172 |             } | 
|---|
| 173 |  | 
|---|
| 174 |             if (substr($root, -1) != '/') { | 
|---|
| 175 |                 $root .= '/'; | 
|---|
| 176 |             } | 
|---|
| 177 |  | 
|---|
| 178 |             if (($d = @dir($root)) === false) { | 
|---|
| 179 |                 continue; | 
|---|
| 180 |             } | 
|---|
| 181 |  | 
|---|
| 182 |             while (($entry = $d->read()) !== false) { | 
|---|
| 183 |                 $full_entry = $root . $entry; | 
|---|
| 184 |  | 
|---|
| 185 |                 if ($entry != '.' && $entry != '..' && is_dir($full_entry) | 
|---|
| 186 |                     && file_exists($full_entry . '/_define.php')) { | 
|---|
| 187 |                     if (!file_exists($full_entry . '/_disabled') && !$disabled) { | 
|---|
| 188 |                         $this->id    = $entry; | 
|---|
| 189 |                         $this->mroot = $full_entry; | 
|---|
| 190 |                         ob_start(); | 
|---|
| 191 |                         require $full_entry . '/_define.php'; | 
|---|
| 192 |                         ob_end_clean(); | 
|---|
| 193 |                         $this->all_modules[$entry] = &$this->modules[$entry]; | 
|---|
| 194 |                         $this->id                  = null; | 
|---|
| 195 |                         $this->mroot               = null; | 
|---|
| 196 |                     } else { | 
|---|
| 197 |                         if (file_exists($full_entry . '/_define.php')) { | 
|---|
| 198 |                             $this->id            = $entry; | 
|---|
| 199 |                             $this->mroot         = $full_entry; | 
|---|
| 200 |                             $this->disabled_mode = true; | 
|---|
| 201 |                             ob_start(); | 
|---|
| 202 |                             require $full_entry . '/_define.php'; | 
|---|
| 203 |                             ob_end_clean(); | 
|---|
| 204 |                             $this->disabled_mode       = false; | 
|---|
| 205 |                             $this->disabled[$entry]    = $this->disabled_meta; | 
|---|
| 206 |                             $this->all_modules[$entry] = &$this->disabled[$entry]; | 
|---|
| 207 |                             $this->id                  = null; | 
|---|
| 208 |                             $this->mroot               = null; | 
|---|
| 209 |                         } | 
|---|
| 210 |                     } | 
|---|
| 211 |                 } | 
|---|
| 212 |             } | 
|---|
| 213 |             $d->close(); | 
|---|
| 214 |         } | 
|---|
| 215 |         $this->checkDependencies(); | 
|---|
| 216 |         # Sort plugins | 
|---|
| 217 |         uasort($this->modules, array($this, 'sortModules')); | 
|---|
| 218 |  | 
|---|
| 219 |         foreach ($this->modules as $id => $m) { | 
|---|
| 220 |             # Load translation and _prepend | 
|---|
| 221 |             if (file_exists($m['root'] . '/_prepend.php')) { | 
|---|
| 222 |                 $r = $this->loadModuleFile($m['root'] . '/_prepend.php'); | 
|---|
| 223 |  | 
|---|
| 224 |                 # If _prepend.php file returns null (ie. it has a void return statement) | 
|---|
| 225 |                 if (is_null($r)) { | 
|---|
| 226 |                     $ignored[] = $id; | 
|---|
| 227 |                     continue; | 
|---|
| 228 |                 } | 
|---|
| 229 |                 unset($r); | 
|---|
| 230 |             } | 
|---|
| 231 |  | 
|---|
| 232 |             $this->loadModuleL10N($id, $lang, 'main'); | 
|---|
| 233 |             if ($ns == 'admin') { | 
|---|
| 234 |                 $this->loadModuleL10Nresources($id, $lang); | 
|---|
| 235 |                 $this->core->adminurl->register('admin.plugin.' . $id, 'plugin.php', array('p' => $id)); | 
|---|
| 236 |             } | 
|---|
| 237 |         } | 
|---|
| 238 |  | 
|---|
| 239 |         // Give opportunity to do something before loading context (admin,public,xmlrpc) files | 
|---|
| 240 |         $this->core->callBehavior('coreBeforeLoadingNsFiles', $this->core, $this, $lang); | 
|---|
| 241 |  | 
|---|
| 242 |         foreach ($this->modules as $id => $m) { | 
|---|
| 243 |             # If _prepend.php file returns null (ie. it has a void return statement) | 
|---|
| 244 |             if (in_array($id, $ignored)) { | 
|---|
| 245 |                 continue; | 
|---|
| 246 |             } | 
|---|
| 247 |             # Load ns_file | 
|---|
| 248 |             $this->loadNsFile($id, $ns); | 
|---|
| 249 |         } | 
|---|
| 250 |     } | 
|---|
| 251 |  | 
|---|
| 252 |     public function requireDefine($dir, $id) | 
|---|
| 253 |     { | 
|---|
| 254 |         if (file_exists($dir . '/_define.php')) { | 
|---|
| 255 |             $this->id = $id; | 
|---|
| 256 |             ob_start(); | 
|---|
| 257 |             require $dir . '/_define.php'; | 
|---|
| 258 |             ob_end_clean(); | 
|---|
| 259 |             $this->id = null; | 
|---|
| 260 |         } | 
|---|
| 261 |     } | 
|---|
| 262 |  | 
|---|
| 263 |     /** | 
|---|
| 264 |     This method registers a module in modules list. You should use this to | 
|---|
| 265 |     register a new module. | 
|---|
| 266 |  | 
|---|
| 267 |     <var>$permissions</var> is a comma separated list of permissions for your | 
|---|
| 268 |     module. If <var>$permissions</var> is null, only super admin has access to | 
|---|
| 269 |     this module. | 
|---|
| 270 |  | 
|---|
| 271 |     <var>$priority</var> is an integer. Modules are sorted by priority and name. | 
|---|
| 272 |     Lowest priority comes first. | 
|---|
| 273 |  | 
|---|
| 274 |     @param    name            <b>string</b>        Module name | 
|---|
| 275 |     @param    desc            <b>string</b>        Module description | 
|---|
| 276 |     @param    author        <b>string</b>        Module author name | 
|---|
| 277 |     @param    version        <b>string</b>        Module version | 
|---|
| 278 |     @param    properties    <b>array</b>        extra properties | 
|---|
| 279 |     (currently available keys : permissions, priority, type) | 
|---|
| 280 |      */ | 
|---|
| 281 |     public function registerModule($name, $desc, $author, $version, $properties = array()) | 
|---|
| 282 |     { | 
|---|
| 283 |         if ($this->disabled_mode) { | 
|---|
| 284 |             $this->disabled_meta = array_merge( | 
|---|
| 285 |                 $properties, | 
|---|
| 286 |                 array( | 
|---|
| 287 |                     'root'          => $this->mroot, | 
|---|
| 288 |                     'name'          => $name, | 
|---|
| 289 |                     'desc'          => $desc, | 
|---|
| 290 |                     'author'        => $author, | 
|---|
| 291 |                     'version'       => $version, | 
|---|
| 292 |                     'enabled'       => false, | 
|---|
| 293 |                     'root_writable' => is_writable($this->mroot) | 
|---|
| 294 |                 ) | 
|---|
| 295 |             ); | 
|---|
| 296 |             return; | 
|---|
| 297 |         } | 
|---|
| 298 |         # Fallback to legacy registerModule parameters | 
|---|
| 299 |         if (!is_array($properties)) { | 
|---|
| 300 |             $args       = func_get_args(); | 
|---|
| 301 |             $properties = array(); | 
|---|
| 302 |             if (isset($args[4])) { | 
|---|
| 303 |                 $properties['permissions'] = $args[4]; | 
|---|
| 304 |             } | 
|---|
| 305 |             if (isset($args[5])) { | 
|---|
| 306 |                 $properties['priority'] = (integer) $args[5]; | 
|---|
| 307 |             } | 
|---|
| 308 |         } | 
|---|
| 309 |  | 
|---|
| 310 |         # Default module properties | 
|---|
| 311 |         $properties = array_merge( | 
|---|
| 312 |             array( | 
|---|
| 313 |                 'permissions'       => null, | 
|---|
| 314 |                 'priority'          => 1000, | 
|---|
| 315 |                 'standalone_config' => false, | 
|---|
| 316 |                 'type'              => null, | 
|---|
| 317 |                 'enabled'           => true, | 
|---|
| 318 |                 'requires'          => array(), | 
|---|
| 319 |                 'settings'          => array() | 
|---|
| 320 |             ), $properties | 
|---|
| 321 |         ); | 
|---|
| 322 |  | 
|---|
| 323 |         # Check module type | 
|---|
| 324 |         if (self::$type !== null && $properties['type'] !== null && $properties['type'] != self::$type) { | 
|---|
| 325 |             $this->errors[] = sprintf( | 
|---|
| 326 |                 __('Module "%s" has type "%s" that mismatch required module type "%s".'), | 
|---|
| 327 |                 '<strong>' . html::escapeHTML($name) . '</strong>', | 
|---|
| 328 |                 '<em>' . html::escapeHTML($properties['type']) . '</em>', | 
|---|
| 329 |                 '<em>' . html::escapeHTML(self::$type) . '</em>' | 
|---|
| 330 |             ); | 
|---|
| 331 |             return; | 
|---|
| 332 |         } | 
|---|
| 333 |  | 
|---|
| 334 |         # Check module perms on admin side | 
|---|
| 335 |         $permissions = $properties['permissions']; | 
|---|
| 336 |         if ($this->ns == 'admin') { | 
|---|
| 337 |             if ($permissions == '' && !$this->core->auth->isSuperAdmin()) { | 
|---|
| 338 |                 return; | 
|---|
| 339 |             } elseif (!$this->core->auth->check($permissions, $this->core->blog->id)) { | 
|---|
| 340 |                 return; | 
|---|
| 341 |             } | 
|---|
| 342 |         } | 
|---|
| 343 |  | 
|---|
| 344 |         # Check module install on multiple path | 
|---|
| 345 |         if ($this->id) { | 
|---|
| 346 |             $module_exists    = array_key_exists($name, $this->modules_names); | 
|---|
| 347 |             $module_overwrite = $module_exists ? version_compare($this->modules_names[$name], $version, '<') : false; | 
|---|
| 348 |             if (!$module_exists || ($module_exists && $module_overwrite)) { | 
|---|
| 349 |                 $this->modules_names[$name] = $version; | 
|---|
| 350 |                 $this->modules[$this->id]   = array_merge( | 
|---|
| 351 |                     $properties, | 
|---|
| 352 |                     array( | 
|---|
| 353 |                         'root'          => $this->mroot, | 
|---|
| 354 |                         'name'          => $name, | 
|---|
| 355 |                         'desc'          => $desc, | 
|---|
| 356 |                         'author'        => $author, | 
|---|
| 357 |                         'version'       => $version, | 
|---|
| 358 |                         'root_writable' => is_writable($this->mroot) | 
|---|
| 359 |                     ) | 
|---|
| 360 |                 ); | 
|---|
| 361 |             } else { | 
|---|
| 362 |                 $path1          = path::real($this->moduleInfo($name, 'root')); | 
|---|
| 363 |                 $path2          = path::real($this->mroot); | 
|---|
| 364 |                 $this->errors[] = sprintf( | 
|---|
| 365 |                     __('Module "%s" is installed twice in "%s" and "%s".'), | 
|---|
| 366 |                     '<strong>' . $name . '</strong>', | 
|---|
| 367 |                     '<em>' . $path1 . '</em>', | 
|---|
| 368 |                     '<em>' . $path2 . '</em>' | 
|---|
| 369 |                 ); | 
|---|
| 370 |             } | 
|---|
| 371 |         } | 
|---|
| 372 |     } | 
|---|
| 373 |  | 
|---|
| 374 |     public function resetModulesList() | 
|---|
| 375 |     { | 
|---|
| 376 |         $this->modules       = array(); | 
|---|
| 377 |         $this->modules_names = array(); | 
|---|
| 378 |         $this->errors        = array(); | 
|---|
| 379 |     } | 
|---|
| 380 |  | 
|---|
| 381 |     public static function installPackage($zip_file, dcModules &$modules) | 
|---|
| 382 |     { | 
|---|
| 383 |         $zip = new fileUnzip($zip_file); | 
|---|
| 384 |         $zip->getList(false, '#(^|/)(__MACOSX|\.svn|\.hg|\.git|\.DS_Store|\.directory|Thumbs\.db)(/|$)#'); | 
|---|
| 385 |  | 
|---|
| 386 |         $zip_root_dir = $zip->getRootDir(); | 
|---|
| 387 |         $define       = ''; | 
|---|
| 388 |         if ($zip_root_dir != false) { | 
|---|
| 389 |             $target      = dirname($zip_file); | 
|---|
| 390 |             $destination = $target . '/' . $zip_root_dir; | 
|---|
| 391 |             $define      = $zip_root_dir . '/_define.php'; | 
|---|
| 392 |             $has_define  = $zip->hasFile($define); | 
|---|
| 393 |         } else { | 
|---|
| 394 |             $target      = dirname($zip_file) . '/' . preg_replace('/\.([^.]+)$/', '', basename($zip_file)); | 
|---|
| 395 |             $destination = $target; | 
|---|
| 396 |             $define      = '_define.php'; | 
|---|
| 397 |             $has_define  = $zip->hasFile($define); | 
|---|
| 398 |         } | 
|---|
| 399 |  | 
|---|
| 400 |         if ($zip->isEmpty()) { | 
|---|
| 401 |             $zip->close(); | 
|---|
| 402 |             unlink($zip_file); | 
|---|
| 403 |             throw new Exception(__('Empty module zip file.')); | 
|---|
| 404 |         } | 
|---|
| 405 |  | 
|---|
| 406 |         if (!$has_define) { | 
|---|
| 407 |             $zip->close(); | 
|---|
| 408 |             unlink($zip_file); | 
|---|
| 409 |             throw new Exception(__('The zip file does not appear to be a valid Dotclear module.')); | 
|---|
| 410 |         } | 
|---|
| 411 |  | 
|---|
| 412 |         $ret_code = 1; | 
|---|
| 413 |  | 
|---|
| 414 |         if (!is_dir($destination)) { | 
|---|
| 415 |             try { | 
|---|
| 416 |                 files::makeDir($destination, true); | 
|---|
| 417 |  | 
|---|
| 418 |                 $sandbox = clone $modules; | 
|---|
| 419 |                 $zip->unzip($define, $target . '/_define.php'); | 
|---|
| 420 |  | 
|---|
| 421 |                 $sandbox->resetModulesList(); | 
|---|
| 422 |                 $sandbox->requireDefine($target, basename($destination)); | 
|---|
| 423 |                 unlink($target . '/_define.php'); | 
|---|
| 424 |  | 
|---|
| 425 |                 $new_errors = $sandbox->getErrors(); | 
|---|
| 426 |                 if (!empty($new_errors)) { | 
|---|
| 427 |                     $new_errors = is_array($new_errors) ? implode(" \n", $new_errors) : $new_errors; | 
|---|
| 428 |                     throw new Exception($new_errors); | 
|---|
| 429 |                 } | 
|---|
| 430 |  | 
|---|
| 431 |                 files::deltree($destination); | 
|---|
| 432 |             } catch (Exception $e) { | 
|---|
| 433 |                 $zip->close(); | 
|---|
| 434 |                 unlink($zip_file); | 
|---|
| 435 |                 files::deltree($destination); | 
|---|
| 436 |                 throw new Exception($e->getMessage()); | 
|---|
| 437 |             } | 
|---|
| 438 |         } else { | 
|---|
| 439 |             # test for update | 
|---|
| 440 |             $sandbox = clone $modules; | 
|---|
| 441 |             $zip->unzip($define, $target . '/_define.php'); | 
|---|
| 442 |  | 
|---|
| 443 |             $sandbox->resetModulesList(); | 
|---|
| 444 |             $sandbox->requireDefine($target, basename($destination)); | 
|---|
| 445 |             unlink($target . '/_define.php'); | 
|---|
| 446 |             $new_modules = $sandbox->getModules(); | 
|---|
| 447 |  | 
|---|
| 448 |             if (!empty($new_modules)) { | 
|---|
| 449 |                 $tmp        = array_keys($new_modules); | 
|---|
| 450 |                 $id         = $tmp[0]; | 
|---|
| 451 |                 $cur_module = $modules->getModules($id); | 
|---|
| 452 |                 if (!empty($cur_module) && (defined('DC_DEV') && DC_DEV === true || dcUtils::versionsCompare($new_modules[$id]['version'], $cur_module['version'], '>', true))) { | 
|---|
| 453 |                     # delete old module | 
|---|
| 454 |                     if (!files::deltree($destination)) { | 
|---|
| 455 |                         throw new Exception(__('An error occurred during module deletion.')); | 
|---|
| 456 |                     } | 
|---|
| 457 |                     $ret_code = 2; | 
|---|
| 458 |                 } else { | 
|---|
| 459 |                     $zip->close(); | 
|---|
| 460 |                     unlink($zip_file); | 
|---|
| 461 |                     throw new Exception(sprintf(__('Unable to upgrade "%s". (older or same version)'), basename($destination))); | 
|---|
| 462 |                 } | 
|---|
| 463 |             } else { | 
|---|
| 464 |                 $zip->close(); | 
|---|
| 465 |                 unlink($zip_file); | 
|---|
| 466 |                 throw new Exception(sprintf(__('Unable to read new _define.php file'))); | 
|---|
| 467 |             } | 
|---|
| 468 |         } | 
|---|
| 469 |         $zip->unzipAll($target); | 
|---|
| 470 |         $zip->close(); | 
|---|
| 471 |         unlink($zip_file); | 
|---|
| 472 |         return $ret_code; | 
|---|
| 473 |     } | 
|---|
| 474 |  | 
|---|
| 475 |     /** | 
|---|
| 476 |     This method installs all modules having a _install file. | 
|---|
| 477 |  | 
|---|
| 478 |     @see dcModules::installModule | 
|---|
| 479 |      */ | 
|---|
| 480 |     public function installModules() | 
|---|
| 481 |     { | 
|---|
| 482 |         $res = array('success' => array(), 'failure' => array()); | 
|---|
| 483 |         foreach ($this->modules as $id => &$m) { | 
|---|
| 484 |             $i = $this->installModule($id, $msg); | 
|---|
| 485 |             if ($i === true) { | 
|---|
| 486 |                 $res['success'][$id] = true; | 
|---|
| 487 |             } elseif ($i === false) { | 
|---|
| 488 |                 $res['failure'][$id] = $msg; | 
|---|
| 489 |             } | 
|---|
| 490 |         } | 
|---|
| 491 |  | 
|---|
| 492 |         return $res; | 
|---|
| 493 |     } | 
|---|
| 494 |  | 
|---|
| 495 |     /** | 
|---|
| 496 |     This method installs module with ID <var>$id</var> and having a _install | 
|---|
| 497 |     file. This file should throw exception on failure or true if it installs | 
|---|
| 498 |     successfully. | 
|---|
| 499 |  | 
|---|
| 500 |     <var>$msg</var> is an out parameter that handle installer message. | 
|---|
| 501 |  | 
|---|
| 502 |     @param    id        <b>string</b>        Module ID | 
|---|
| 503 |     @param    msg        <b>string</b>        Module installer message | 
|---|
| 504 |     @return    <b>boolean</b> | 
|---|
| 505 |      */ | 
|---|
| 506 |     public function installModule($id, &$msg) | 
|---|
| 507 |     { | 
|---|
| 508 |         try { | 
|---|
| 509 |             $i = $this->loadModuleFile($this->modules[$id]['root'] . '/_install.php'); | 
|---|
| 510 |             if ($i === true) { | 
|---|
| 511 |                 return true; | 
|---|
| 512 |             } | 
|---|
| 513 |         } catch (Exception $e) { | 
|---|
| 514 |             $msg = $e->getMessage(); | 
|---|
| 515 |             return false; | 
|---|
| 516 |         } | 
|---|
| 517 |  | 
|---|
| 518 |         return; | 
|---|
| 519 |     } | 
|---|
| 520 |  | 
|---|
| 521 |     public function deleteModule($id, $disabled = false) | 
|---|
| 522 |     { | 
|---|
| 523 |         if ($disabled) { | 
|---|
| 524 |             $p = &$this->disabled; | 
|---|
| 525 |         } else { | 
|---|
| 526 |             $p = &$this->modules; | 
|---|
| 527 |         } | 
|---|
| 528 |  | 
|---|
| 529 |         if (!isset($p[$id])) { | 
|---|
| 530 |             throw new Exception(__('No such module.')); | 
|---|
| 531 |         } | 
|---|
| 532 |  | 
|---|
| 533 |         if (!files::deltree($p[$id]['root'])) { | 
|---|
| 534 |             throw new Exception(__('Cannot remove module files')); | 
|---|
| 535 |         } | 
|---|
| 536 |     } | 
|---|
| 537 |  | 
|---|
| 538 |     public function deactivateModule($id) | 
|---|
| 539 |     { | 
|---|
| 540 |         if (!isset($this->modules[$id])) { | 
|---|
| 541 |             throw new Exception(__('No such module.')); | 
|---|
| 542 |         } | 
|---|
| 543 |  | 
|---|
| 544 |         if (!$this->modules[$id]['root_writable']) { | 
|---|
| 545 |             throw new Exception(__('Cannot deactivate plugin.')); | 
|---|
| 546 |         } | 
|---|
| 547 |  | 
|---|
| 548 |         if (@file_put_contents($this->modules[$id]['root'] . '/_disabled', '')) { | 
|---|
| 549 |             throw new Exception(__('Cannot deactivate plugin.')); | 
|---|
| 550 |         } | 
|---|
| 551 |     } | 
|---|
| 552 |  | 
|---|
| 553 |     public function activateModule($id) | 
|---|
| 554 |     { | 
|---|
| 555 |         if (!isset($this->disabled[$id])) { | 
|---|
| 556 |             throw new Exception(__('No such module.')); | 
|---|
| 557 |         } | 
|---|
| 558 |  | 
|---|
| 559 |         if (!$this->disabled[$id]['root_writable']) { | 
|---|
| 560 |             throw new Exception(__('Cannot activate plugin.')); | 
|---|
| 561 |         } | 
|---|
| 562 |  | 
|---|
| 563 |         if (@unlink($this->disabled[$id]['root'] . '/_disabled') === false) { | 
|---|
| 564 |             throw new Exception(__('Cannot activate plugin.')); | 
|---|
| 565 |         } | 
|---|
| 566 |     } | 
|---|
| 567 |  | 
|---|
| 568 |     /** | 
|---|
| 569 |     This method will search for file <var>$file</var> in language | 
|---|
| 570 |     <var>$lang</var> for module <var>$id</var>. | 
|---|
| 571 |  | 
|---|
| 572 |     <var>$file</var> should not have any extension. | 
|---|
| 573 |  | 
|---|
| 574 |     @param    id        <b>string</b>        Module ID | 
|---|
| 575 |     @param    lang        <b>string</b>        Language code | 
|---|
| 576 |     @param    file        <b>string</b>        File name (without extension) | 
|---|
| 577 |      */ | 
|---|
| 578 |     public function loadModuleL10N($id, $lang, $file) | 
|---|
| 579 |     { | 
|---|
| 580 |         if (!$lang || !isset($this->modules[$id])) { | 
|---|
| 581 |             return; | 
|---|
| 582 |         } | 
|---|
| 583 |  | 
|---|
| 584 |         $lfile = $this->modules[$id]['root'] . '/locales/%s/%s'; | 
|---|
| 585 |         if (l10n::set(sprintf($lfile, $lang, $file)) === false && $lang != 'en') { | 
|---|
| 586 |             l10n::set(sprintf($lfile, 'en', $file)); | 
|---|
| 587 |         } | 
|---|
| 588 |     } | 
|---|
| 589 |  | 
|---|
| 590 |     public function loadModuleL10Nresources($id, $lang) | 
|---|
| 591 |     { | 
|---|
| 592 |         if (!$lang || !isset($this->modules[$id])) { | 
|---|
| 593 |             return; | 
|---|
| 594 |         } | 
|---|
| 595 |  | 
|---|
| 596 |         $f = l10n::getFilePath($this->modules[$id]['root'] . '/locales', 'resources.php', $lang); | 
|---|
| 597 |         if ($f) { | 
|---|
| 598 |             $this->loadModuleFile($f); | 
|---|
| 599 |         } | 
|---|
| 600 |     } | 
|---|
| 601 |  | 
|---|
| 602 |     /** | 
|---|
| 603 |     Returns all modules associative array or only one module if <var>$id</var> | 
|---|
| 604 |     is present. | 
|---|
| 605 |  | 
|---|
| 606 |     @param    id        <b>string</b>        Optionnal module ID | 
|---|
| 607 |     @return    <b>array</b> | 
|---|
| 608 |      */ | 
|---|
| 609 |     public function getModules($id = null) | 
|---|
| 610 |     { | 
|---|
| 611 |         if ($id && isset($this->modules[$id])) { | 
|---|
| 612 |             return $this->modules[$id]; | 
|---|
| 613 |         } | 
|---|
| 614 |         return $this->modules; | 
|---|
| 615 |     } | 
|---|
| 616 |  | 
|---|
| 617 |     /** | 
|---|
| 618 |     Returns true if the module with ID <var>$id</var> exists. | 
|---|
| 619 |  | 
|---|
| 620 |     @param    id        <b>string</b>        Module ID | 
|---|
| 621 |     @return    <b>boolean</b> | 
|---|
| 622 |      */ | 
|---|
| 623 |     public function moduleExists($id) | 
|---|
| 624 |     { | 
|---|
| 625 |         return isset($this->modules[$id]); | 
|---|
| 626 |     } | 
|---|
| 627 |  | 
|---|
| 628 |     /** | 
|---|
| 629 |     Returns all disabled modules in an array | 
|---|
| 630 |  | 
|---|
| 631 |     @return    <b>array</b> | 
|---|
| 632 |      */ | 
|---|
| 633 |     public function getDisabledModules() | 
|---|
| 634 |     { | 
|---|
| 635 |         return $this->disabled; | 
|---|
| 636 |     } | 
|---|
| 637 |  | 
|---|
| 638 |     /** | 
|---|
| 639 |     Returns root path for module with ID <var>$id</var>. | 
|---|
| 640 |  | 
|---|
| 641 |     @param    id        <b>string</b>        Module ID | 
|---|
| 642 |     @return    <b>string</b> | 
|---|
| 643 |      */ | 
|---|
| 644 |     public function moduleRoot($id) | 
|---|
| 645 |     { | 
|---|
| 646 |         return $this->moduleInfo($id, 'root'); | 
|---|
| 647 |     } | 
|---|
| 648 |  | 
|---|
| 649 |     /** | 
|---|
| 650 |     Returns a module information that could be: | 
|---|
| 651 |     - root | 
|---|
| 652 |     - name | 
|---|
| 653 |     - desc | 
|---|
| 654 |     - author | 
|---|
| 655 |     - version | 
|---|
| 656 |     - permissions | 
|---|
| 657 |     - priority | 
|---|
| 658 |  | 
|---|
| 659 |     @param    id        <b>string</b>        Module ID | 
|---|
| 660 |     @param    info        <b>string</b>        Information to retrieve | 
|---|
| 661 |     @return    <b>string</b> | 
|---|
| 662 |      */ | 
|---|
| 663 |     public function moduleInfo($id, $info) | 
|---|
| 664 |     { | 
|---|
| 665 |         return isset($this->modules[$id][$info]) ? $this->modules[$id][$info] : null; | 
|---|
| 666 |     } | 
|---|
| 667 |  | 
|---|
| 668 |     /** | 
|---|
| 669 |     Loads namespace <var>$ns</var> specific files for all modules. | 
|---|
| 670 |  | 
|---|
| 671 |     @param    ns        <b>string</b>        Namespace name | 
|---|
| 672 |      */ | 
|---|
| 673 |     public function loadNsFiles($ns = null) | 
|---|
| 674 |     { | 
|---|
| 675 |         foreach ($this->modules as $k => $v) { | 
|---|
| 676 |             $this->loadNsFile($k, $ns); | 
|---|
| 677 |         } | 
|---|
| 678 |     } | 
|---|
| 679 |  | 
|---|
| 680 |     /** | 
|---|
| 681 |     Loads namespace <var>$ns</var> specific file for module with ID | 
|---|
| 682 |     <var>$id</var> | 
|---|
| 683 |  | 
|---|
| 684 |     @param    id        <b>string</b>        Module ID | 
|---|
| 685 |     @param    ns        <b>string</b>        Namespace name | 
|---|
| 686 |      */ | 
|---|
| 687 |     public function loadNsFile($id, $ns = null) | 
|---|
| 688 |     { | 
|---|
| 689 |         switch ($ns) { | 
|---|
| 690 |             case 'admin': | 
|---|
| 691 |                 $this->loadModuleFile($this->modules[$id]['root'] . '/_admin.php'); | 
|---|
| 692 |                 break; | 
|---|
| 693 |             case 'public': | 
|---|
| 694 |                 $this->loadModuleFile($this->modules[$id]['root'] . '/_public.php'); | 
|---|
| 695 |                 break; | 
|---|
| 696 |             case 'xmlrpc': | 
|---|
| 697 |                 $this->loadModuleFile($this->modules[$id]['root'] . '/_xmlrpc.php'); | 
|---|
| 698 |                 break; | 
|---|
| 699 |         } | 
|---|
| 700 |     } | 
|---|
| 701 |  | 
|---|
| 702 |     public function getErrors() | 
|---|
| 703 |     { | 
|---|
| 704 |         return $this->errors; | 
|---|
| 705 |     } | 
|---|
| 706 |  | 
|---|
| 707 |     protected function loadModuleFile($________, $catch = true) | 
|---|
| 708 |     { | 
|---|
| 709 |         if (!file_exists($________)) { | 
|---|
| 710 |             return; | 
|---|
| 711 |         } | 
|---|
| 712 |  | 
|---|
| 713 |         self::$_k = array_keys($GLOBALS); | 
|---|
| 714 |  | 
|---|
| 715 |         foreach (self::$_k as self::$_n) { | 
|---|
| 716 |             if (!in_array(self::$_n, self::$superglobals)) { | 
|---|
| 717 |                 global ${self::$_n}; | 
|---|
| 718 |             } | 
|---|
| 719 |         } | 
|---|
| 720 |  | 
|---|
| 721 |         if ($catch) { | 
|---|
| 722 |             // Catch ouput to prevents hacked or corrupted modules | 
|---|
| 723 |             ob_start(); | 
|---|
| 724 |             $ret = require $________; | 
|---|
| 725 |             ob_end_clean(); | 
|---|
| 726 |             return $ret; | 
|---|
| 727 |         } | 
|---|
| 728 |  | 
|---|
| 729 |         return require $________; | 
|---|
| 730 |     } | 
|---|
| 731 |  | 
|---|
| 732 |     private function sortModules($a, $b) | 
|---|
| 733 |     { | 
|---|
| 734 |         if ($a['priority'] == $b['priority']) { | 
|---|
| 735 |             return strcasecmp($a['name'], $b['name']); | 
|---|
| 736 |         } | 
|---|
| 737 |  | 
|---|
| 738 |         return ($a['priority'] < $b['priority']) ? -1 : 1; | 
|---|
| 739 |     } | 
|---|
| 740 | } | 
|---|