Index: plugins/serendipity_event_bbcode/serendipity_event_bbcode.php =================================================================== --- plugins/serendipity_event_bbcode/serendipity_event_bbcode.php (revision 202) +++ plugins/serendipity_event_bbcode/serendipity_event_bbcode.php (working copy) @@ -40,6 +40,7 @@ )); $propbag->add('cachable_events', array('frontend_display' => true)); $propbag->add('event_hooks', array('frontend_display' => true, 'frontend_comment' => true, 'css' => true)); + $propbag->add('groups', array('MARKUP')); $this->markup_elements = array( array( Index: plugins/serendipity_event_karma/serendipity_event_karma.php =================================================================== --- plugins/serendipity_event_karma/serendipity_event_karma.php (revision 202) +++ plugins/serendipity_event_karma/serendipity_event_karma.php (working copy) @@ -123,7 +123,7 @@ )); $propbag->add('event_hooks', array('frontend_configure' => true, 'entry_display' => true, 'css' => true, 'event_additional_statistics' => true)); $propbag->add('scrambles_true_content', true); - + $propbag->add('groups', array('STATISTICS', 'FRONTEND_ENTRY')); $propbag->add('configuration', array('karma_active', 'visits_active', 'max_entrytime', 'max_votetime', 'extended_only', 'max_karmatime', 'logging')); } Index: plugins/serendipity_event_s9ymarkup/serendipity_event_s9ymarkup.php =================================================================== --- plugins/serendipity_event_s9ymarkup/serendipity_event_s9ymarkup.php (revision 202) +++ plugins/serendipity_event_s9ymarkup/serendipity_event_s9ymarkup.php (working copy) @@ -41,6 +41,7 @@ )); $propbag->add('cachable_events', array('frontend_display' => true)); $propbag->add('event_hooks', array('frontend_display' => true, 'frontend_comment' => true)); + $propbag->add('groups', array('MARKUP')); $this->markup_elements = array( array( Index: plugins/serendipity_event_spartacus/serendipity_event_spartacus.php =================================================================== --- plugins/serendipity_event_spartacus/serendipity_event_spartacus.php (revision 202) +++ plugins/serendipity_event_spartacus/serendipity_event_spartacus.php (working copy) @@ -42,7 +42,13 @@ class serendipity_event_spartacus extends serendipity_event { var $title = PLUGIN_EVENT_SPARTACUS_NAME; + var $purgeCache = false; + function microtime_float() { + list($usec, $sec) = explode(" ", microtime()); + return ((float)$usec + (float)$sec); + } + function introspect(&$propbag) { global $serendipity; @@ -51,19 +57,17 @@ $propbag->add('description', PLUGIN_EVENT_SPARTACUS_DESC); $propbag->add('stackable', false); $propbag->add('author', 'Garvin Hicking'); - $propbag->add('version', '1.2'); + $propbag->add('version', '1.3'); $propbag->add('requirements', array( - 'serendipity' => '0.8', + 'serendipity' => '0.9', 'smarty' => '2.6.7', 'php' => '4.1.0' )); $propbag->add('event_hooks', array( - 'backend_plugins_sidebar_header' => true, - 'backend_plugins_event_header' => true, - 'backend_plugins_fetchlist' => true, 'backend_plugins_fetchplugin' => true )); + $propbag->add('groups', array('SYSTEM')); } function generate_content(&$title) { @@ -126,7 +130,7 @@ return true; } - function &fetchfile($url, $target, $cacheTimeout = 0) { + function &fetchfile($url, $target, $cacheTimeout = 0, $decode_utf8 = false) { printf(PLUGIN_EVENT_SPARTACUS_FETCHING, '' . basename($url) . ''); echo '
'; @@ -165,18 +169,45 @@ echo '
'; return false; } + + if ($decode_utf8) { + $this->decode($data); + } fwrite($fp, $data); fclose($fp); echo PLUGIN_EVENT_SPARTACUS_FETCHED_DONE; echo '
'; + $this->purgeCache = true; } } return $data; } - function &fetchOnline($type) { + function decode(&$data) { + switch (strtolower(LANG_CHARSET)) { + case 'utf-8': + // The XML file is UTF-8 format. No changes needed. + break; + + case 'iso-8859-1': + $data = utf8_decode($data); + break; + + default: + if (function_exists('iconv')) { + $data = iconv('UTF-8', LANG_CHARSET, $data); + } elseif (function_exists('recode')) { + $data = recode('utf-8..' . LANG_CHARSET, $data); + } + break; + } + } + + function &fetchOnline($type, $no_cache = false) { + global $serendipity; + switch($type) { // Sanitize to not fetch other URLs default: @@ -188,15 +219,28 @@ $url_type = 'sidebar'; break; } + + if (isset($serendipity['languages'][$serendipity['lang']])) { + $lang = $serendipity['lang']; + } else { + $lang = 'en'; + } - $url = 'http://netmirror.org/mirror/serendipity/package_' . $url_type . '.xml'; + $url = 'http://netmirror.org/mirror/serendipity/package_' . $url_type . '_' . $lang . '.xml'; $cacheTimeout = 60*60*12; // XML file is cached for half a day - $target = $serendipity['serendipityPath'] . 'templates_c/package_' . $url_type . '.xml'; + $target = $serendipity['serendipityPath'] . 'templates_c/package_' . $url_type . '_' . $lang . '.xml'; - $xml = $this->fetchfile($url, $target, $cacheTimeout); + $xml = $this->fetchfile($url, $target, $cacheTimeout, true); echo '

'; + $new_crc = md5($xml); + $last_crc = $this->get_config('last_crc_' . $url_type); + + if (!$no_cache && !$this->purgeCache && $last_crc == $new_crc) { + return 'cached'; + } + // XML functions $p = xml_parser_create(); xml_parser_set_option($p, XML_OPTION_CASE_FOLDING, 0); @@ -212,14 +256,80 @@ 'children' => $this->GetChildren($vals, $i) ); + $this->set_config('last_crc_' . $url_type, $new_crc); + return $tree; } + function &getCachedPlugins(&$plugins, $type) { + global $serendipity; + static $pluginlist = null; + + if ($pluginlist === null) { + $data = serendipity_db_query("SELECT p.*, + pc.category + FROM {$serendipity['dbPrefix']}pluginlist AS p + LEFT OUTER JOIN {$serendipity['dbPrefix']}plugincategories AS pc + ON pc.class_name = p.class_name + WHERE p.pluginlocation = 'Spartacus' AND + p.plugintype = '" . serendipity_db_escape_string($type) . "'"); + + if (is_array($data)) { + foreach($data AS $p) { + $p['stackable'] = serendipity_db_bool($p['stackable']); + $p['requirements'] = unserialize($p['requirements']); + $this->checkPlugin($p, $plugins, $p['pluginlocation']); + + if (!isset($pluginlist[$p['plugin_file']])) { + $pluginlist[$p['plugin_file']] = $p; + } + + $pluginlist[$p['plugin_file']]['groups'][] = $p['category']; + } + } + } + + return $pluginlist; + } + + function checkPlugin(&$data, &$plugins, $type) { + $installable = true; + $upgradeLink = ''; + if (in_array($data['class_name'], $plugins)) { + $infoplugin =& serendipity_plugin_api::load_plugin($data['class_name']); + if (is_object($infoplugin)) { + $bag = new serendipity_property_bag; + $infoplugin->introspect($bag); + if ($bag->get('version') == $data['version']) { + $installable = false; + } elseif (version_compare($bag->get('version'), $data['version'], '<')) { + $data['upgradable'] = true; + $data['upgrade_version'] = $pluginstack[$i]['version']; + $data['version'] = $bag->get('version'); + $upgradeLink = '&serendipity[spartacus_upgrade]=true'; + } + } + } + + $data['installable'] = $installable; + $data['pluginPath'] = 'online_repository'; + $data['pluginlocation'] = 'Spartacus'; + $data['plugintype'] = $type; + $data['customURI'] = '&serendipity[spartacus_fetch]=' . $type . $upgradeLink; + + return true; + } + function &buildList(&$tree, $type) { $plugins = serendipity_plugin_api::get_installed_plugins(); + + if ($tree === 'cached') { + return $this->getCachedPlugins($plugins, $type); + } + $pluginstack = array(); $i = 0; - + foreach($tree[0]['children'] AS $idx => $subtree) { if ($subtree['tag'] == 'package') { $i++; @@ -227,16 +337,22 @@ foreach($subtree['children'] AS $child => $childtree) { switch($childtree['tag']) { case 'name': - $pluginstack[$i]['plugin_class'] = $childtree['value']; - $pluginstack[$i]['class_name'] = $childtree['value']; + $pluginstack[$i]['plugin_class'] = + $pluginstack[$i]['plugin_file'] = + $pluginstack[$i]['class_name'] = + $pluginstack[$i]['true_name'] = $childtree['value']; break; case 'summary': $pluginstack[$i]['name'] = $childtree['value']; break; + case 'groups': + $pluginstack[$i]['groups'] = explode(',', $childtree['value']); + break; + case 'description': - $pluginstack[$i]['desc'] = $childtree['value']; + $pluginstack[$i]['description'] = $childtree['value']; break; case 'release': @@ -260,28 +376,14 @@ break; } } + + $this->checkPlugin($pluginstack[$i], $plugins, $type); - $installable = true; - $upgradeLink = ''; - if (in_array($pluginstack[$i]['class_name'], $plugins)) { - $infoplugin =& serendipity_plugin_api::load_plugin($pluginstack[$i]['class_name']); - if (is_object($infoplugin)) { - $bag = new serendipity_property_bag; - $infoplugin->introspect($bag); - if ($bag->get('version') == $pluginstack[$i]['version']) { - $installable = false; - } elseif (version_compare($bag->get('version'), $pluginstack[$i]['version'], '<')) { - $pluginstack[$i]['upgradable'] = true; - $pluginstack[$i]['upgrade_version'] = $pluginstack[$i]['version']; - $pluginstack[$i]['version'] = $bag->get('version'); - $upgradeLink = '&serendipity[spartacus_upgrade]=true'; - } - } - } - - $pluginstack[$i]['installable'] = $installable; - $pluginstack[$i]['pluginPath'] = 'online_repository'; - $pluginstack[$i]['customURI'] = '&serendipity[spartacus_fetch]=' . $type . $upgradeLink; + serendipity_plugin_api::setPluginInfo($pluginstack[$i], $pluginstack[$i]['plugin_file'], $i, $i, 'Spartacus'); + // Remove the temporary $i reference, as the array should be associative + $plugname = $pluginstack[$i]['true_name']; + $pluginstack[$plugname] = $pluginstack[$i]; + unset($pluginstack[$i]); } } @@ -353,30 +455,12 @@ if (isset($hooks[$event])) { switch($event) { - case 'backend_plugins_sidebar_header': -?> -
- -
- $this->buildList($this->fetchOnline($type), $type), - 'errorstack' => array() + 'pluginstack' => $this->buildList($this->fetchOnline($type), $type), + 'errorstack' => array() ); return true; @@ -384,7 +468,7 @@ case 'backend_plugins_fetchplugin': if (!empty($eventData['GET']['spartacus_fetch'])) { - $baseDir = $this->download($this->fetchOnline($eventData['GET']['spartacus_fetch']), $eventData['GET']['install_plugin']); + $baseDir = $this->download($this->fetchOnline($eventData['GET']['spartacus_fetch'], true), $eventData['GET']['install_plugin']); if (!empty($baseDir)) { $eventData['GET']['pluginPath'] = $baseDir; } else { Index: plugins/serendipity_event_nl2br/serendipity_event_nl2br.php =================================================================== --- plugins/serendipity_event_nl2br/serendipity_event_nl2br.php (revision 202) +++ plugins/serendipity_event_nl2br/serendipity_event_nl2br.php (working copy) @@ -39,6 +39,7 @@ )); $propbag->add('cachable_events', array('frontend_display' => true)); $propbag->add('event_hooks', array('frontend_display' => true)); + $propbag->add('groups', array('MARKUP')); $this->markup_elements = array( array( Index: serendipity_config.inc.php =================================================================== --- serendipity_config.inc.php (revision 202) +++ serendipity_config.inc.php (working copy) @@ -21,7 +21,7 @@ include_once(S9Y_INCLUDE_PATH . 'include/compat.inc.php'); // The version string -$serendipity['version'] = '0.9-alpha3'; +$serendipity['version'] = '0.9-alpha4'; // Name of folder for the default theme $serendipity['defaultTemplate'] = 'default'; Index: include/plugin_api.inc.php =================================================================== --- include/plugin_api.inc.php (revision 202) +++ include/plugin_api.inc.php (working copy) @@ -152,9 +152,13 @@ if ($p == 'serendipity_plugin' && $class_name != 'serendipity_event' && (!$event_only || is_null($event_only))) { $classes[$class_name] = array('name' => '@' . $class_name, + 'type' => 'internal_event', + 'true_name' => $class_name, 'pluginPath' => ''); } elseif ($p == 'serendipity_event' && $class_name != 'serendipity_event' && ($event_only || is_null($event_only))) { $classes[$class_name] = array('name' => '@' . $class_name, + 'type' => 'internal_plugin', + 'true_name' => $class_name, 'pluginPath' => ''); } } @@ -179,35 +183,40 @@ if ($f{0} == '.' || $f == 'CVS' || !is_dir($ppath . '/' . $f)) { continue; } - + $subd = opendir($ppath . '/' . $f); if (!$subd) { continue; } // Instead of only looking for directories, search for files within subdirectories + $final_loop = false; while (($subf = readdir($subd)) !== false) { + if ($subf{0} == '.' || $subf == 'CVS') { continue; } - if (is_dir($ppath . '/' . $f . '/' . $subf) && $maindir != $ppath . '/' . $f) { + if (!$final_loop && is_dir($ppath . '/' . $f . '/' . $subf) && $maindir != $ppath . '/' . $f) { // Search for another level of subdirectories serendipity_plugin_api::traverse_plugin_dir($ppath . '/' . $f, $classes, $event_only, $f . '/'); + // We can break after that operation because the current directory has been fully checked already. + $final_loop = true; } if (!preg_match('@^[^_]+_(event|plugin)_.+\.php$@i', $subf)) { continue; } + $class_name = str_replace('.php', '', $subf); // If an external plugin/event already exists as internal, remove the internal reference because its redundant - if (isset($classes['@' . $subf])) { - unset($classes['@' . $subf]); + if (isset($classes['@' . $class_name])) { + unset($classes['@' . $class_name]); } // A local plugin will be preferred over general plugins [used when calling this function the second time] - if (isset($classes[$subf])) { - unset($classes[$subf]); + if (isset($classes[$class_name])) { + unset($classes[$class_name]); } if (!is_null($event_only) && $event_only && !serendipity_plugin_api::is_event_plugin($subf)) { @@ -218,11 +227,14 @@ continue; } - $class_name = str_replace('.php', '', $subf); $classes[$class_name] = array('name' => $class_name, + 'true_name' => $class_name, + 'type' => 'additional_plugin', 'pluginPath' => $maindir . $f); } + closedir($subd); } + closedir($d); } } @@ -301,29 +313,45 @@ if (empty($pluginPath)) { $pluginPath = $name; } + + $file = false; // First try the local path, and then (if existing) a shared library repository ... if (file_exists($serendipity['serendipityPath'] . 'plugins/' . $pluginPath . '/' . $name . '.php')) { - include_once $serendipity['serendipityPath'] . 'plugins/' . $pluginPath . '/' . $name . '.php'; + $file = $serendipity['serendipityPath'] . 'plugins/' . $pluginPath . '/' . $name . '.php'; } elseif (file_exists(S9Y_INCLUDE_PATH . 'plugins/' . $pluginPath . '/' . $name . '.php')) { - include_once S9Y_INCLUDE_PATH . 'plugins/' . $pluginPath . '/' . $name . '.php'; + $file = S9Y_INCLUDE_PATH . 'plugins/' . $pluginPath . '/' . $name . '.php'; } + + return $file; } - /* Creates an instance of a named plugin */ - function &load_plugin($instance_id, $authorid = null, $pluginPath = '') - { - global $serendipity; - + function getClassByInstanceID($instance_id, &$is_internal) { $instance = explode(':', $instance_id); $name = $instance[0]; if ($name{0} == '@') { $class_name = substr($name, 1); } else { + $class_name =& $name; + } + + return $class_name; + } + + /* Probes for the plugin filename */ + function probePlugin($instance_id, &$class_name, &$pluginPath) { + global $serendipity; + + $filename = false; + $is_internal = false; + + $class_name = serendipity_plugin_api::getClassByInstanceID($instance_id, $is_internal); + + if (!$is_internal) { /* plugin from the plugins/ dir */ - serendipity_plugin_api::includePlugin($name, $pluginPath); - if (!class_exists($name) && empty($pluginPath)) { + $filename = serendipity_plugin_api::includePlugin($class_name, $pluginPath); + if (empty($filename) && !empty($instance_id)) { $sql = "SELECT path from {$serendipity['dbPrefix']}plugins WHERE name = '" . $instance_id . "'"; $plugdata = serendipity_db_query($sql, true, 'both', false, false, false, true); if (is_array($plugdata) && isset($plugdata[0])) { @@ -331,19 +359,36 @@ } if (empty($pluginPath)) { - $pluginPath = $name; + $pluginPath = $class_name; } - serendipity_plugin_api::includePlugin($name, $pluginPath); + $filename = serendipity_plugin_api::includePlugin($class_name, $pluginPath); } - if (!class_exists($name)) { + if (empty($filename)) { return false; } + } - $class_name =& $name; + return $filename; + } + + /* Creates an instance of a named plugin */ + function &load_plugin($instance_id, $authorid = null, $pluginPath = '', $pluginFile = null) { + global $serendipity; + + if ($pluginFile === null) { + $class_name = ''; + $pluginFile = serendipity_plugin_api::probePlugin($instance_id, $class_name, $pluginPath); + } else { + $is_internal = false; + $class_name = serendipity_plugin_api::getClassByInstanceID($instance_id, $is_internal); } - + + if (!class_exists($class_name) && (empty($pluginFile) || !include_once($pluginFile))) { + return false; + } + $p =& new $class_name($instance_id); if (!is_null($authorid)) { $p->serendipity_owner = $authorid; @@ -358,6 +403,98 @@ return $p; } + function &getPluginInfo(&$pluginFile, &$class_data, $type) { + global $serendipity; + + static $pluginlist = null; + + if ($pluginlist === null) { + $data = serendipity_db_query("SELECT p.*, + pc.category + FROM {$serendipity['dbPrefix']}pluginlist AS p + LEFT OUTER JOIN {$serendipity['dbPrefix']}plugincategories AS pc + ON pc.class_name = p.class_name + WHERE p.pluginlocation = 'local' AND + p.plugintype = '" . serendipity_db_escape_string($type) . "'"); + if (is_array($data)) { + foreach($data AS $p) { + if (!isset($pluginlist[$p['plugin_file']])) { + $pluginlist[$p['plugin_file']] = $p; + } + + $pluginlist[$p['plugin_file']]['groups'][] = $p['category']; + } + } + } + + if (is_array($pluginlist[$pluginFile])) { + $data = $pluginlist[$pluginFile]; + if ((int)filemtime($pluginFile) == (int)$data['last_modified']) { + $data['stackable'] = serendipity_db_bool($data['stackable']); + + $plugin = $data; + return $plugin; + } + } + + $plugin =& serendipity_plugin_api::load_plugin($class_data['name'], null, $class_data['pluginPath'], $pluginFile); + + return $plugin; + } + + function &setPluginInfo(&$plugin, &$pluginFile, &$bag, &$class_data, $pluginlocation = 'local') { + global $serendipity; + + serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}pluginlist WHERE plugin_file = '" . serendipity_db_escape_string($pluginFile) . "' AND pluginlocation = '" . serendipity_db_escape_string($pluginlocation) . "'"); + + if (is_object($plugin)) { + $data = array( + 'class_name' => get_class($plugin), + 'stackable' => $bag->get('stackable'), + 'name' => $bag->get('name'), + 'description' => $bag->get('description'), + 'author' => $bag->get('author'), + 'version' => $bag->get('version'), + 'upgrade_version' => $bag->get('version'), + 'requirements' => serialize($bag->get('requirements')), + 'website' => $bag->get('website'), + 'plugin_class' => $class_data['name'], + 'pluginPath' => $class_data['pluginPath'], + 'plugin_file' => $pluginFile, + 'pluginlocation' => $pluginlocation, + 'plugintype' => $serendipity['GET']['type'], + 'last_modified' => filemtime($pluginFile) + ); + $groups = $bag->get('groups'); + } elseif (is_array($plugin)) { + $data = $plugin; + $groups = $data['groups']; + unset($data['installable']); + unset($data['true_name']); + unset($data['customURI']); + unset($data['groups']); + $data['requirements'] = serialize($data['requirements']); + } + + serendipity_db_insert('pluginlist', $data); + + serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}plugincategories WHERE class_name = '" . serendipity_db_escape_string($data['class_name']) . "'"); + foreach((array)$groups AS $group) { + if (empty($group)) { + continue; + } + + $cat = array( + 'class_name' => $data['class_name'], + 'category' => $group + ); + } + + $data['groups'] = $groups; + + return $data; + } + function update_plugin_placement($name, $placement, $order=null) { global $serendipity; Index: include/admin/plugins.inc.php =================================================================== --- include/admin/plugins.inc.php (revision 202) +++ include/admin/plugins.inc.php (working copy) @@ -13,8 +13,16 @@ include_once S9Y_INCLUDE_PATH . 'include/plugin_api.inc.php'; include_once S9Y_INCLUDE_PATH . 'include/plugin_internal.inc.php'; +function serendipity_groupname($group) { + if (defined('PLUGIN_GROUP_' . $group)) { + return constant('PLUGIN_GROUP_' . $group); + } else { + return $group; + } +} + function serendipity_pluginListSort($x, $y) { - return strnatcasecmp($x['name'] . ' - ' . $x['desc'], $y['name'] . ' - ' . $y['desc']); + return strnatcasecmp($x['name'] . ' - ' . $x['description'], $y['name'] . ' - ' . $y['description']); } function show_plugins($event_only = false) @@ -506,133 +514,147 @@

-
-
- - - - - - 0) { - $pluginstack = $foreignPlugins['pluginstack']; - $errorstack = $foreignPlugins['errorstack']; - } else { - $pluginsPerPage = 45; - $currentPage = (int)(!empty($serendipity['GET']['page']) ? $serendipity['GET']['page'] : 1); - $countStart = ($currentPage-1) * $pluginsPerPage; - $countEnd = ($currentPage) * $pluginsPerPage; + $plugins = serendipity_plugin_api::get_installed_plugins(); + $classes = serendipity_plugin_api::enum_plugin_classes(($serendipity['GET']['type'] == 'event')); + usort($classes, 'serendipity_pluginListSort'); - $plugins = serendipity_plugin_api::get_installed_plugins(); + $counter = 0; + foreach ($classes as $class_data) { - $errorstack = $pluginstack = array(); - $classes = serendipity_plugin_api::enum_plugin_classes(($serendipity['GET']['type'] == 'event')); - // TODO: With pagination of the plugins we cannot get the localized title of the plugins before sorting it. - // This means we get a weird order of the plugins. We need to find a way - // to do the sorting on the real plugin name WITHOUT loading ALL dozen plugins - usort($classes, 'serendipity_pluginListSort'); + $pluginFile = serendipity_plugin_api::probePlugin($class_data['name'], $class_data['classname'], $class_data['pluginPath']); + $plugin =& serendipity_plugin_api::getPluginInfo($pluginFile, $class_data, $serendipity['GET']['type']); - $maxPage = ceil(count($classes)/$pluginsPerPage); - $plugin_navigation = '
PluginAction
' . sprintf(PAGE_BROWSE_PLUGINS, $currentPage, $maxPage, count($classes)) . '
'; - $plugin_pages = array(); - for ($i = 1; $i <= $maxPage; $i++) { - $plugin_page = ''; - if ($i == $currentPage) { - $plugin_page .= ''; - } + if (is_object($plugin)) { + // Object is returned when a plugin could not be cached. + $bag = new serendipity_property_bag; + $plugin->introspect($bag); + $props = serendipity_plugin_api::setPluginInfo($plugin, $pluginFile, $bag, $class_data); + $counter++; + } elseif (is_array($plugin)) { + // Array is returned if a plugin could be fetched from info cache + $props = $plugin; + } else { + $props = false; + } + + if (is_array($props)) { + $props['installable'] = !($props['stackable'] === false && in_array($class_data['true_name'], $plugins)); + $props['requirements'] = unserialize($props['requirements']); - $plugin_page .= '' . $i . ''; - - if ($i == $currentPage) { - $plugin_page .= ''; - } - - $plugin_pages[] = $plugin_page; + $pluginstack[$class_data['true_name']] = $props; + } else { + // False is returned if a plugin could not be instantiated + $errorstack[] = $class_data['true_name']; } - $plugin_navigation .= implode(' | ', $plugin_pages); - - echo $plugin_navigation; + } - $counter = 0; - foreach ($classes as $class_data) { - $counter++; - - if ($counter < $countStart || $counter > $countEnd) { - continue; - } - - $plugin =& serendipity_plugin_api::load_plugin($class_data['name'], null, $class_data['pluginPath']); - if (is_object($plugin)) { - $bag = new serendipity_property_bag; - $plugin->introspect($bag); - $pluginstack[] = array('plugin_class' => $class_data['name'], - 'class_name' => get_class($plugin), - 'name' => $bag->get('name'), - 'desc' => $bag->get('description'), - 'installable' => !($bag->get('stackable') === false && in_array($class_data['name'], $plugins)), - 'author' => $bag->get('author'), - 'version' => $bag->get('version'), - 'requirements' => $bag->get('requirements'), - 'website' => $bag->get('website'), - 'pluginPath' => $class_data['pluginPath']); - } else { - $errorstack[] = $class_data['name']; - } + usort($pluginstack, 'serendipity_pluginListSort'); + $pluggroups = array(); + foreach($pluginstack AS $plugname => $plugdata) { + foreach((array)$plugdata['groups'] AS $group) { + $pluggroups[$group][] = $plugdata; } } - usort($pluginstack, 'serendipity_pluginListSort'); foreach($errorstack as $e_idx => $e_name) { echo ERROR . ': ' . $e_name . '
'; } - - foreach ($pluginstack as $plug) { - $jsLine = " onmouseout=\"document.getElementById('serendipity_plugin_". $plug['class_name'] ."').className='';\""; - $jsLine .= " onmouseover=\"document.getElementById('serendipity_plugin_". $plug['class_name'] ."').className='serendipity_admin_list_item_uneven';\""; - - $pluginInfo = $notice = array(); - if (!empty($plug['author'])) { - $pluginInfo[] = AUTHOR . ': ' . $plug['author']; +?> + + $groupstack) { + if (empty($pluggroup)) { + ?> + + + + + + + + + + + + +') ) { + $notice['requirements_failures'][] = 's9y ' . $plug['requirements']['serendipity']; + } + + if ( !empty($plug['requirements']['php']) && version_compare($plug['requirements']['php'], phpversion(), '>') ) { + $notice['requirements_failures'][] = 'PHP ' . $plug['requirements']['php']; + } + + /* Enable after Smarty 2.6.7 upgrade. + * TODO: How can we get current Smarty version here? $smarty is not created! + if ( !empty($plug['requirements']['smarty']) && version_compare($plug['requirements']['smarty'], '2.6.7', '>') ) { + $notice['requirements_failures'][] = 'Smarty: ' . $plug['requirements']['smarty']; + } + */ + + if (count($notice['requirements_failures']) > 0) { + $plug['requirements_fail'] = true; + } - if (!empty($plug['version'])) { - $pluginInfo[] = VERSION . ': ' . $plug['version']; - } - - if (!empty($plug['upgrade_version'])) { - $pluginInfo[] = sprintf(UPGRADE_TO_VERSION, $plug['upgrade_version']); - } - - if (!isset($plug['customURI'])) { - $plug['customURI'] = ''; - } - - if ( !empty($plug['requirements']['serendipity']) && version_compare($plug['requirements']['serendipity'], serendipity_getCoreVersion($serendipity['version']), '>') ) { - $notice['requirements_failures'][] = 's9y ' . $plug['requirements']['serendipity']; - } - - if ( !empty($plug['requirements']['php']) && version_compare($plug['requirements']['php'], phpversion(), '>') ) { - $notice['requirements_failures'][] = 'PHP ' . $plug['requirements']['php']; - } - - /* Enable after Smarty 2.6.7 upgrade. - * TODO: How can we get current Smarty version here? $smarty is not created! - if ( !empty($plug['requirements']['smarty']) && version_compare($plug['requirements']['smarty'], '2.6.7', '>') ) { - $notice['requirements_failures'][] = 'Smarty: ' . $plug['requirements']['smarty']; - } - */ - - if (count($notice['requirements_failures']) > 0) { - $plug['requirements_fail'] = true; - } - ?> - + - 0) { ?> + 0) { ?> - +
+
+ + + + : + +
+
PluginAction
> @@ -645,27 +667,26 @@ <?php echo UPGRADE ?> - <?php echo INSTALL ?> + <?php echo INSTALL ?>
Index: docs/NEWS =================================================================== --- docs/NEWS (revision 202) +++ docs/NEWS (working copy) @@ -92,9 +92,6 @@ that within templates you can still use the $CONST shortcut (garvinhicking) - * Pagination of plugins to install to save memory allocation if many - plugins are downloaded (garvinhicking) - * Added new event hook to the trackback sending facility so that plugins like serendipity_event_trackback (additional_plugins) can send trackbacks to Blogs without RDF-metadata (garvinhicking) Index: templates/default/admin/style.css =================================================================== --- templates/default/admin/style.css (revision 202) +++ templates/default/admin/style.css (working copy) @@ -255,3 +255,20 @@ .direction_rtl { direction: rtl; } + +.serendipity_pluginlist_header { + height: 30px; + background-color: white; + color: black; + vertical-align: middle; + padding: 10px 0px 5px 10px; +} + +.serendipity_pluginlist_section { + height: 50px; + border: 1px solid black; + background-color: #E0E0E0; + color: black; + vertical-align: middle; + padding: 10px 0px 5px 10px; +} \ No newline at end of file Index: sql/db_update_0.9-alpha3_0.9-alpha4_mysql.sql =================================================================== --- sql/db_update_0.9-alpha3_0.9-alpha4_mysql.sql (revision 0) +++ sql/db_update_0.9-alpha3_0.9-alpha4_mysql.sql (revision 0) @@ -0,0 +1,29 @@ +create table {PREFIX}plugincategories ( + class_name varchar(250) default null, + category varchar(250) default null +); + +CREATE INDEX plugincat_idx ON {PREFIX}plugincategories(class_name, category); + +create table {PREFIX}pluginlist ( + plugin_file varchar(255) NOT NULL default '', + class_name varchar(255) NOT NULL default '', + plugin_class varchar(255) NOT NULL default '', + pluginPath varchar(255) NOT NULL default '', + name varchar(255) NOT NULL default '', + description text NOT NULL, + version varchar(12) NOT NULL default '', + upgrade_version varchar(12) NOT NULL default '', + plugintype varchar(255) NOT NULL default '', + pluginlocation varchar(255) NOT NULL default '', + stackable int(1) NOT NULL default '0', + author varchar(255) NOT NULL default '', + requirements text NOT NULL, + website varchar(255) NOT NULL default '', + last_modified int(11) NOT NULL default '0' +); + +CREATE INDEX pluginlist_f_idx ON {PREFIX}pluginlist(plugin_file); +CREATE INDEX pluginlist_cn_idx ON {PREFIX}pluginlist(class_name); +CREATE INDEX pluginlist_pt_idx ON {PREFIX}pluginlist(plugintype); +CREATE INDEX pluginlist_pl_idx ON {PREFIX}pluginlist(pluginlocation); Index: sql/db.sql =================================================================== --- sql/db.sql (revision 202) +++ sql/db.sql (working copy) @@ -245,3 +245,33 @@ CREATE INDEX ple_idx ON {PREFIX}permalinks (entry_id); CREATE INDEX plt_idx ON {PREFIX}permalinks (type); CREATE INDEX plcomb_idx ON {PREFIX}permalinks (permalink, type); + +create table {PREFIX}plugincategories ( + class_name varchar(250) default null, + category varchar(250) default null +); + +CREATE INDEX plugincat_idx ON {PREFIX}plugincategories(class_name, category); + +create table {PREFIX}pluginlist ( + plugin_file varchar(255) NOT NULL default '', + class_name varchar(255) NOT NULL default '', + plugin_class varchar(255) NOT NULL default '', + pluginPath varchar(255) NOT NULL default '', + name varchar(255) NOT NULL default '', + description text NOT NULL, + version varchar(12) NOT NULL default '', + upgrade_version varchar(12) NOT NULL default '', + plugintype varchar(255) NOT NULL default '', + pluginlocation varchar(255) NOT NULL default '', + stackable int(1) NOT NULL default '0', + author varchar(255) NOT NULL default '', + requirements text NOT NULL, + website varchar(255) NOT NULL default '', + last_modified int(11) NOT NULL default '0' +); + +CREATE INDEX pluginlist_f_idx ON {PREFIX}pluginlist(plugin_file); +CREATE INDEX pluginlist_cn_idx ON {PREFIX}pluginlist(class_name); +CREATE INDEX pluginlist_pt_idx ON {PREFIX}pluginlist(plugintype); +CREATE INDEX pluginlist_pl_idx ON {PREFIX}pluginlist(pluginlocation);