3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
10 * Copyright © 2009-2014 The Galette Team
12 * This file is part of Galette (http://galette.tuxfamily.org).
14 * Galette is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
19 * Galette is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2009-2014 The Galette Team
32 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
34 * @link http://galette.tuxfamily.org
35 * @since Available since 0.7 - 2009-03-09
38 namespace Galette\Core
;
41 use Galette\Common\ClassLoader
;
42 use Galette\Core\Preferences
;
45 * Plugins class for galette
50 * @author Johan Cwiklinski <johan@x-tnd.be>
51 * @copyright 2009-2014 The Galette Team
52 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
53 * @link http://galette.tuxfamily.org
54 * @since Available since 0.7 - 2009-03-09
59 const DISABLED_COMPAT
= 0;
60 const DISABLED_MISS
= 1;
61 const DISABLED_EXPLICIT
= 2;
64 protected $modules = array();
65 protected $disabled = array();
70 protected $preferences;
71 protected $autoload = false;
74 * Register autoloaders for all plugins
76 * @param string $path could be a separated list of paths
77 * (path separator depends on your OS).
81 public function autoload($path)
83 $this->path
= explode(PATH_SEPARATOR
, $path);
84 $this->autoload
= true;
85 $this->parseModules();
89 * Parse modules in current path
93 protected function parseModules()
95 foreach ($this->path
as $root) {
96 if (!is_dir($root) ||
!is_readable($root)) {
100 if (substr($root, -1) != '/') {
104 if (($d = @dir
($root)) === false) {
108 while (($entry = $d->read()) !== false) {
109 $full_entry = realpath($root . $entry);
110 if ($entry != '.' && $entry != '..' && is_dir($full_entry)) {
112 $this->mroot
= $full_entry;
114 !file_exists($full_entry . '/_define.php')
115 ||
!file_exists($full_entry . '/_routes.php')
117 //plugin is not compatible with that version of galette.
119 'Plugin ' . $entry . ' is missing a _define.php and/or _routes.php ' .
120 'files that are required.',
123 $this->setDisabled(self
::DISABLED_MISS
);
124 } elseif (!file_exists($full_entry . '/_disabled')) {
125 include $full_entry . '/_define.php';
128 if ($this->autoload
== true) {
129 //set autoloader to PluginName.
130 if (file_exists($full_entry . '/lib') && isset($this->modules
[$entry])) {
131 $varname = $entry . 'Loader';
132 $
$varname = new ClassLoader(
133 $this->getNamespace($entry),
136 $
$varname->register();
140 //plugin is not compatible with that version of galette.
142 'Plugin ' . $entry . ' is explicitely disabled',
145 $this->setDisabled(self
::DISABLED_EXPLICIT
);
156 * @param Preferences $preferences Galette's Preferences
157 * @param string $path could be a separated list of paths
158 * (path separator depends on your OS).
159 * @param string $lang Indicates if we need to load a lang file on plugin
164 public function loadModules(Preferences
$preferences, $path, $lang = null)
166 $this->preferences
= $preferences;
167 $this->path
= explode(PATH_SEPARATOR
, $path);
169 $this->parseModules();
172 uasort($this->modules
, array($this, 'sortModules'));
174 // Load translation, _prepend and ns_file
175 foreach ($this->modules
as $id => $m) {
176 $this->loadModuleL10N($id, $lang);
177 $this->loadSmarties($id);
178 $this->loadEventProviders($id);
179 $this->overridePrefs($id);
184 * This method registers a module in modules list. You should use this to
185 * register a new module.
187 * <var>$permissions</var> is a comma separated list of permissions for your
188 * module. If <var>$permissions</var> is null, only super admin has access to
191 * <var>$priority</var> is an integer. Modules are sorted by priority and name.
192 * Lowest priority comes first.
194 * @param string $name Module name
195 * @param string $desc Module description
196 * @param string $author Module author name
197 * @param string $version Module version
198 * @param string $compver Galette version compatibility
199 * @param string $route Module route name
200 * @param string $date Module release date
201 * @param string $acls Module routes ACLs
202 * @param integer $priority Module priority
206 public function register(
217 if ($compver === null) {
218 //plugin compatibility missing!
220 'Plugin ' . $name . ' does not contains mandatory version ' .
221 'compatiblity information. Please contact the author.',
224 $this->setDisabled(self
::DISABLED_COMPAT
);
225 } elseif (version_compare($compver, GALETTE_COMPAT_VERSION
, '<')) {
226 //plugin is not compatible with that version of galette.
228 'Plugin ' . $name . ' is known to be compatible with Galette ' .
229 $compver . ' only, but you current installation require a ' .
230 'plugin compatible with at least ' . GALETTE_COMPAT_VERSION
,
233 $this->setDisabled(self
::DISABLED_COMPAT
);
236 $release_date = $date;
237 if ($date !== null && $this->autoload
=== false) {
238 //try to localize release date
240 $release_date = new \
DateTime($date);
241 $release_date = $release_date->format(__("Y-m-d"));
242 } catch (\Exception
$e) {
244 'Unable to localize release date for plugin ' . $name,
250 $this->modules
[$this->id
] = array(
251 'root' => $this->mroot
,
255 'version' => $version,
257 'date' => $release_date,
258 'priority' => $priority === null ?
259 1000 : (int)$priority,
260 'root_writable' => is_writable($this->mroot
),
272 public function resetModulesList()
274 $this->modules
= array();
278 * Deactivate specified module
280 * @param string $id Module's ID
282 * @return void|exception
284 public function deactivateModule($id)
286 if (!isset($this->modules
[$id])) {
287 throw new \
Exception(_T("No such module."));
290 if (!$this->modules
[$id]['root_writable']) {
291 throw new \
Exception(_T("Cannot deactivate plugin."));
294 if (@file_put_contents
($this->modules
[$id]['root'] . '/_disabled', '')) {
295 throw new \
Exception(_T("Cannot deactivate plugin."));
300 * Activate specified module
302 * @param string $id Module's ID
304 * @return void|exception
306 public function activateModule($id)
308 if (!isset($this->disabled
[$id])) {
309 throw new \
Exception(_T("No such module."));
312 if (!$this->disabled
[$id]['root_writable']) {
313 throw new \
Exception(_T("Cannot activate plugin."));
316 if (@unlink
($this->disabled
[$id]['root'] . '/_disabled') === false) {
317 throw new \
Exception(_T("Cannot activate plugin."));
322 * This method will search for file <var>$file</var> in language
323 * <var>$lang</var> for module <var>$id</var>.
324 * <var>$file</var> should not have any extension.
326 * @param string $id Module ID
327 * @param string $language Language code
331 public function loadModuleL10N($id, $language)
335 if (!$language ||
!isset($this->modules
[$id])) {
340 $this->modules
[$id]['route']
342 foreach ($domains as $domain) {
343 //load translation file for domain
344 $translator->addTranslationFilePattern(
346 $this->modules
[$id]['root'] . '/lang/',
347 '/%s/LC_MESSAGES/' . $domain . '.mo',
351 //check if a local lang file exists and load it
352 $translator->addTranslationFilePattern(
354 $this->modules
[$id]['root'] . '/lang/',
355 $domain . '_%s_local_lang.php',
362 * Loads smarties specific (headers, assigments and so on)
364 * @param string $id Module ID
368 public function loadSmarties($id)
370 $f = $this->modules
[$id]['root'] . '/_smarties.php';
371 if (file_exists($f)) {
373 if (isset($_tpl_assignments)) {
374 $this->modules
[$id]['tpl_assignments'] = $_tpl_assignments;
380 * Loads event provider
382 * @param string $id Module ID
386 public function loadEventProviders($id)
390 $providerClassName = '\\' . $this->getNamespace($id) . '\\' . 'PluginEventProvider';
392 class_exists($providerClassName)
393 && method_exists($providerClassName, 'provideListeners')
395 $emitter->useListenerProvider(new $providerClassName());
400 * Returns all modules associative array or only one module if <var>$id</var>
403 * @param string $id Optionnal module ID
405 * @return <b>array</b>
407 public function getModules($id = null)
409 if ($id && isset($this->modules
[$id])) {
410 return $this->modules
[$id];
412 return $this->modules
;
416 * Returns true if the module with ID <var>$id</var> exists.
418 * @param string $id Module ID
420 * @return <b>boolean</b>
422 public function moduleExists($id)
424 return isset($this->modules
[$id]);
428 * Returns all disabled modules in an array
430 * @return <b>array</b>
432 public function getDisabledModules()
434 return $this->disabled
;
438 * Returns root path for module with ID <var>$id</var>.
440 * @param string $id Module ID
442 * @return <b>string</b>
444 public function moduleRoot($id)
446 return $this->moduleInfo($id, 'root');
450 * Returns a module information that could be:
460 * @param string $id Module ID
461 * @param string $info Information to retrieve
463 * @return module's information
465 public function moduleInfo($id, $info)
467 return isset($this->modules
[$id][$info]) ?
$this->modules
[$id][$info] : null;
471 * Search and load menu templates from plugins.
472 * Also sets the web path to the plugin with the var "galette_[plugin-name]_path"
474 * @param Smarty $tpl Smarty template
478 public function getMenus($tpl)
480 $modules = $this->getModules();
481 foreach (array_keys($this->getModules()) as $r) {
482 $menu_path = $this->getTemplatesPath($r) . '/menu.tpl';
483 if ($tpl->templateExists($menu_path)) {
484 $name2path = strtolower(
485 str_replace(' ', '_', $modules[$r]['name'])
488 'galette_' . $name2path . '_path',
489 'plugins/' . $r . '/'
491 $tpl->display($menu_path);
497 * Search and load public menu templates from plugins.
498 * Also sets the web path to the plugin with the var "galette_[plugin-name]_path"
500 * @param Smarty $tpl Smarty template
501 * @param boolean $public_page Called from a public page
505 public function getPublicMenus($tpl, $public_page = false)
507 $modules = $this->getModules();
508 foreach (array_keys($this->getModules()) as $r) {
509 $menu_path = $this->getTemplatesPath($r) . '/public_menu.tpl';
510 if ($tpl->templateExists($menu_path)) {
511 $name2path = strtolower(
512 str_replace(' ', '_', $modules[$r]['name'])
515 'galette_' . $name2path . '_path',
516 'plugins/' . $r . '/'
522 $tpl->display($menu_path);
528 * Get plugins dashboard entries.
530 * @param Smarty $tpl Smarty template
534 public function getDashboard($tpl)
536 $modules = $this->getModules();
537 foreach (array_keys($this->getModules()) as $r) {
538 $dash_path = $this->getTemplatesPath($r) . '/dashboard.tpl';
539 if ($tpl->templateExists($dash_path)) {
540 $name2path = strtolower(
541 str_replace(' ', '_', $modules[$r]['name'])
543 $tpl->display($dash_path);
549 * Get plugins single member dashboard entries.
551 * @param Smarty $tpl Smarty template
555 public function getMemberDashboard($tpl)
557 $modules = $this->getModules();
558 foreach (array_keys($this->getModules()) as $r) {
559 $dash_path = $this->getTemplatesPath($r) . '/dashboard_member.tpl';
560 if ($tpl->templateExists($dash_path)) {
561 $name2path = strtolower(
562 str_replace(' ', '_', $modules[$r]['name'])
564 $tpl->display($dash_path);
572 * @param array $a A module
573 * @param array $b Another module
575 * @return 1 if a has the highest priority, -1 otherwise
577 private function sortModules($a, $b)
579 if ($a['priority'] == $b['priority']) {
580 return strcasecmp($a['name'], $b['name']);
583 return ($a['priority'] < $b['priority']) ?
-1 : 1;
587 * Get the templates path for a specified module
589 * @param string $id Module's ID
591 * @return Concatenated templates path for requested module
593 public function getTemplatesPath($id)
595 return $this->moduleRoot($id) . '/templates/' . $this->preferences
->pref_theme
;
599 * Get the templates path for a specified module name
601 * @param string $name Module's name
603 * @return Concatenated templates path for requested module
605 public function getTemplatesPathFromName($name)
608 foreach (array_keys($this->getModules()) as $r) {
609 $mod = $this->getModules($r);
610 if ($mod['name'] === $name) {
611 return $this->getTemplatesPath($r);
617 * For each module, returns the headers.tpl full path, if present.
619 * @return array of headers to include for all modules
621 public function getTplHeaders()
624 foreach (array_keys($this->modules
) as $key) {
625 $headers_path = $this->getTemplatesPath($key) . '/headers.tpl';
626 if (file_exists($headers_path)) {
627 $_headers[$key] = $headers_path;
634 * For each module, return the adh_actions.tpl full path, if present.
636 * @return array of adherent actions to include on member list for all modules
638 public function getTplAdhActions()
641 foreach (array_keys($this->modules
) as $key) {
642 $actions_path = $this->getTemplatesPath($key) . '/adh_actions.tpl';
643 if (file_exists($actions_path)) {
644 $_actions['actions_' . $key] = $actions_path;
651 * For each module, return the adh_batch_action.tpl full path, if present.
653 * @return array of adherents batch actions to include on members list
656 public function getTplAdhBatchActions()
659 foreach (array_keys($this->modules
) as $key) {
660 $actions_path = $this->getTemplatesPath($key) . '/adh_batch_action.tpl';
661 if (file_exists($actions_path)) {
662 $_actions['batch_action_' . $key] = $actions_path;
669 * For each module, return the adh_fiche_action.tpl full path, if present.
671 * @return array of adherent actions to include on member detailled view for
674 public function getTplAdhDetailledActions()
677 foreach (array_keys($this->modules
) as $key) {
678 $actions_path = $this->getTemplatesPath($key) . '/adh_fiche_action.tpl';
679 if (file_exists($actions_path)) {
680 $_actions['det_actions_' . $key] = $actions_path;
687 * For each module, gets templates assignements ; and replace some path variables
689 * @return array of Smarty templates assignement for all modules
691 public function getTplAssignments()
694 foreach ($this->modules
as $key => $module) {
695 if (isset($module['tpl_assignments'])) {
696 foreach ($module['tpl_assignments'] as $k => $v) {
699 'plugins/' . $key . '/',
703 '__plugin_include_dir__',
704 'plugins/' . $key . '/includes/',
708 '__plugin_templates_dir__',
709 'plugins/' . $key . '/templates/' .
710 $this->preferences
->pref_theme
. '/',
721 * Does module needs a database?
723 * @param string $id Module's ID
727 public function needsDatabase($id)
729 if (isset($this->modules
[$id])) {
730 $d = $this->modules
[$id]['root'] . '/scripts/';
731 if (file_exists($d)) {
737 throw new \
Exception(_T("Module does not exists!"));
742 * Override preferences from plugin
744 * @param string $id Module ID
748 public function overridePrefs($id)
750 $overridables = ['pref_adhesion_form'];
752 $f = $this->modules
[$id]['root'] . '/_preferences.php';
753 if (file_exists($f)) {
755 if (isset($_preferences)) {
756 foreach ($_preferences as $k => $v) {
757 if (in_array($k, $overridables)) {
758 $this->preferences
->$k = $v;
766 * Get plugins routes ACLs
770 public function getAcls()
773 foreach ($this->modules
as $module) {
774 $acls = array_merge($acls, $module['acls']);
780 * Retrieve a file that should be publically exposed
782 * @param int $id Module id
783 * @param string $path File path
787 public function getFile($id, $path)
789 if (isset($this->modules
[$id])) {
790 $file = $this->modules
[$id]['root'] . '/webroot/' . $path;
791 if (file_exists($file)) {
794 throw new \
RuntimeException(_T("File not found!"));
797 throw new \
Exception(_T("Module does not exists!"));
802 * Set a module as disabled
804 * @param integer $cause Cause (one of Plugins::DISABLED_* constants)
808 private function setDisabled($cause)
810 $this->disabled
[$this->id
] = array(
811 'root' => $this->mroot
,
812 'root_writable' => is_writable($this->mroot
),
820 * Get module namespace
822 * @param integer $id Module ID
826 public function getNamespace($id)
828 return str_replace(' ', '', $this->modules
[$id]['name']);