1<?php
2
3if (IN_serendipity !== true) {
4    die ('Don\'t hack!');
5}
6
7if (!serendipity_checkPermission('adminPlugins')) {
8    return;
9}
10
11$data = array();
12
13include_once S9Y_INCLUDE_PATH . 'include/plugin_api.inc.php';
14include_once S9Y_INCLUDE_PATH . 'include/functions_entries_admin.inc.php';
15include_once S9Y_INCLUDE_PATH . 'include/functions_plugins_admin.inc.php';
16if (!class_exists('Smarty')) {
17    @define('SMARTY_DIR', S9Y_PEAR_PATH . 'Smarty/libs/');
18    include_once SMARTY_DIR . 'Smarty.class.php';
19}
20
21if (isset($_GET['serendipity']['plugin_to_move']) && isset($_GET['submit']) && serendipity_checkFormToken()) {
22
23    if (isset($_GET['serendipity']['event_plugin'])) {
24        $plugins = serendipity_plugin_api::enum_plugins('event', false);
25    } else {
26        $plugins = serendipity_plugin_api::enum_plugins('event', true);
27    }
28
29    /* Renumber the sort order to be certain that one actually exists
30        Also look for the one we're going to move */
31    $idx_to_move = -1;
32    for($idx = 0; $idx < count($plugins); $idx++) {
33        $plugins[$idx]['sort_order'] = $idx;
34
35        if ($plugins[$idx]['name'] == $_GET['serendipity']['plugin_to_move']) {
36            $idx_to_move = $idx;
37        }
38    }
39
40    /* If idx_to_move is still -1 then we never found it (shouldn't happen under normal conditions)
41        Also make sure the swapping idx is around */
42    if ($idx_to_move >= 0 && (($_GET['submit'] == 'move down' && $idx_to_move < (count($plugins)-1)) || ($_GET['submit'] == 'move up' && $idx_to_move > 0))) {
43
44        /* Swap the one were moving with the one that's in the spot we're moving to */
45        $tmp = $plugins[$idx_to_move]['sort_order'];
46
47        $plugins[$idx_to_move]['sort_order'] = (int)$plugins[$idx_to_move + ($_GET['submit'] == 'move down' ? 1 : -1)]['sort_order'];
48        $plugins[$idx_to_move + ($_GET['submit'] == 'move down' ? 1 : -1)]['sort_order'] = (int)$tmp;
49
50        /* Update table */
51        foreach($plugins as $plugin) {
52            $key = serendipity_db_escape_string($plugin['name']);
53            serendipity_db_query("UPDATE {$serendipity['dbPrefix']}plugins SET sort_order = {$plugin['sort_order']} WHERE name='$key'");
54        }
55    }
56
57    /* TODO: Moving The first Right oriented plugin up,
58            or the last left oriented plugin down
59            should not be displayed to the user as an option.
60            It's a behavior which really has no meaning. */
61}
62
63if (isset($_GET['serendipity']['plugin_to_conf'])) {
64
65    /* configure a specific instance */
66    $plugin =& serendipity_plugin_api::load_plugin($_GET['serendipity']['plugin_to_conf']);
67
68    if (!($plugin->protected === FALSE || $plugin->serendipity_owner == '0' || $plugin->serendipity_owner == $serendipity['authorid'] || serendipity_checkPermission('adminPluginsMaintainOthers'))) {
69        return;
70    }
71    $data['plugin_to_conf'] = true;
72
73    $bag  = new serendipity_property_bag;
74    $plugin->introspect($bag);
75
76    if (method_exists($plugin, 'performConfig')) {
77        $plugin->performConfig($bag);
78    }
79
80    $name = serendipity_specialchars($bag->get('name'));
81    $desc = serendipity_specialchars($bag->get('description'));
82    $license = serendipity_specialchars($bag->get('license'));
83
84    $documentation = $bag->get('website');
85
86    $config_names = $bag->get('configuration');
87    $config_groups = $bag->get('config_groups');
88
89    if (isset($_POST['SAVECONF']) && serendipity_checkFormToken()) {
90        /* enum properties and set their values */
91
92        $save_errors = array();
93        foreach ($config_names as $config_item) {
94            $cbag = new serendipity_property_bag;
95            if ($plugin->introspect_config_item($config_item, $cbag)) {
96                $value    = $_POST['serendipity']['plugin'][$config_item];
97
98                $validate = $plugin->validate($config_item, $cbag, $value);
99                if ($validate === true) {
100                    if (!empty($_POST['serendipity']['plugin']['override'][$config_item])) {
101                        $value = $_POST['serendipity']['plugin']['override'][$config_item];
102                    }
103
104                    if (is_array($_POST['serendipity']['plugin']['activate'][$config_item])) {
105                        $active_values = array();
106                        foreach($_POST['serendipity']['plugin']['activate'][$config_item] as $ordered_item_value) {
107                            $ordered_item_value;
108                            $active_values[] = $ordered_item_value;
109                        }
110                        $value = implode(',', $active_values);
111                    }
112
113                    $plugin->set_config($config_item, $value);
114                } else {
115                    $save_errors[] = $validate;
116                }
117            }
118        }
119
120        $plugin->cleanup();
121    }
122
123    if ( isset($save_errors) && is_array($save_errors) && count($save_errors) > 0 ) {
124        $data['save_errors'] = $save_errors;
125    } elseif ( isset($_POST['SAVECONF'])) {
126        $data['saveconf'] = true;
127        $data['timestamp'] = serendipity_strftime('%H:%M:%S');
128    }
129    $data['formToken'] =  serendipity_setFormToken();
130    $data['name'] = $name;
131    $data['class'] = get_class($plugin);
132    $data['desc'] = $desc;
133    $data['documentation'] = $documentation;
134    $data['plugin'] = $plugin;
135
136    if (@file_exists(dirname($plugin->pluginFile) . '/ChangeLog')) {
137        $data['changelog'] = true;
138    }
139
140    if (@file_exists(dirname($plugin->pluginFile) . '/documentation_' . $serendipity['lang'] . '.html')) {
141        $data['documentation_local'] = '/documentation_' . $serendipity['lang'] . '.html';
142    } elseif (@file_exists(dirname($plugin->pluginFile) . '/documentation_en.html')) {
143        $data['documentation_local'] = '/documentation_en.html';
144    } elseif (@file_exists(dirname($plugin->pluginFile) . '/documentation.html')) {
145         $data['documentation_local'] = '/documentation.html';
146    } elseif (@file_exists(dirname($plugin->pluginFile) . '/README')) {
147        $data['documentation_local'] = '/README';
148    }
149
150    $data['license'] = $license;
151    $data['config'] = serendipity_plugin_config($plugin, $bag, $name, $desc, $config_names, true, true, true, true, 'plugin', $config_groups);
152
153} elseif ( $serendipity['GET']['adminAction'] == 'addnew' && serendipity_checkFormToken()) {
154    $serendipity['GET']['type'] = $serendipity['GET']['type'] ?: 'sidebar';
155    $data['adminAction'] = 'addnew';
156    $data['type'] = $serendipity['GET']['type'];
157
158    # get plugin data from Spartacus
159    $foreignPlugins = $pluginstack = $errorstack = array();
160    serendipity_plugin_api::hook_event('backend_plugins_fetchlist', $foreignPlugins);
161    $pluginstack = array_merge((array)$foreignPlugins['pluginstack'], $pluginstack);
162    $errorstack  = array_merge((array)$foreignPlugins['errorstack'], $errorstack);
163    if ($serendipity['GET']['only_group'] == 'UPGRADE') {
164        // for upgrades, the distinction in sidebar and event-plugins is not useful. We will fetch both and mix the lists
165        if ($serendipity['GET']['type'] == 'event') {
166            $serendipity['GET']['type'] = 'sidebar';
167        } else {
168            $serendipity['GET']['type'] = 'event';
169        }
170        $foreignPluginsTemp = array();
171        serendipity_plugin_api::hook_event('backend_plugins_fetchlist', $foreignPluginsTemp);
172        $pluginstack = array_merge((array)$foreignPluginsTemp['pluginstack'], $pluginstack);
173        $errorstack  = array_merge((array)$foreignPluginsTemp['errorstack'], $errorstack);
174        $foreignPlugins = array_merge($foreignPlugins, $foreignPluginsTemp);
175    }
176
177    # load data for installed plugins
178    $plugins = serendipity_plugin_api::get_installed_plugins();
179    $classes = serendipity_plugin_api::enum_plugin_classes(($serendipity['GET']['type'] === 'event'));
180    if ($serendipity['GET']['only_group'] == 'UPGRADE') {
181        $classes = array_merge($classes, serendipity_plugin_api::enum_plugin_classes(!($serendipity['GET']['type'] === 'event')));
182        $data['type'] = 'both';
183    }
184    usort($classes, 'serendipity_pluginListSort');
185
186    $counter    = 0;
187    foreach ($classes as $class_data) {
188        $pluginFile =  serendipity_plugin_api::probePlugin($class_data['name'], $class_data['classname'], $class_data['pluginPath']);
189        $plugin     =& serendipity_plugin_api::getPluginInfo($pluginFile, $class_data, $serendipity['GET']['type']);
190
191        if (is_object($plugin)) {
192            // Object is returned when a plugin could not be cached.
193            $bag = new serendipity_property_bag;
194            $plugin->introspect($bag);
195
196            // If a foreign plugin is upgradable, keep the new version number.
197            if (isset($foreignPlugins['pluginstack'][$class_data['name']]) && isset($foreignPlugins['pluginstack'][$class_data['name']]['upgrade_version'])) {
198                $class_data['upgrade_version'] = $foreignPlugins['pluginstack'][$class_data['name']]['upgrade_version'];
199            }
200
201            $props = serendipity_plugin_api::setPluginInfo($plugin, $pluginFile, $bag, $class_data, 'local', $foreignPlugins);
202
203            $counter++;
204        } elseif (is_array($plugin)) {
205            // Array is returned if a plugin could be fetched from info cache
206            $props = $plugin;
207        } else {
208            $props = false;
209        }
210
211        if (is_array($props)) {
212            if (version_compare($props['version'], $props['upgrade_version'], '<')) {
213                $props['upgradable']      = true;
214                // since we merged sidebar and event plugins before, we can no longer rely on spartacus' $foreignPlugins['baseURI']
215                // NOTE: This is not nice and it would be better to move it into the plugins array instead, but that collides with the cache
216                if (strpos($class_data['name'], 'serendipity_plugin') !== false) {
217                    $baseURI = "&amp;serendipity[spartacus_fetch]=sidebar";
218                } else {
219                    $baseURI = "&amp;serendipity[spartacus_fetch]=event";
220                }
221                $props['customURI'] .= $baseURI . $foreignPlugins['upgradeURI'];
222            }
223
224            $props['installable']  = !($props['stackable'] === false && in_array($class_data['true_name'], $plugins));
225            $props['requirements'] = unserialize($props['requirements']);
226
227            if (empty($props['changelog']) && @file_exists(dirname($props['plugin_file']) . '/ChangeLog')) {
228                $props['changelog'] = 'plugins/' . $props['pluginPath'] . '/ChangeLog';
229            }
230
231            if (empty($props['local_documentation'])) {
232                if (@file_exists(dirname($props['plugin_file']) . '/documentation_' . $serendipity['lang'] . '.html')) {
233                    $props['local_documentation'] = 'plugins/' . $props['pluginPath'] . '/documentation_' . $serendipity['lang'] . '.html';
234                } elseif (@file_exists(dirname($props['plugin_file']) . '/documentation_en.html')) {
235                    $props['local_documentation'] = 'plugins/' . $props['pluginPath'] . '/documentation_en.html';
236                } elseif (@file_exists(dirname($props['plugin_file']) . '/documentation.html')) {
237                    $props['local_documentation'] = 'plugins/' . $props['pluginPath'] . '/documentation.html';
238                } elseif (@file_exists(dirname($props['plugin_file']) . '/README')) {
239                    $props['local_documentation'] = 'plugins/' . $props['pluginPath'] . '/README';
240                }
241            }
242            $pluginstack[$class_data['true_name']] = $props;
243        } else {
244            // False is returned if a plugin could not be instantiated
245            $errorstack[] = $class_data['true_name'];
246        }
247    }
248
249    usort($pluginstack, 'serendipity_pluginListSort');
250    $pluggroups     = array();
251    $pluggroups[''] = array();
252    foreach($pluginstack AS $plugname => $plugdata) {
253        # add pluginsource to pluginstack
254        if (isset($foreignPlugins['pluginstack'][$plugdata['class_name']])) {
255            # remote plugin
256            $plugdata['pluginsource'] = 'Spartacus';
257        } elseif (serendipity_plugin_api::is_bundled_plugin($plugdata['class_name'])) {
258            # bundled plugin
259            $plugdata['pluginsource'] = PLUGIN_SOURCE_BUNDLED;
260        } else {
261            # everything else must be "local"
262            $plugdata['pluginsource'] = PLUGIN_SOURCE_LOCAL;
263        }
264        # create pluggroups
265        if ($serendipity['GET']['only_group'] == 'ALL') {
266            $pluggroups['ALL'][] = $plugdata;
267        } elseif ($serendipity['GET']['only_group'] == 'UPGRADE' && $plugdata['upgradable']) {
268            $pluggroups['UPGRADE'][] = $plugdata;
269        } elseif (is_array($plugdata['groups'])) {
270            foreach($plugdata['groups'] AS $group) {
271                $pluggroups[$group][] = $plugdata;
272            }
273        } else {
274            $pluggroups[''][] = $plugdata;
275        }
276    }
277
278    ksort($pluggroups);
279
280    $data['count_pluginstack'] = count($pluginstack);
281    $data['errorstack'] = $errorstack;
282
283    if ($serendipity['GET']['only_group'] == 'UPGRADE') {
284        serendipity_plugin_api::hook_event('backend_pluginlisting_header_upgrade', $pluggroups);
285    }
286
287    $available_groups = array_keys($pluggroups);
288    $data['available_groups'] = $available_groups;
289    $groupnames = array();
290    foreach($available_groups as $available_group) {
291        $groupnames[$available_group] = serendipity_groupname($available_group);
292    }
293    $data['groupnames'] = $groupnames;
294    $data['pluggroups'] = $pluggroups;
295    $data['formToken'] = serendipity_setFormToken();
296    $data['only_group'] = $serendipity['GET']['only_group'];
297    $data['available_upgrades'] = isset($pluggroups['UPGRADE']);
298    $requirement_failures = array();
299
300    foreach($pluggroups AS $pluggroup => $groupstack) {
301        foreach ($groupstack as $plug) {
302            if ( !empty($plug['requirements']['serendipity']) && version_compare($plug['requirements']['serendipity'], serendipity_getCoreVersion($serendipity['version']), '>') ) {
303                $requirement_failures[$plug['class_name']] = array("s9y" => true);
304            }
305
306            if ( !empty($plug['requirements']['php']) && version_compare($plug['requirements']['php'], phpversion(), '>') ) {
307                if (isset($requirement_failures[$plug['class_name']])) {
308                    $requirement_failures[$plug['class_name']] = array_merge($requirement_failures[$plug['class_name']] , array("php" => true));
309                } else {
310                    $requirement_failures[$plug['class_name']] = array("php" => true);
311                }
312            }
313
314            if ( !empty($plug['requirements']['smarty']) && version_compare($plug['requirements']['smarty'], str_replace('Smarty-', '', Smarty::SMARTY_VERSION), '>') ) {
315                if (isset($requirement_failures[$plug['class_name']])) {
316                     $requirement_failures[$plug['class_name']] = array_merge($requirement_failures[$plug['class_name']] , array("smarty" => true));
317                } else {
318                    $requirement_failures[$plug['class_name']] = array("smarty" => true);
319                }
320            }
321
322        }
323    }
324    $data['requirement_failures'] = $requirement_failures;
325} elseif ( $serendipity['GET']['adminAction'] == 'renderOverlay' ) {
326    $data['adminAction'] = 'overlay';
327} else {
328    /* show general plugin list */
329
330    /* get sidebar locations */
331    serendipity_smarty_init();
332
333    if (is_array($template_config)) {
334        $template_vars =& serendipity_loadThemeOptions($template_config);
335    }
336
337    $col_assoc = array(
338        'event_col'  => 'event',
339        'eventh_col' => 'eventh'
340    );
341
342    if (isset($template_vars['sidebars'])) {
343        $sidebars = explode(',', $template_vars['sidebars']);
344    } elseif (isset($serendipity['sidebars'])) {
345        $sidebars = $serendipity['sidebars'];
346    } else {
347        $sidebars = array('left', 'hide', 'right');
348    }
349
350    foreach($sidebars AS $sidebar) {
351        $col_assoc[$sidebar . '_col'] = $sidebar;
352    }
353
354    if (isset($_POST['SAVE'])  && serendipity_checkFormToken()) {
355        $pos=0;
356        foreach($_POST['serendipity']['plugin'] as $plugin) {
357            serendipity_db_query("UPDATE {$serendipity['dbPrefix']}plugins
358                                     SET sort_order = ".  $pos . "
359                                   WHERE name='" . serendipity_db_escape_string($plugin['id']) . "'");
360
361            serendipity_plugin_api::update_plugin_placement(
362                addslashes($plugin['id']),
363                addslashes($plugin['placement'])
364            );
365
366            serendipity_plugin_api::update_plugin_owner(
367                addslashes($plugin['id']),
368                addslashes($_POST['serendipity']['ownership'][$plugin['name']])
369            );
370            $pos++;
371        }
372    }
373
374    if (isset($serendipity['GET']['install_plugin']) && serendipity_checkFormToken()) {
375        $authorid = $serendipity['authorid'];
376        if (serendipity_checkPermission('adminPluginsMaintainOthers')) {
377            $authorid = '0';
378        }
379        if ($serendipity['ajax']) {
380             // we need to catch the spartacus messages to return only them to the ajax call (used by the update all button)
381            ob_start();
382        }
383        $fetchplugin_data = array('GET'     => &$serendipity['GET'],
384                                  'install' => true);
385
386
387        serendipity_plugin_api::hook_event('backend_plugins_fetchplugin', $fetchplugin_data);
388
389        // we now have to check that the plugin is not already installed, or stackable, to prevent invalid double instances
390        $new_plugin = true;
391        if ($fetchplugin_data['install']) {
392            // spartacus will set this to false on upgrade, and we want to check this only on install
393            foreach (serendipity_plugin_api::get_installed_plugins() as $pluginName) {
394                if ($serendipity['GET']['install_plugin'] === $pluginName) {
395                    $existingPlugin =& serendipity_plugin_api::load_plugin($serendipity['GET']['install_plugin']);
396                    if (is_object($existingPlugin)) {
397                        $bag = new serendipity_property_bag();
398                        $existingPlugin->introspect($bag);
399                        if ($bag->get('stackable') != true) {
400                            $new_plugin = false;
401                        }
402                    }
403                    break;
404                }
405            }
406        }
407
408        $data['new_plugin_failed'] = ! $new_plugin;
409
410        if ($fetchplugin_data['install'] && $new_plugin) {
411            $serendipity['debug']['pluginload'] = array();
412            $inst = serendipity_plugin_api::create_plugin_instance($serendipity['GET']['install_plugin'], null, (serendipity_plugin_api::is_event_plugin($serendipity['GET']['install_plugin']) ? 'event': 'right'), $authorid, serendipity_db_escape_string($serendipity['GET']['pluginPath']));
413
414            /* Load the new plugin */
415            $plugin = &serendipity_plugin_api::load_plugin($inst);
416            if (!is_object($plugin)) {
417                echo "DEBUG: Plugin " . serendipity_specialchars($inst) . " not an object: " . serendipity_specialchars(print_r($plugin, true))
418                     . ".<br />Input: " . serendipity_specialchars(print_r($serendipity['GET'], true)) . ".<br /><br />\n\nThis error
419                     can happen if a plugin was not properly downloaded (check your plugins directory if the requested plugin
420                     was downloaded) or the inclusion of a file failed (permissions?)<br />\n";
421                echo "Backtrace:<br />\n" . nl2br(serendipity_specialchars(implode("\n", $serendipity['debug']['pluginload']))) . "<br />";
422            }
423            $bag  = new serendipity_property_bag;
424            $plugin->introspect($bag);
425
426            serendipity_plugin_api::hook_event('backend_plugins_install', $serendipity['GET']['install_plugin'], $fetchplugin_data);
427
428            if ($bag->is_set('configuration')) {
429                /* Only play with the plugin if there is something to play with */
430                echo '<script type="text/javascript">location.href = \'' . $serendipity['baseURL'] . 'serendipity_admin.php?serendipity[adminModule]=plugins&serendipity[plugin_to_conf]=' . $inst . '\';</script>';
431                die();
432            } else {
433                /* If no config is available, redirect to plugin overview, because we do not want that a user can install the plugin a second time via accidental browser refresh */
434                echo '<script type="text/javascript">location.href = \'' . $serendipity['baseURL'] . 'serendipity_admin.php?serendipity[adminModule]=plugins\';</script>';
435                die();
436            }
437        } else {
438            serendipity_plugin_api::hook_event('backend_plugins_update', $serendipity['GET']['install_plugin'], $fetchplugin_data);
439        }
440        if ($serendipity['ajax']) {
441            $data['ajax_output'] = ob_get_contents();
442            ob_end_clean();
443        }
444    }
445
446    if (isset($_POST['REMOVE']) && serendipity_checkFormToken()) {
447        if (is_array($_POST['serendipity']['plugin_to_remove'])) {
448            foreach ($_POST['serendipity']['plugin_to_remove'] as $key) {
449                $plugin =& serendipity_plugin_api::load_plugin($key);
450
451                if ($plugin->serendipity_owner == '0' || $plugin->serendipity_owner == $serendipity['authorid'] || serendipity_checkPermission('adminPluginsMaintainOthers')) {
452                    serendipity_plugin_api::remove_plugin_instance($key);
453                }
454            }
455        }
456    }
457    if (isset($_POST['SAVE'])) {
458        $data['save'] = true;
459        $data['timestamp'] = serendipity_strftime('%H:%M:%S');
460    }
461
462    ob_start();
463    serendipity_plugin_api::hook_event('backend_pluginlisting_header', $null);
464    $data['backend_pluginlisting_header'] = ob_get_contents();
465    ob_end_clean();
466
467
468    ob_start();
469    serendipity_plugin_api::hook_event('backend_plugins_sidebar_header', $serendipity);
470    $data['backend_plugins_sidebar_header'] = ob_get_contents();
471    ob_end_clean();
472
473    $data['sidebar_plugins'] = show_plugins(false, $sidebars);
474
475    ob_start();
476    serendipity_plugin_api::hook_event('backend_plugins_event_header', $serendipity);
477    $data['backend_plugins_event_header'] = ob_get_contents();
478    ob_end_clean();
479
480    $data['event_plugins'] = show_plugins(true);
481
482    if ($serendipity['memSnaps'] && count($serendipity['memSnaps']) > 0) {
483        $data['$memsnaps'] = $serendipity['memSnaps'];
484    }
485    $data['updateAllMsg'] = isset($serendipity['GET']['updateAllMsg']);
486}
487
488$data['urltoken']      = serendipity_setFormToken('url');
489echo serendipity_smarty_show('admin/plugins.inc.tpl', $data);
490
491
492/* vim: set sts=4 ts=4 expandtab : */
493