diff options
author | ArthurHoaro <arthur@hoa.ro> | 2020-10-13 12:07:13 +0200 |
---|---|---|
committer | ArthurHoaro <arthur@hoa.ro> | 2020-10-13 12:07:13 +0200 |
commit | d9f6275ebca035fec8331652c677981056793ccc (patch) | |
tree | 37a64baf4f0eba6b781040605965383d8aded2cc /application/plugin | |
parent | 38672ba0d1c722e5d6d33a58255ceb55e9410e46 (diff) | |
parent | d63ff87a009313141ae684ec447b902562ff6ee7 (diff) | |
download | Shaarli-d9f6275ebca035fec8331652c677981056793ccc.tar.gz Shaarli-d9f6275ebca035fec8331652c677981056793ccc.tar.zst Shaarli-d9f6275ebca035fec8331652c677981056793ccc.zip |
Merge branch 'v0.11' into stablestable
Diffstat (limited to 'application/plugin')
-rw-r--r-- | application/plugin/PluginManager.php | 233 | ||||
-rw-r--r-- | application/plugin/exception/PluginFileNotFoundException.php | 23 |
2 files changed, 256 insertions, 0 deletions
diff --git a/application/plugin/PluginManager.php b/application/plugin/PluginManager.php new file mode 100644 index 00000000..f7b24a8e --- /dev/null +++ b/application/plugin/PluginManager.php | |||
@@ -0,0 +1,233 @@ | |||
1 | <?php | ||
2 | namespace Shaarli\Plugin; | ||
3 | |||
4 | use Shaarli\Config\ConfigManager; | ||
5 | use Shaarli\Plugin\Exception\PluginFileNotFoundException; | ||
6 | |||
7 | /** | ||
8 | * Class PluginManager | ||
9 | * | ||
10 | * Use to manage, load and execute plugins. | ||
11 | */ | ||
12 | class PluginManager | ||
13 | { | ||
14 | /** | ||
15 | * List of authorized plugins from configuration file. | ||
16 | * | ||
17 | * @var array $authorizedPlugins | ||
18 | */ | ||
19 | private $authorizedPlugins; | ||
20 | |||
21 | /** | ||
22 | * List of loaded plugins. | ||
23 | * | ||
24 | * @var array $loadedPlugins | ||
25 | */ | ||
26 | private $loadedPlugins = array(); | ||
27 | |||
28 | /** | ||
29 | * @var ConfigManager Configuration Manager instance. | ||
30 | */ | ||
31 | protected $conf; | ||
32 | |||
33 | /** | ||
34 | * @var array List of plugin errors. | ||
35 | */ | ||
36 | protected $errors; | ||
37 | |||
38 | /** | ||
39 | * Plugins subdirectory. | ||
40 | * | ||
41 | * @var string $PLUGINS_PATH | ||
42 | */ | ||
43 | public static $PLUGINS_PATH = 'plugins'; | ||
44 | |||
45 | /** | ||
46 | * Plugins meta files extension. | ||
47 | * | ||
48 | * @var string $META_EXT | ||
49 | */ | ||
50 | public static $META_EXT = 'meta'; | ||
51 | |||
52 | /** | ||
53 | * Constructor. | ||
54 | * | ||
55 | * @param ConfigManager $conf Configuration Manager instance. | ||
56 | */ | ||
57 | public function __construct(&$conf) | ||
58 | { | ||
59 | $this->conf = $conf; | ||
60 | $this->errors = array(); | ||
61 | } | ||
62 | |||
63 | /** | ||
64 | * Load plugins listed in $authorizedPlugins. | ||
65 | * | ||
66 | * @param array $authorizedPlugins Names of plugin authorized to be loaded. | ||
67 | * | ||
68 | * @return void | ||
69 | */ | ||
70 | public function load($authorizedPlugins) | ||
71 | { | ||
72 | $this->authorizedPlugins = $authorizedPlugins; | ||
73 | |||
74 | $dirs = glob(self::$PLUGINS_PATH . '/*', GLOB_ONLYDIR); | ||
75 | $dirnames = array_map('basename', $dirs); | ||
76 | foreach ($this->authorizedPlugins as $plugin) { | ||
77 | $index = array_search($plugin, $dirnames); | ||
78 | |||
79 | // plugin authorized, but its folder isn't listed | ||
80 | if ($index === false) { | ||
81 | continue; | ||
82 | } | ||
83 | |||
84 | try { | ||
85 | $this->loadPlugin($dirs[$index], $plugin); | ||
86 | } catch (PluginFileNotFoundException $e) { | ||
87 | error_log($e->getMessage()); | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * Execute all plugins registered hook. | ||
94 | * | ||
95 | * @param string $hook name of the hook to trigger. | ||
96 | * @param array $data list of data to manipulate passed by reference. | ||
97 | * @param array $params additional parameters such as page target. | ||
98 | * | ||
99 | * @return void | ||
100 | */ | ||
101 | public function executeHooks($hook, &$data, $params = array()) | ||
102 | { | ||
103 | if (!empty($params['target'])) { | ||
104 | $data['_PAGE_'] = $params['target']; | ||
105 | } | ||
106 | |||
107 | if (isset($params['loggedin'])) { | ||
108 | $data['_LOGGEDIN_'] = $params['loggedin']; | ||
109 | } | ||
110 | |||
111 | foreach ($this->loadedPlugins as $plugin) { | ||
112 | $hookFunction = $this->buildHookName($hook, $plugin); | ||
113 | |||
114 | if (function_exists($hookFunction)) { | ||
115 | $data = call_user_func($hookFunction, $data, $this->conf); | ||
116 | } | ||
117 | } | ||
118 | } | ||
119 | |||
120 | /** | ||
121 | * Load a single plugin from its files. | ||
122 | * Call the init function if it exists, and collect errors. | ||
123 | * Add them in $loadedPlugins if successful. | ||
124 | * | ||
125 | * @param string $dir plugin's directory. | ||
126 | * @param string $pluginName plugin's name. | ||
127 | * | ||
128 | * @return void | ||
129 | * @throws \Shaarli\Plugin\Exception\PluginFileNotFoundException - plugin files not found. | ||
130 | */ | ||
131 | private function loadPlugin($dir, $pluginName) | ||
132 | { | ||
133 | if (!is_dir($dir)) { | ||
134 | throw new PluginFileNotFoundException($pluginName); | ||
135 | } | ||
136 | |||
137 | $pluginFilePath = $dir . '/' . $pluginName . '.php'; | ||
138 | if (!is_file($pluginFilePath)) { | ||
139 | throw new PluginFileNotFoundException($pluginName); | ||
140 | } | ||
141 | |||
142 | $conf = $this->conf; | ||
143 | include_once $pluginFilePath; | ||
144 | |||
145 | $initFunction = $pluginName . '_init'; | ||
146 | if (function_exists($initFunction)) { | ||
147 | $errors = call_user_func($initFunction, $this->conf); | ||
148 | if (!empty($errors)) { | ||
149 | $this->errors = array_merge($this->errors, $errors); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | $this->loadedPlugins[] = $pluginName; | ||
154 | } | ||
155 | |||
156 | /** | ||
157 | * Construct normalize hook name for a specific plugin. | ||
158 | * | ||
159 | * Format: | ||
160 | * hook_<plugin_name>_<hook_name> | ||
161 | * | ||
162 | * @param string $hook hook name. | ||
163 | * @param string $pluginName plugin name. | ||
164 | * | ||
165 | * @return string - plugin's hook name. | ||
166 | */ | ||
167 | public function buildHookName($hook, $pluginName) | ||
168 | { | ||
169 | return 'hook_' . $pluginName . '_' . $hook; | ||
170 | } | ||
171 | |||
172 | /** | ||
173 | * Retrieve plugins metadata from *.meta (INI) files into an array. | ||
174 | * Metadata contains: | ||
175 | * - plugin description [description] | ||
176 | * - parameters split with ';' [parameters] | ||
177 | * | ||
178 | * Respects plugins order from settings. | ||
179 | * | ||
180 | * @return array plugins metadata. | ||
181 | */ | ||
182 | public function getPluginsMeta() | ||
183 | { | ||
184 | $metaData = array(); | ||
185 | $dirs = glob(self::$PLUGINS_PATH . '/*', GLOB_ONLYDIR | GLOB_MARK); | ||
186 | |||
187 | // Browse all plugin directories. | ||
188 | foreach ($dirs as $pluginDir) { | ||
189 | $plugin = basename($pluginDir); | ||
190 | $metaFile = $pluginDir . $plugin . '.' . self::$META_EXT; | ||
191 | if (!is_file($metaFile) || !is_readable($metaFile)) { | ||
192 | continue; | ||
193 | } | ||
194 | |||
195 | $metaData[$plugin] = parse_ini_file($metaFile); | ||
196 | $metaData[$plugin]['order'] = array_search($plugin, $this->authorizedPlugins); | ||
197 | |||
198 | if (isset($metaData[$plugin]['description'])) { | ||
199 | $metaData[$plugin]['description'] = t($metaData[$plugin]['description']); | ||
200 | } | ||
201 | // Read parameters and format them into an array. | ||
202 | if (isset($metaData[$plugin]['parameters'])) { | ||
203 | $params = explode(';', $metaData[$plugin]['parameters']); | ||
204 | } else { | ||
205 | $params = array(); | ||
206 | } | ||
207 | $metaData[$plugin]['parameters'] = array(); | ||
208 | foreach ($params as $param) { | ||
209 | if (empty($param)) { | ||
210 | continue; | ||
211 | } | ||
212 | |||
213 | $metaData[$plugin]['parameters'][$param]['value'] = ''; | ||
214 | // Optional parameter description in parameter.PARAM_NAME= | ||
215 | if (isset($metaData[$plugin]['parameter.' . $param])) { | ||
216 | $metaData[$plugin]['parameters'][$param]['desc'] = t($metaData[$plugin]['parameter.' . $param]); | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | |||
221 | return $metaData; | ||
222 | } | ||
223 | |||
224 | /** | ||
225 | * Return the list of encountered errors. | ||
226 | * | ||
227 | * @return array List of errors (empty array if none exists). | ||
228 | */ | ||
229 | public function getErrors() | ||
230 | { | ||
231 | return $this->errors; | ||
232 | } | ||
233 | } | ||
diff --git a/application/plugin/exception/PluginFileNotFoundException.php b/application/plugin/exception/PluginFileNotFoundException.php new file mode 100644 index 00000000..e5386f02 --- /dev/null +++ b/application/plugin/exception/PluginFileNotFoundException.php | |||
@@ -0,0 +1,23 @@ | |||
1 | <?php | ||
2 | namespace Shaarli\Plugin\Exception; | ||
3 | |||
4 | use Exception; | ||
5 | |||
6 | /** | ||
7 | * Class PluginFileNotFoundException | ||
8 | * | ||
9 | * Raise when plugin files can't be found. | ||
10 | */ | ||
11 | class PluginFileNotFoundException extends Exception | ||
12 | { | ||
13 | /** | ||
14 | * Construct exception with plugin name. | ||
15 | * Generate message. | ||
16 | * | ||
17 | * @param string $pluginName name of the plugin not found | ||
18 | */ | ||
19 | public function __construct($pluginName) | ||
20 | { | ||
21 | $this->message = sprintf(t('Plugin "%s" files not found.'), $pluginName); | ||
22 | } | ||
23 | } | ||