add('name', PLUGIN_CATEGORYTEMPLATES_NAME); $propbag->add('description', PLUGIN_CATEGORYTEMPLATES_DESC); $propbag->add('stackable', false); $propbag->add('author', 'Garvin Hicking, Judebert'); $propbag->add('version', '0.35'); $propbag->add('requirements', array( 'serendipity' => '0.9', 'php' => '4.1.0' )); $propbag->add('event_hooks', array( 'genpage' => true, 'external_plugin' => true, 'backend_category_addNew' => true, 'backend_category_update' => true, 'backend_category_delete' => true, 'backend_category_showForm' => true, 'frontend_fetchentries' => true, 'frontend_fetchentry' => true, 'backend_sidebar_entries_event_display_cattemplate' => true, // 'frontend_configure' => true )); $propbag->add('configuration', array('pass', 'sort_order', 'fixcat', 'cat_precedence')); $propbag->add('groups', array('FRONTEND_FULL_MODS', 'FRONTEND_VIEWS', 'BACKEND_TEMPLATES')); } function introspect_config_item($name, &$propbag) { switch($name) { case 'pass': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_CATEGORYTEMPLATES_PASS); $propbag->add('description', PLUGIN_CATEGORYTEMPLATES_PASS_DESC); $propbag->add('default', 'false'); break; case 'sort_order': $propbag->add('type', 'string'); $propbag->add('name', USE_DEFAULT . ': ' . SORT_ORDER); $propbag->add('description', ''); $propbag->add('default', 'timestamp DESC'); break; case 'fixcat': $propbag->add('type', 'radio'); $propbag->add('name', PLUGIN_CATEGORYTEMPLATES_FIXENTRY); $propbag->add('description', PLUGIN_CATEGORYTEMPLATES_FIXENTRY_DESC); $propbag->add('radio', array( 'value' => array('true', 'false', 'hard'), 'desc' => array(YES, NO, FORCE) )); $propbag->add('default', 'false'); break; case 'cat_precedence': $propbag->add('type', 'sequence'); $propbag->add('name', PLUGIN_CATEGORYTEMPLATES_CATPRECEDENCE); $propbag->add('description', PLUGIN_CATEGORYTEMPLATES_CATPRECEDENCE_DESC); $tcats = $this->getTemplatizedCats(); $values = array(); if (is_array($tcats)) { foreach($tcats AS $cat) { $values[$cat['categoryid']] = array('display' => $cat['category_name']); } } else { $values = array(PLUGIN_CATEGORYTEMPLATES_NO_CUSTOMIZED_CATEGORIES); } $propbag->add('values', $values); // People who already had custom categories, but don't have // the sequence widget, will not save this value. So entries // won't ever use custom templates. To duplicate the original // without-sequence-widget behavior, we'll have to do some // magic when the 'cat_precedence' is retrieved. // If you want to set a default here: // Note that get_config() will cause HTTP error 500 the first // time the plugin is installed, unless we provide a default } return true; } /** * Retrieves a list of IDs of all categories that have some customization enabled. * @return array A list of category IDs, or false if no categories are customized. */ function getTemplatizedCats() { global $serendipity; // Find all the categories that have custom templates $query = "SELECT c.categoryid, c.category_name, c.category_icon FROM {$serendipity['dbPrefix']}category AS c INNER JOIN {$serendipity['dbPrefix']}categorytemplates AS t ON t.categoryid = c.categoryid WHERE t.template != '' ORDER BY c.category_name ASC"; $dbcids = serendipity_db_query($query); if (!is_array($dbcids)) { // It's the value "1", for "success", or something $dbcids = false; } return $dbcids; //--TODO: Maybe find all the ones with custom sort orders and other display alterations, too } /** * Updates database to version-appropriate schema * @param The current version number of the database (could be empty) * @return true */ function checkScheme($ver) { global $serendipity; if ($ver == 3) { $q = "ALTER TABLE {$serendipity['dbPrefix']}categorytemplates ADD COLUMN hide_rss varchar(4) default false"; $sql = serendipity_db_schema_import($q); $this->set_config('dbversion', CATEGORYTEMPLATE_DB_VERSION); } elseif ($ver == 2) { $q = "ALTER TABLE {$serendipity['dbPrefix']}categorytemplates ADD COLUMN hide_rss varchar(4) default false"; $sql = serendipity_db_schema_import($q); $q = "ALTER TABLE {$serendipity['dbPrefix']}categorytemplates ADD COLUMN sort_order varchar(255)"; $sql = serendipity_db_schema_import($q); $this->set_config('dbversion', CATEGORYTEMPLATE_DB_VERSION); } elseif ($ver == 1) { $q = "ALTER TABLE {$serendipity['dbPrefix']}categorytemplates ADD COLUMN hide_rss tinyint(1) default false"; $sql = serendipity_db_schema_import($q); $q = "ALTER TABLE {$serendipity['dbPrefix']}categorytemplates ADD COLUMN sort_order varchar(255)"; $sql = serendipity_db_schema_import($q); $q = "ALTER TABLE {$serendipity['dbPrefix']}categorytemplates ADD COLUMN pass varchar(255) default null"; $sql = serendipity_db_schema_import($q); $this->set_config('dbversion', CATEGORYTEMPLATE_DB_VERSION); } elseif ($ver != CATEGORYTEMPLATE_DB_VERSION) { $q = "CREATE TABLE {$serendipity['dbPrefix']}categorytemplates ( categoryid int(11) default null, template varchar(255) default null, fetchlimit int(4) default null, futureentries int(4) default null, lang varchar(255) default null, pass varchar(255) default null, sort_order varchar(255), hide_rss varchar(4) default null )"; $sql = serendipity_db_schema_import($q); $q = "CREATE INDEX ctcid ON {$serendipity['dbPrefix']}categorytemplates (categoryid);"; $sql = serendipity_db_schema_import($q); $this->set_config('dbversion', CATEGORYTEMPLATE_DB_VERSION); } return true; } /** * Returns the most appropriate category ID for the current entry. * Only called from genpage hook. * @global array $serendipity Determines the current entry from HTTP variables * @return int|string Category ID if custom template defined or category * view, otherwise 'default' */ function getID() { global $serendipity; // If category view, just return the current category ID if ($serendipity['GET']['category'] && !isset($serendipity['GET']['id'])) { return (int)$serendipity['GET']['category']; } // If entry view, determine the best category ID for custom templating if ($serendipity['GET']['id']) { // Find all the category IDs that have custom templates $cidstr = $this->get_config('cat_precedence', false); if ($cidstr === false) { // No precedence set: default to old, alphabetical precedence. $tcats = $this->getTemplatizedCats(); $cids = array(); if (is_array($tcats)) { foreach($tcats AS $cat) { $cids[] = $cat['categoryid']; } } } else { if ($cidstr) { $cids = explode(',', $cidstr); } else { // Possibly it's set, but no categories, therefore empty $cids = array(); } } // Get all the categories' IDs belonging to this entry $entrycats = serendipity_fetchEntryCategories($serendipity['GET']['id']); $entrycids = array(); foreach ($entrycats AS $catdata) { $entrycids[] = $catdata['categoryid']; } // Return the first customized template in the entry's categories // Could try array_intersect(), but will it keep order? foreach ($cids AS $idx => $candidate) { if (in_array($candidate, $entrycids)) { return $candidate; } }// End if we know of any customized categories // If set to force, ALWAYS set the category to a forced category. if ((string)$this->get_config('fixcat') === 'hard') { return $entrycids[0]; } }// End if entry return 'default'; } /** * Wrapper for fetchProp() returning name of custom template for the * given category ID, if defined, with default. * @param int cid The category ID to lookup * @param string fallback The default template name * @return string The name of the template to be used */ function fetchTemplate($cid, $fallback) { $this->usesDefaultTemplate = true; if ($cid === false || $cid == 'default') { return $fallback; } else { $val = $this->fetchProp($cid, 'template'); if (!empty($val)) { $this->usesDefaultTemplate = false; return $val; } } return $fallback; } /** * Wrapper for fetchProp() returning the limit of entries to fetch * for this category ID, with default. * @param int cid The category ID to lookup * @param int fallback The default number of entries to fetch * @return int The max number of entries to be fetched */ function fetchLimit($cid, $fallback) { if ($cid == false || $cid == 'default' || $cid == 0) { return $fallback; } else { $val = $this->fetchProp($cid, 'fetchlimit'); if (!empty($val)) { return $val; } } return $fallback; } /** * Wrapper for fetchProp() returning the language for the category ID, * with default. * @param int cid The category ID to lookup * @param string fallback The default language to use * @return string The language to be used */ function fetchLang($cid, $fallback) { if ($cid === false || $cid == 'default') { return $fallback; } else { $val = $this->fetchProp($cid, 'lang'); if (!empty($val)) { return $val; } } return $fallback; } /** * Wrapper for fetchProp() returning whether to display entries with * dates in the future for this category ID, with default. * @param int cid The category ID to lookup * @param bool fallback The default whether to display entries from the future * @return bool Whether to display entries from the future */ function fetchFuture($cid, $fallback) { if ($cid === false || $cid == 'default' || $cid == 0) { return $fallback; } else { $val = $this->fetchProp($cid, 'futureentries'); if ($val == 1) { return false; } elseif ($val == 2) { return true; } } return $fallback; } /** * Wrapper for fetchProp() returning the entry sort order for this * category ID, with default. * @param int cid The category ID to lookup * @param string fallback The default database ordering string * @return string The database ordering string to use (i.e, 'date ASC') */ function fetchSortOrder($cid, $fallback) { if ($cid === false || $cid == 'default' || $cid == 0) { return $fallback; } else { $val = $this->fetchProp($cid, 'sort_order'); if ($val == 'timestamp DESC' /*|| $fallback == 'timestamp DESC'*/) { return false; } if (!empty($val)) { return $val; } } return $fallback; } /** * Fetches the requested property of the given category ID, retrieving * from cache where possible, querying database and populating cache * with all properties otherwise. * @param int cide The category ID to be queried * @param string key optional The property to be fetched (default 'template') * @return mixed The value of the requested property */ function fetchProp($cid, $key = 'template') { global $serendipity; static $cache = array(); if (isset($cache[$cid][$key])) { return $cache[$cid][$key]; } $props = serendipity_db_query("SELECT * FROM {$serendipity['dbPrefix']}categorytemplates WHERE categoryid = " . (int)$cid . " LIMIT 1"); if (is_array($props)) { $cache[$cid] = $props[0]; return $cache[$cid][$key]; } return false; } /** * Sets or deletes properties for this category from the database. * @param int cid The category ID to use * @param array val optional An array associating SQL column names with * their desired values (default false) * @param bool deleteOnly optional Whether to skip inserting new values (default false) * @return true */ function setProps($cid, $val = false, $deleteOnly = false) { global $serendipity; serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}categorytemplates WHERE categoryid = " . (int)$cid); if ($deleteOnly === false) { $db = serendipity_db_insert('categorytemplates', $val, 'execute'); return $db; } return true; } function template_options($template, $catid) { global $serendipity, $template_config; if (!serendipity_checkPermission('adminTemplates')) { return; } $template = str_replace('.', '', urldecode($template)); $catid = (int)$catid; $tpl_path = $serendipity['serendipityPath'] . $serendipity['templatePath'] . $template; if (!is_dir($tpl_path)) { return false; } $serendipity['GET']['adminModule'] == 'templates'; $serendipity['smarty_vars']['template_option'] = $template . '_' . $catid; echo '
' . STYLE_OPTIONS_NONE . '
'; serendipity_plugin_api::hook_event('backend_templates_configuration_none', $template_config); } } /** * The meat of the plugin, called for each registered hook. * @param string event The name of the hook being called * @param mixed bag An array of configuration options for this plugin * @param mixed eventData An array containing parameters for the hook * @param mixed addData Additional hook data, if any * @return true */ function event_hook($event, &$bag, &$eventData, $addData = null) { global $serendipity; $hooks = &$bag->get('event_hooks'); if (isset($hooks[$event])) { // Update the database on first run if changed. $ver = $this->get_config('dbversion', 0); if ($ver != CATEGORYTEMPLATE_DB_VERSION) { $this->checkScheme($ver); } switch($event) { // When changing category options, display the new extended // options, such as template, future entries, limit, and order case 'backend_category_showForm': // The $eventData is the category ID $clang = $this->fetchLang($eventData, ''); $cfuture = $this->fetchFuture($eventData, ''); $styles = serendipity_fetchTemplates(); $template = $this->fetchTemplate($eventData, ''); $hide_rss = serendipity_db_query("SELECT hide_rss FROM {$serendipity['dbPrefix']}categorytemplates AS t WHERE t.categoryid = {$eventData}", true); if ($hide_rss !== false) { $hide_rss = serendipity_db_bool($hide_rss['hide_rss']); } ?>