if the plugin uses the sql_table() method to get table names * 'HelpPage' -> if the plugin provides a helppage * 'SqlApi' -> if the plugin uses the complete sql_* or DB::* api (must also require nucleuscms 3.5) */ public function supportsFeature($feature) { return 0; } /** * Report a list of plugin that is required to final public function * * @returns an array of names of plugin, an empty array indicates no dependency */ public function getPluginDep() { return array(); } // these helper final public functions should not be redefined in your plugin /** * Creates a new option for this plugin * * @param name * A string uniquely identifying your option. (max. length is 20 characters) * @param description * A description that will show up in the nucleus admin area (max. length: 255 characters) * @param type * Either 'text', 'yesno' or 'password' * This info is used when showing 'edit plugin options' screens * @param value * Initial value for the option (max. value length is 128 characters) */ final public function createOption($name, $desc, $type, $defValue = '', $typeExtras = '') { return $this->create_option('global', $name, $desc, $type, $defValue, $typeExtras); } final public function createBlogOption($name, $desc, $type, $defValue = '', $typeExtras = '') { return $this->create_option('blog', $name, $desc, $type, $defValue, $typeExtras); } final public function createMemberOption($name, $desc, $type, $defValue = '', $typeExtras = '') { return $this->create_option('member', $name, $desc, $type, $defValue, $typeExtras); } final public function createCategoryOption($name, $desc, $type, $defValue = '', $typeExtras = '') { return $this->create_option('category', $name, $desc, $type, $defValue, $typeExtras); } final public function createItemOption($name, $desc, $type, $defValue = '', $typeExtras = '') { return $this->create_option('item', $name, $desc, $type, $defValue, $typeExtras); } /** * Removes the option from the database * * Note: Options get erased automatically on plugin uninstall */ final public function deleteOption($name) { return $this->delete_option('global', $name); } final public function deleteBlogOption($name) { return $this->delete_option('blog', $name); } final public function deleteMemberOption($name) { return $this->delete_option('member', $name); } final public function deleteCategoryOption($name) { return $this->delete_option('category', $name); } final public function deleteItemOption($name) { return $this->delete_option('item', $name); } /** * Sets the value of an option to something new */ final public function setOption($name, $value) { return $this->set_option('global', 0, $name, $value); } final public function setBlogOption($blogid, $name, $value) { return $this->set_option('blog', $blogid, $name, $value); } final public function setMemberOption($memberid, $name, $value) { return $this->set_option('member', $memberid, $name, $value); } final public function setCategoryOption($catid, $name, $value) { return $this->set_option('category', $catid, $name, $value); } final public function setItemOption($itemid, $name, $value) { return $this->set_option('item', $itemid, $name, $value); } /** * Retrieves the current value for an option */ final public function getOption($name) { // only request the options the very first time. On subsequent requests // the static collection is used to save SQL queries. if ( $this->plugin_options == 0 ) { $this->plugin_options = array(); $query = "SELECT d.oname as name, o.ovalue as value FROM %s o, %s d WHERE d.opid=%d AND d.oid=o.oid;"; $query = sprintf($query, sql_table('plugin_option'), sql_table('plugin_option_desc'), (integer) $this->plugid); $result = DB::getResult($query); foreach ( $result as $row ) { $this->plugin_options[strtolower($row['name'])] = $row['value']; } } if ( isset($this->plugin_options[strtolower($name)]) ) { return $this->plugin_options[strtolower($name)]; } else { return $this->get_option('global', 0, $name); } } final public function getBlogOption($blogid, $name) { return $this->get_option('blog', $blogid, $name); } final public function getMemberOption($memberid, $name) { return $this->get_option('member', $memberid, $name); } final public function getCategoryOption($catid, $name) { return $this->get_option('category', $catid, $name); } final public function getItemOption($itemid, $name) { return $this->get_option('item', $itemid, $name); } /** * Retrieves an associative array with the option value for each * context id */ final public function getAllBlogOptions($name) { return $this->get_all_options('blog', $name); } final public function getAllMemberOptions($name) { return $this->get_all_options('member', $name); } final public function getAllCategoryOptions($name) { return $this->get_all_options('category', $name); } final public function getAllItemOptions($name) { return $this->get_all_options('item', $name); } /** * Retrieves an indexed array with the top (or bottom) of an option * (delegates to getOptionTop()) */ final public function getBlogOptionTop($name, $amount = 10, $sort = 'desc') { return $this->get_option_top('blog', $name, $amount, $sort); } final public function getMemberOptionTop($name, $amount = 10, $sort = 'desc') { return $this->get_option_top('member', $name, $amount, $sort); } final public function getCategoryOptionTop($name, $amount = 10, $sort = 'desc') { return $this->get_option_top('category', $name, $amount, $sort); } final public function getItemOptionTop($name, $amount = 10, $sort = 'desc') { return $this->get_option_top('item', $name, $amount, $sort); } /** * NucleusPlugin::getID() * get id for this plugin * * @access public * @param void * @return integer this plugid id */ final public function getID() { return (integer) $this->plugid; } /** * NucleusPlugin::setID() * set favorite id for this plugin * * @access public * @param integer $plugid favorite id for plugin * @return void */ final public function setID($plugid) { $this->plugid = (integer) $plugid; return; } /** * Returns the URL of the admin area for this plugin (in case there's * no such area, the returned information is invalid) * * public */ final public function getAdminURL() { global $CONF; return $CONF['PluginURL'] . $this->getShortName() . '/'; } /** * Returns the directory where the admin directory is located and * where the plugin can maintain his extra files * * public */ final public function getDirectory() { global $DIR_PLUGINS; return $DIR_PLUGINS . $this->getShortName() . '/'; } /** * Derives the short name for the plugin from the classname (all * lowercase) * * public */ final public function getShortName() { return str_replace('np_','',strtolower(get_class($this))); } /** * Clears the option value cache which saves the option values during * the plugin execution. This function is usefull if the options has * changed during the plugin execution (especially in association with * the PrePluginOptionsUpdate and the PostPluginOptionsUpdate events) * * public **/ final public function clearOptionValueCache() { $this->option_values = array(); $this->plugin_options = 0; return; } // internal functions of the class starts here protected $option_values; // oid_contextid => value protected $option_info; // context_name => array('oid' => ..., 'default' => ...) protected $plugin_options; // see getOption() protected $plugid; // plugin id /** * Class constructor: Initializes some internal data */ public function __construct() { $this->option_values = array(); // oid_contextid => value $this->option_info = array(); // context_name => array('oid' => ..., 'default' => ...) $this->plugin_options = 0; } /** * Retrieves an array of the top (or bottom) of an option from a plugin. * @author TeRanEX * @param string $context the context for the option: item, blog, member,... * @param string $name the name of the option * @param int $amount how many rows must be returned * @param string $sort desc or asc * @return array array with both values and contextid's * @access private */ final protected function get_option_top($context, $name, $amount = 10, $sort = 'desc') { if ( ($sort != 'desc') && ($sort != 'asc') ) { $sort= 'desc'; } $oid = $this->get_option_id($context, $name); // retrieve the data and return $query = "SELECT otype, oextra FROM %s WHERE oid = %d;"; $query = sprintf($query, sql_table('plugin_option_desc'), $oid); $row = DB::getRow($query); if ( ($this->optionCanBeNumeric($row['otype'])) && ($row['oextra'] == 'number' ) ) { $orderby = 'CAST(ovalue AS SIGNED)'; } else { $orderby = 'ovalue'; } $query = "SELECT ovalue value, ocontextid id FROM %s WHERE oid = %d ORDER BY %s %s LIMIT 0,%d;"; $query = sprintf($query, sql_table('plugin_option'), $oid, $orderby, $sort, (integer) $amount); $result = DB::getResult($query); // create the array $i = 0; $top = array(); foreach( $result as $row ) { $top[$i++] = $row; } // return the array (duh!) return $top; } /** * Creates an option in the database table plugin_option_desc * * private */ final protected function create_option($context, $name, $desc, $type, $defValue, $typeExtras = '') { // create in plugin_option_desc $query = 'INSERT INTO ' . sql_table('plugin_option_desc') .' (opid, oname, ocontext, odesc, otype, odef, oextra)' .' VALUES ('.intval($this->plugid) .', '.DB::quoteValue($name) .', '.DB::quoteValue($context) .', '.DB::quoteValue($desc) .', '.DB::quoteValue($type) .', '.DB::quoteValue($defValue) .', '.DB::quoteValue($typeExtras).')'; DB::execute($query); $oid = DB::getInsertId(); $key = $context . '_' . $name; $this->option_info[$key] = array('oid' => $oid, 'default' => $defValue); return 1; } /** * Deletes an option from the database tables * plugin_option and plugin_option_desc * * private */ final protected function delete_option($context, $name) { $oid = $this->get_option_id($context, $name); if ( !$oid ) { return 0; // no such option } // delete all things from plugin_option $query = "DELETE FROM %s WHERE oid=%d;"; $query = sprintf($query, sql_table('plugin_option'), (integer) $oid); DB::execute($query); // delete entry from plugin_option_desc $query = "DELETE FROM %s WHERE oid=%d;"; $query = sprintf($query, sql_table('plugin_option_desc'), $oid); DB::execute($query); // clear from cache unset($this->option_info["{$context}_{$name}"]); $this->option_values = array(); return 1; } /** * Update an option in the database table plugin_option * * returns: 1 on success, 0 on failure * private */ final protected function set_option($context, $contextid, $name, $value) { global $manager; $oid = $this->get_option_id($context, $name); if ( !$oid ) { return 0; } // check if context id exists switch ( $context ) { case 'member': if ( !Member::existsID($contextid) ) { return 0; } break; case 'blog': if ( !$manager->existsBlogID($contextid) ) { return 0; } break; case 'category': if ( !$manager->existsCategory($contextid) ) { return 0; } break; case 'item': if ( !$manager->existsItem($contextid, true, true) ) { return 0; } break; case 'global': if ( $contextid != 0 ) { return 0; } break; } // update plugin_option $query = "DELETE FROM %s WHERE oid=%d and ocontextid=%d;"; $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid); DB::execute($query); $query = "INSERT INTO %s (ovalue, oid, ocontextid) VALUES (%s, %d, %d);"; $query = sprintf($query, sql_table('plugin_option'), DB::quoteValue($value), $oid, $contextid); DB::execute($query); // update cache $this->option_values["{$oid}_{$contextid}"] = $value; if ( $context == 'global' ) { $this->plugin_options[strtolower($name)] = $value; } return 1; } /** * Get an option from Cache or database * - if not in the option Cache read it from the database * - if not in the database write default values into the database * * private */ final protected function get_option($context, $contextid, $name) { $oid = $this->get_option_id($context, $name); if ( !$oid ) { return ''; } $key = "{$oid}_{$contextid}"; if ( isset($this->option_values[$key]) ) { return $this->option_values[$key]; } // get from DB $query = "SELECT ovalue FROM %s WHERE oid=%d and ocontextid=%d;"; $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid); $result = DB::getResult($query); if ( !$result || ($result->rowCount() == 0) ) { // fill DB with default value $this->option_values[$key] = $this->get_default_value($context, $name); $query = "INSERT INTO %s (oid, ocontextid, ovalue) VALUES (%d, %d, %s);"; $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid, DB::quoteValue($this->option_values[$key])); DB::execute($query); } else { $row = $result->fetch(); $this->option_values[$key] = $row['ovalue']; } return $this->option_values[$key]; } /** * Returns assoc array with all values for a given option * (one option per possible context id) * * private */ final protected function get_all_options($context, $name) { $oid = $this->get_option_id($context, $name); if ( !$oid ) { return array(); } $default_value = $this->get_default_value($context, $name); $options = array(); $query = "SELECT %s as contextid FROM %s;"; switch ( $context ) { case 'blog': $query = sprintf($query, 'bnumber', sql_table('blog')); break; case 'category': $query = sprintf($query, 'catid', sql_table('category')); break; case 'member': $query = sprintf($query, 'mnumber', sql_table('member')); break; case 'item': $query = sprintf($query, 'inumber', sql_table('item')); break; } $result = DB::getResult($query); if ( $result ) { foreach ( $result as $row ) { $options[$row['contextid']] = $default_value; } } $query = "SELECT ocontextid, ovalue FROM %s WHERE oid=%d;"; $query = sprintf($query, sql_table('plugin_option'), $oid); $result = DB::getResult($query); foreach ( $result as $row ) { $options[$row['ocontextid']] = $row['ovalue']; } return $options; } /** * NucleusPlugin::get_option_id * * Gets the 'option identifier' that corresponds to a given option name. * When this method is called for the first time, all the OIDs for the plugin * are loaded into memory, to avoid re-doing the same query all over. * * @param string $context option context * @param string $name plugin name * @return integer option id */ final protected function get_option_id($context, $name) { $key = "{$context}_{$name}"; if ( array_key_exists($key, $this->option_info) && array_key_exists('oid', $this->option_info[$key]) ) { return $this->option_info[$key]['oid']; } // load all OIDs for this plugin from the database $this->option_info = array(); $query = "SELECT oid, oname, ocontext, odef FROM %s WHERE opid=%d;"; $query = sprintf($query, sql_table('plugin_option_desc'), $this->plugid); $result = DB::getResult($query); foreach ( $result as $row ) { $k = $row['ocontext'] . '_' . $row['oname']; $this->option_info[$k] = array('oid' => $row['oid'], 'default' => $row['odef']); } $result->closeCursor(); return $this->option_info[$key]['oid']; } final protected function get_default_value($context, $name) { $key = $context . '_' . $name; if ( array_key_exists($key, $this->option_info) && array_key_exists('default', $this->option_info[$key]) ) { return $this->option_info[$key]['default']; } return; } /** * NucleusPlugin::delete_option_values() * Deletes all option values for a given context and contextid * (used when e.g. a blog, member or category is deleted) * *@static *@param String $context global/blog/category/item/member *@param Integer $contextid ID *@return Void */ static public function delete_option_values($context, $contextid) { // delete all associated plugin options $aOIDs = array(); // find ids $query = "SELECT oid FROM %s WHERE ocontext=%s;"; $query = sprintf($query, sql_table('plugin_option_desc'), DB::quoteValue($context)); $result = DB::getResult($query); foreach ( $result as $row ) { array_push($aOIDs, $row['oid']); } $result->closeCursor(); // delete those options. go go go if ( count($aOIDs) > 0 ) { $query = "DELETE FROM %s WHERE oid in (%s) and ocontextid=%d;"; $query = sprintf($query, sql_table('plugin_option'), implode(',',$aOIDs), (integer) $contextid); DB::execute($query); } return; } /** * NucleusPlugin::getOptionMeta() * splits the option's typeextra field (at ;'s) to split the meta collection * * @static * @param string $typeExtra the value of the typeExtra field of an option * @return array array of the meta-key/value-pairs */ static public function getOptionMeta($typeExtra) { $meta = array(); /* 1. if $typeExtra includes delimiter ';', split it to tokens */ $tokens = preg_split('#;#', $typeExtra); /* * 2. if each of tokens includes "=", it consists of key => value * else it's 'select' option */ foreach ( $tokens as $token ) { $matches = array(); if ( preg_match("#^([^=]+)?=([^=]+)?$#", $token, $matches) ) { $meta[$matches[1]] = $matches[2]; } else { $meta['select'] = $token; } } return $meta; } /** * NucleusPlugin::getOptionSelectValues() * filters the selectlists out of the meta collection * * @static * @param string $typeExtra the value of the typeExtra field of an option * @return string the selectlist */ static public function getOptionSelectValues($typeExtra) { $meta = NucleusPlugin::getOptionMeta($typeExtra); if ( array_key_exists('select', $meta) ) { return $meta['select']; } return; } /** * checks if the eventlist in the database is up-to-date * @return bool if it is up-to-date it return true, else false * @author TeRanEX */ public function subscribtionListIsUptodate() { $res = DB::getResult('SELECT event FROM '.sql_table('plugin_event').' WHERE pid = '.$this->plugid); $ev = array(); foreach ( $res as $row ) { array_push($ev, $row['event']); } if ( count($ev) != count($this->getEventList()) ) { return false; } $d = array_diff($ev, $this->getEventList()); if ( count($d) > 0 ) { // there are differences so the db is not up-to-date return false; } return true; } /** * NucleusPlugin::apply_plugin_options() * Update its entry in database table * * @static * @param $options: array ( 'oid' => array( 'contextid' => 'value')) * (taken from request using requestVar()) * @param $new_contextid: integer (accepts a contextid when it is for a new * contextid there was no id available at the moment of writing the * formcontrols into the page (by ex: itemOptions for new item) * @return void */ static public function apply_plugin_options(&$options, $new_contextid = 0) { global $manager; if ( !is_array($options) ) { return; } foreach ( $options as $oid => $values ) { // get option type info $query = "SELECT opid, oname, ocontext, otype, oextra, odef FROM %s WHERE oid=%d;"; $query = sprintf($query, sql_table('plugin_option_desc'), (integer) $oid); $result = DB::getRow($query); if ( $result ) { foreach ( $values as $id => $value ) { // decide wether we are using the contextid of newContextid if ( $new_contextid != 0 ) { $contextid = $new_contextid; } else { $contextid = $id; } // retreive any metadata $meta = NucleusPlugin::getOptionMeta($result['oextra']); // if the option is readonly or hidden it may not be saved if ( array_key_exists('access', $meta) && in_array($meta['access'], array('readonly', 'hidden')) ) { return; } // value comes from request $value = undoMagic($value); /* validation the value according to its type */ switch ( $result['otype'] ) { case 'yesno': if ( ($value != 'yes') && ($value != 'no') ) { $value = 'no'; } break; case 'text': case 'select': if ( array_key_exists('datatype', $meta) && ($meta['datatype'] == 'numerical') && ($value != (integer) $value) ) { $value = (integer) $result['odef']; } break; case 'password': case 'textarea': default: break; } /* * trigger event PrePluginOptionsUpdate to give the plugin the * possibility to change/validate the new value for the option */ $data = array( 'context' => $result['ocontext'], 'plugid' => $result['opid'], 'optionname' => $result['oname'], 'contextid' => $contextid, 'value' => &$value); $manager->notify('PrePluginOptionsUpdate', $data); // delete and insert its fields of table in database $query = "DELETE FROM %s WHERE oid=%d AND ocontextid=%d;"; $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid); DB::execute($query); $query = "INSERT INTO %s (oid, ocontextid, ovalue) VALUES (%d, %d, %s);"; $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid, DB::quoteValue($value)); DB::execute($query); // clear option value cache if the plugin object is already loaded $plugin=& $manager->pidLoaded($result['opid']); if ( $plugin ) { $plugin->clearOptionValueCache(); } continue; } } continue; } return; } }