|
1 <?php |
|
2 |
|
3 /* |
|
4 * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between |
|
5 * Version 1.1.3 (Caoineag alpha 3) |
|
6 * Copyright (C) 2006-2007 Dan Fuhry |
|
7 * |
|
8 * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License |
|
9 * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. |
|
10 * |
|
11 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied |
|
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. |
|
13 */ |
|
14 |
|
15 /** |
|
16 * SYNOPSIS OF PLUGIN FRAMEWORK |
|
17 * |
|
18 * The new plugin manager is making an alternative approach to managing plugin files by allowing metadata to be embedded in them |
|
19 * or optionally included from external files. This method is API- and format-compatible with old plugins. The change is being |
|
20 * made because we believe this will provide greater flexibility within plugin files. |
|
21 * |
|
22 * Plugin files can contain one or more specially formatted comment blocks with metadata, language strings, and installation or |
|
23 * upgrade SQL schemas. For this to work, plugins need to define their version numbers in an Enano-readable and standardized |
|
24 * format, and we think the best way to do this is with JSON. It is important that plugins define both the current version and |
|
25 * a list of all past versions, and then have upgrade sections telling which version they go from and which one they go to. |
|
26 * |
|
27 * The format for the special comment blocks is: |
|
28 <code> |
|
29 /**!blocktype( param1 = "value1" [ param2 = "value2" ... ] )** |
|
30 |
|
31 ... block content ... |
|
32 |
|
33 **!* / (remove that last space) |
|
34 </code> |
|
35 * The format inside blocks varies. Metadata and language strings will be in JSON; installation and upgrade schemas will be in |
|
36 * SQL. You can include an external file into a block using the following syntax inside of a block: |
|
37 <code> |
|
38 !include "path/to/file" |
|
39 </code> |
|
40 * The file will always be relative to the Enano root. So if your plugin has a language file in ENANO_ROOT/plugins/fooplugin/, |
|
41 * you would use "plugins/fooplugin/language.json". |
|
42 * |
|
43 * The format for plugin metadata is as follows: |
|
44 <code> |
|
45 /**!info** |
|
46 { |
|
47 "Plugin Name" : "Foo plugin", |
|
48 "Plugin URI" : "http://fooplugin.enanocms.org/", |
|
49 "Description" : "Some short descriptive text", |
|
50 "Author" : "John Doe", |
|
51 "Version" : "0.1", |
|
52 "Author URI" : "http://yourdomain.com/", |
|
53 "Version list" : [ "0.1-alpha1", "0.1-alpha2", "0.1-beta1", "0.1" ] |
|
54 } |
|
55 **!* / |
|
56 </code> |
|
57 * This is the format for language data: |
|
58 <code> |
|
59 /**!language** |
|
60 { |
|
61 eng: { |
|
62 categories: [ 'meta', 'foo', 'bar' ], |
|
63 strings: { |
|
64 meta: { |
|
65 foo: "Foo strings", |
|
66 bar: "Bar strings" |
|
67 }, |
|
68 foo: { |
|
69 string_name: "string value", |
|
70 string_name_2: "string value 2" |
|
71 } |
|
72 } |
|
73 } |
|
74 } |
|
75 **!* / (once more, remove the space in there) |
|
76 </code> |
|
77 * Here is the format for installation schemas: |
|
78 <code> |
|
79 /**!install** |
|
80 |
|
81 CREATE TABLE {{TABLE_PREFIX}}foo_table( |
|
82 ... |
|
83 ) |
|
84 |
|
85 **!* / |
|
86 </code> |
|
87 * And finally, the format for upgrade schemas: |
|
88 <code> |
|
89 /**!upgrade from = "0.1-alpha1" to = "0.1-alpha2" ** |
|
90 |
|
91 **!* / |
|
92 </code> |
|
93 * Remember that upgrades will always be done incrementally, so if the user is upgrading 0.1-alpha2 to 0.1, Enano's plugin |
|
94 * engine will run the 0.1-alpha2 to 0.1-beta1 upgrader, then the 0.1-beta1 to 0.1 upgrader, going by the versions listed in |
|
95 * the example metadata block above. |
|
96 * |
|
97 * All of this information is effective as of Enano 1.1.4. |
|
98 */ |
|
99 |
|
100 // Plugin manager "2.0" |
|
101 |
|
102 function page_Admin_PluginManager() |
|
103 { |
|
104 global $db, $session, $paths, $template, $plugins; // Common objects |
|
105 global $lang; |
|
106 if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN ) |
|
107 { |
|
108 $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true); |
|
109 echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>'; |
|
110 echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>'; |
|
111 return; |
|
112 } |
|
113 |
|
114 // Are we processing an AJAX request from the smartform? |
|
115 if ( $paths->getParam(0) == 'action.json' ) |
|
116 { |
|
117 // Set to application/json to discourage advertisement scripts |
|
118 header('Content-Type: application/json'); |
|
119 |
|
120 // Init return data |
|
121 $return = array('mode' => 'error', 'error' => 'undefined'); |
|
122 |
|
123 // Start parsing process |
|
124 try |
|
125 { |
|
126 // Is the request properly sent on POST? |
|
127 if ( isset($_POST['r']) ) |
|
128 { |
|
129 // Try to decode the request |
|
130 $request = enano_json_decode($_POST['r']); |
|
131 // Is the action to perform specified? |
|
132 if ( isset($request['mode']) ) |
|
133 { |
|
134 switch ( $request['mode'] ) |
|
135 { |
|
136 default: |
|
137 // The requested action isn't something this script knows how to do |
|
138 $return = array( |
|
139 'mode' => 'error', |
|
140 'error' => 'Unknown mode "' . $request['mode'] . '" sent in request' |
|
141 ); |
|
142 break; |
|
143 } |
|
144 } |
|
145 else |
|
146 { |
|
147 // Didn't specify action |
|
148 $return = array( |
|
149 'mode' => 'error', |
|
150 'error' => 'Missing key "mode" in request' |
|
151 ); |
|
152 } |
|
153 } |
|
154 else |
|
155 { |
|
156 // Didn't send a request |
|
157 $return = array( |
|
158 'mode' => 'error', |
|
159 'error' => 'No request specified' |
|
160 ); |
|
161 } |
|
162 } |
|
163 catch ( Exception $e ) |
|
164 { |
|
165 // Sent a request but it's not valid JSON |
|
166 $return = array( |
|
167 'mode' => 'error', |
|
168 'error' => 'Invalid request - JSON parsing failed' |
|
169 ); |
|
170 } |
|
171 |
|
172 echo enano_json_encode($return); |
|
173 |
|
174 return true; |
|
175 } |
|
176 |
|
177 // |
|
178 // Not a JSON request, output normal HTML interface |
|
179 // |
|
180 |
|
181 // Scan all plugins |
|
182 $plugin_list = array(); |
|
183 |
|
184 if ( $dirh = @opendir( ENANO_ROOT . '/plugins' ) ) |
|
185 { |
|
186 while ( $dh = @readdir($dirh) ) |
|
187 { |
|
188 if ( !preg_match('/\.php$/i', $dh) ) |
|
189 continue; |
|
190 $fullpath = ENANO_ROOT . "/plugins/$dh"; |
|
191 // it's a PHP file, attempt to read metadata |
|
192 // pass 1: try to read a !info block |
|
193 $blockdata = $plugins->parse_plugin_blocks($fullpath, 'info'); |
|
194 if ( empty($blockdata) ) |
|
195 { |
|
196 // no !info block, check for old header |
|
197 $fh = @fopen($fullpath, 'r'); |
|
198 if ( !$fh ) |
|
199 // can't read, bail out |
|
200 continue; |
|
201 $plugin_data = array(); |
|
202 for ( $i = 0; $i < 8; $i++ ) |
|
203 { |
|
204 $plugin_data[] = @fgets($fh, 8096); |
|
205 } |
|
206 // close our file handle |
|
207 fclose($fh); |
|
208 // is the header correct? |
|
209 if ( trim($plugin_data[0]) != '<?php' || trim($plugin_data[1]) != '/*' ) |
|
210 { |
|
211 // nope. get out. |
|
212 continue; |
|
213 } |
|
214 // parse all the variables |
|
215 $plugin_meta = array(); |
|
216 for ( $i = 2; $i <= 7; $i++ ) |
|
217 { |
|
218 if ( !preg_match('/^([A-z0-9 ]+?): (.+?)$/', trim($plugin_data[$i]), $match) ) |
|
219 continue 2; |
|
220 $plugin_meta[ strtolower($match[1]) ] = $match[2]; |
|
221 } |
|
222 } |
|
223 else |
|
224 { |
|
225 // parse JSON block |
|
226 $plugin_data =& $blockdata[0]['value']; |
|
227 $plugin_data = enano_clean_json(enano_trim_json($plugin_data)); |
|
228 try |
|
229 { |
|
230 $plugin_meta_uc = enano_json_decode($plugin_data); |
|
231 } |
|
232 catch ( Exception $e ) |
|
233 { |
|
234 continue; |
|
235 } |
|
236 // convert all the keys to lowercase |
|
237 $plugin_meta = array(); |
|
238 foreach ( $plugin_meta_uc as $key => $value ) |
|
239 { |
|
240 $plugin_meta[ strtolower($key) ] = $value; |
|
241 } |
|
242 } |
|
243 if ( !isset($plugin_meta) || !is_array(@$plugin_meta) ) |
|
244 { |
|
245 // parsing didn't work. |
|
246 continue; |
|
247 } |
|
248 // check for required keys |
|
249 $required_keys = array('plugin name', 'plugin uri', 'description', 'author', 'version', 'author uri'); |
|
250 foreach ( $required_keys as $key ) |
|
251 { |
|
252 if ( !isset($plugin_meta[$key]) ) |
|
253 // not set, skip this plugin |
|
254 continue 2; |
|
255 } |
|
256 // all checks passed |
|
257 $plugin_list[$dh] = $plugin_meta; |
|
258 } |
|
259 } |
|
260 echo '<pre>' . print_r($plugin_list, true) . '</pre>'; |
|
261 } |