3 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
4 * Copyright (C) 2002-2009 The Nucleus Group
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 * (see nucleus/documentation/index.html#license for more info)
13 * This is an (abstract) class of which all Nucleus Plugins must inherit
15 * for more information on plugins and how to write your own, see the
16 * plugins.html file that is included with the Nucleus documenation
18 * @license http://nucleuscms.org/license.txt GNU General Public License
19 * @copyright Copyright (C) 2002-2009 The Nucleus Group
20 * @version $Id: PLUGIN.php 1866 2012-05-20 13:21:55Z sakamocchi $
22 abstract class NucleusPlugin
24 // these public functions should to be redefined in your plugin
25 public function getName()
30 public function getAuthor()
35 public function getURL()
40 public function getVersion()
45 public function getDescription()
50 // these final public function _may_ be redefined in your plugin
52 public function getMinNucleusVersion()
57 public function getMinNucleusPatchLevel()
62 public function getEventList()
67 public function getTableList()
72 public function hasAdminArea()
77 public function install()
82 public function unInstall()
87 public function init()
92 public function doSkinVar($skinType)
97 public function doTemplateVar(&$item)
99 $args = func_get_args();
101 array_unshift($args, 'template');
102 call_user_func_array(array($this, 'doSkinVar'), $args);
106 public function doTemplateCommentsVar(&$item, &$comment)
108 $args = func_get_args();
111 array_unshift($args, 'template');
112 call_user_func_array(array($this, 'doSkinVar'), $args);
116 public function doAction($type)
118 return _ERROR_PLUGIN_NOSUCHACTION;
121 public function doIf($key,$value)
126 public function doItemVar (&$item)
132 * Checks if a plugin supports a certain feature.
134 * @returns 1 if the feature is reported, 0 if not
136 * Name of the feature. See plugin documentation for more info
137 * 'SqlTablePrefix' -> if the plugin uses the sql_table() method to get table names
138 * 'HelpPage' -> if the plugin provides a helppage
139 * 'SqlApi' -> if the plugin uses the complete sql_* or DB::* api (must also require nucleuscms 3.5)
141 public function supportsFeature($feature)
147 * Report a list of plugin that is required to final public function
149 * @returns an array of names of plugin, an empty array indicates no dependency
151 public function getPluginDep()
156 // these helper final public functions should not be redefined in your plugin
159 * Creates a new option for this plugin
162 * A string uniquely identifying your option. (max. length is 20 characters)
164 * A description that will show up in the nucleus admin area (max. length: 255 characters)
166 * Either 'text', 'yesno' or 'password'
167 * This info is used when showing 'edit plugin options' screens
169 * Initial value for the option (max. value length is 128 characters)
171 final public function createOption($name, $desc, $type, $defValue = '', $typeExtras = '')
173 return $this->create_option('global', $name, $desc, $type, $defValue, $typeExtras);
176 final public function createBlogOption($name, $desc, $type, $defValue = '', $typeExtras = '')
178 return $this->create_option('blog', $name, $desc, $type, $defValue, $typeExtras);
181 final public function createMemberOption($name, $desc, $type, $defValue = '', $typeExtras = '')
183 return $this->create_option('member', $name, $desc, $type, $defValue, $typeExtras);
186 final public function createCategoryOption($name, $desc, $type, $defValue = '', $typeExtras = '')
188 return $this->create_option('category', $name, $desc, $type, $defValue, $typeExtras);
191 final public function createItemOption($name, $desc, $type, $defValue = '', $typeExtras = '')
193 return $this->create_option('item', $name, $desc, $type, $defValue, $typeExtras);
197 * Removes the option from the database
199 * Note: Options get erased automatically on plugin uninstall
201 final public function deleteOption($name)
203 return $this->delete_option('global', $name);
206 final public function deleteBlogOption($name)
208 return $this->delete_option('blog', $name);
211 final public function deleteMemberOption($name)
213 return $this->delete_option('member', $name);
216 final public function deleteCategoryOption($name)
218 return $this->delete_option('category', $name);
221 final public function deleteItemOption($name)
223 return $this->delete_option('item', $name);
227 * Sets the value of an option to something new
229 final public function setOption($name, $value)
231 return $this->set_option('global', 0, $name, $value);
234 final public function setBlogOption($blogid, $name, $value)
236 return $this->set_option('blog', $blogid, $name, $value);
239 final public function setMemberOption($memberid, $name, $value)
241 return $this->set_option('member', $memberid, $name, $value);
244 final public function setCategoryOption($catid, $name, $value)
246 return $this->set_option('category', $catid, $name, $value);
249 final public function setItemOption($itemid, $name, $value) {
250 return $this->set_option('item', $itemid, $name, $value);
254 * Retrieves the current value for an option
256 final public function getOption($name)
258 // only request the options the very first time. On subsequent requests
259 // the static collection is used to save SQL queries.
260 if ( $this->plugin_options == 0 )
262 $this->plugin_options = array();
264 $query = "SELECT d.oname as name, o.ovalue as value FROM %s o, %s d WHERE d.opid=%d AND d.oid=o.oid;";
265 $query = sprintf($query, sql_table('plugin_option'), sql_table('plugin_option_desc'), (integer) $this->plugid);
266 $result = DB::getResult($query);
267 foreach ( $result as $row )
269 $this->plugin_options[strtolower($row['name'])] = $row['value'];
272 if ( isset($this->plugin_options[strtolower($name)]) )
274 return $this->plugin_options[strtolower($name)];
278 return $this->get_option('global', 0, $name);
282 final public function getBlogOption($blogid, $name)
284 return $this->get_option('blog', $blogid, $name);
287 final public function getMemberOption($memberid, $name)
289 return $this->get_option('member', $memberid, $name);
292 final public function getCategoryOption($catid, $name)
294 return $this->get_option('category', $catid, $name);
297 final public function getItemOption($itemid, $name)
299 return $this->get_option('item', $itemid, $name);
303 * Retrieves an associative array with the option value for each
306 final public function getAllBlogOptions($name)
308 return $this->get_all_options('blog', $name);
311 final public function getAllMemberOptions($name)
313 return $this->get_all_options('member', $name);
316 final public function getAllCategoryOptions($name)
318 return $this->get_all_options('category', $name);
321 final public function getAllItemOptions($name)
323 return $this->get_all_options('item', $name);
327 * Retrieves an indexed array with the top (or bottom) of an option
328 * (delegates to getOptionTop())
330 final public function getBlogOptionTop($name, $amount = 10, $sort = 'desc')
332 return $this->get_option_top('blog', $name, $amount, $sort);
335 final public function getMemberOptionTop($name, $amount = 10, $sort = 'desc')
337 return $this->get_option_top('member', $name, $amount, $sort);
340 final public function getCategoryOptionTop($name, $amount = 10, $sort = 'desc')
342 return $this->get_option_top('category', $name, $amount, $sort);
345 final public function getItemOptionTop($name, $amount = 10, $sort = 'desc')
347 return $this->get_option_top('item', $name, $amount, $sort);
351 * NucleusPlugin::getID()
352 * get id for this plugin
356 * @return integer this plugid id
358 final public function getID()
360 return (integer) $this->plugid;
364 * NucleusPlugin::setID()
365 * set favorite id for this plugin
368 * @param integer $plugid favorite id for plugin
371 final public function setID($plugid)
373 $this->plugid = (integer) $plugid;
378 * Returns the URL of the admin area for this plugin (in case there's
379 * no such area, the returned information is invalid)
383 final public function getAdminURL()
386 return $CONF['PluginURL'] . $this->getShortName() . '/';
390 * Returns the directory where the admin directory is located and
391 * where the plugin can maintain his extra files
395 final public function getDirectory()
398 return $DIR_PLUGINS . $this->getShortName() . '/';
402 * Derives the short name for the plugin from the classname (all
407 final public function getShortName()
409 return str_replace('np_','',strtolower(get_class($this)));
413 * Clears the option value cache which saves the option values during
414 * the plugin execution. This function is usefull if the options has
415 * changed during the plugin execution (especially in association with
416 * the PrePluginOptionsUpdate and the PostPluginOptionsUpdate events)
420 final public function clearOptionValueCache()
422 $this->option_values = array();
423 $this->plugin_options = 0;
427 // internal functions of the class starts here
428 protected $option_values; // oid_contextid => value
429 protected $option_info; // context_name => array('oid' => ..., 'default' => ...)
430 protected $plugin_options; // see getOption()
431 protected $plugid; // plugin id
434 * Class constructor: Initializes some internal data
436 public function __construct()
438 $this->option_values = array(); // oid_contextid => value
439 $this->option_info = array(); // context_name => array('oid' => ..., 'default' => ...)
440 $this->plugin_options = 0;
444 * Retrieves an array of the top (or bottom) of an option from a plugin.
446 * @param string $context the context for the option: item, blog, member,...
447 * @param string $name the name of the option
448 * @param int $amount how many rows must be returned
449 * @param string $sort desc or asc
450 * @return array array with both values and contextid's
453 final protected function get_option_top($context, $name, $amount = 10, $sort = 'desc')
455 if ( ($sort != 'desc') && ($sort != 'asc') )
460 $oid = $this->get_option_id($context, $name);
462 // retrieve the data and return
463 $query = "SELECT otype, oextra FROM %s WHERE oid = %d;";
464 $query = sprintf($query, sql_table('plugin_option_desc'), $oid);
465 $row = DB::getRow($query);
467 if ( ($this->optionCanBeNumeric($row['otype'])) && ($row['oextra'] == 'number' ) )
469 $orderby = 'CAST(ovalue AS SIGNED)';
475 $query = "SELECT ovalue value, ocontextid id FROM %s WHERE oid = %d ORDER BY %s %s LIMIT 0,%d;";
476 $query = sprintf($query, sql_table('plugin_option'), $oid, $orderby, $sort, (integer) $amount);
477 $result = DB::getResult($query);
482 foreach( $result as $row )
487 // return the array (duh!)
492 * Creates an option in the database table plugin_option_desc
496 final protected function create_option($context, $name, $desc, $type, $defValue, $typeExtras = '')
498 // create in plugin_option_desc
499 $query = 'INSERT INTO ' . sql_table('plugin_option_desc')
500 .' (opid, oname, ocontext, odesc, otype, odef, oextra)'
501 .' VALUES ('.intval($this->plugid)
502 .', '.DB::quoteValue($name)
503 .', '.DB::quoteValue($context)
504 .', '.DB::quoteValue($desc)
505 .', '.DB::quoteValue($type)
506 .', '.DB::quoteValue($defValue)
507 .', '.DB::quoteValue($typeExtras).')';
509 $oid = DB::getInsertId();
511 $key = $context . '_' . $name;
512 $this->option_info[$key] = array('oid' => $oid, 'default' => $defValue);
517 * Deletes an option from the database tables
518 * plugin_option and plugin_option_desc
522 final protected function delete_option($context, $name)
524 $oid = $this->get_option_id($context, $name);
527 return 0; // no such option
530 // delete all things from plugin_option
531 $query = "DELETE FROM %s WHERE oid=%d;";
532 $query = sprintf($query, sql_table('plugin_option'), (integer) $oid);
535 // delete entry from plugin_option_desc
536 $query = "DELETE FROM %s WHERE oid=%d;";
537 $query = sprintf($query, sql_table('plugin_option_desc'), $oid);
541 unset($this->option_info["{$context}_{$name}"]);
542 $this->option_values = array();
547 * Update an option in the database table plugin_option
549 * returns: 1 on success, 0 on failure
552 final protected function set_option($context, $contextid, $name, $value)
556 $oid = $this->get_option_id($context, $name);
562 // check if context id exists
566 if ( !Member::existsID($contextid) )
572 if ( !$manager->existsBlogID($contextid) )
578 if ( !$manager->existsCategory($contextid) )
584 if ( !$manager->existsItem($contextid, true, true) )
590 if ( $contextid != 0 )
597 // update plugin_option
598 $query = "DELETE FROM %s WHERE oid=%d and ocontextid=%d;";
599 $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid);
602 $query = "INSERT INTO %s (ovalue, oid, ocontextid) VALUES (%s, %d, %d);";
603 $query = sprintf($query, sql_table('plugin_option'), DB::quoteValue($value), $oid, $contextid);
607 $this->option_values["{$oid}_{$contextid}"] = $value;
608 if ( $context == 'global' )
610 $this->plugin_options[strtolower($name)] = $value;
617 * Get an option from Cache or database
618 * - if not in the option Cache read it from the database
619 * - if not in the database write default values into the database
623 final protected function get_option($context, $contextid, $name)
625 $oid = $this->get_option_id($context, $name);
631 $key = "{$oid}_{$contextid}";
633 if ( isset($this->option_values[$key]) )
635 return $this->option_values[$key];
639 $query = "SELECT ovalue FROM %s WHERE oid=%d and ocontextid=%d;";
640 $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid);
641 $result = DB::getResult($query);
643 if ( !$result || ($result->rowCount() == 0) )
645 // fill DB with default value
646 $this->option_values[$key] = $this->get_default_value($context, $name);
647 $query = "INSERT INTO %s (oid, ocontextid, ovalue) VALUES (%d, %d, %s);";
648 $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid, DB::quoteValue($this->option_values[$key]));
653 $row = $result->fetch();
654 $this->option_values[$key] = $row['ovalue'];
657 return $this->option_values[$key];
661 * Returns assoc array with all values for a given option
662 * (one option per possible context id)
666 final protected function get_all_options($context, $name)
668 $oid = $this->get_option_id($context, $name);
673 $default_value = $this->get_default_value($context, $name);
676 $query = "SELECT %s as contextid FROM %s;";
680 $query = sprintf($query, 'bnumber', sql_table('blog'));
683 $query = sprintf($query, 'catid', sql_table('category'));
686 $query = sprintf($query, 'mnumber', sql_table('member'));
689 $query = sprintf($query, 'inumber', sql_table('item'));
693 $result = DB::getResult($query);
696 foreach ( $result as $row )
698 $options[$row['contextid']] = $default_value;
702 $query = "SELECT ocontextid, ovalue FROM %s WHERE oid=%d;";
703 $query = sprintf($query, sql_table('plugin_option'), $oid);
704 $result = DB::getResult($query);
705 foreach ( $result as $row )
707 $options[$row['ocontextid']] = $row['ovalue'];
714 * NucleusPlugin::get_option_id
716 * Gets the 'option identifier' that corresponds to a given option name.
717 * When this method is called for the first time, all the OIDs for the plugin
718 * are loaded into memory, to avoid re-doing the same query all over.
720 * @param string $context option context
721 * @param string $name plugin name
722 * @return integer option id
724 final protected function get_option_id($context, $name)
726 $key = "{$context}_{$name}";
728 if ( array_key_exists($key, $this->option_info)
729 && array_key_exists('oid', $this->option_info[$key]) )
731 return $this->option_info[$key]['oid'];
734 // load all OIDs for this plugin from the database
735 $this->option_info = array();
736 $query = "SELECT oid, oname, ocontext, odef FROM %s WHERE opid=%d;";
737 $query = sprintf($query, sql_table('plugin_option_desc'), $this->plugid);
738 $result = DB::getResult($query);
739 foreach ( $result as $row )
741 $k = $row['ocontext'] . '_' . $row['oname'];
742 $this->option_info[$k] = array('oid' => $row['oid'], 'default' => $row['odef']);
744 $result->closeCursor();
746 return $this->option_info[$key]['oid'];
748 final protected function get_default_value($context, $name)
750 $key = $context . '_' . $name;
752 if ( array_key_exists($key, $this->option_info)
753 && array_key_exists('default', $this->option_info[$key]) )
755 return $this->option_info[$key]['default'];
761 * NucleusPlugin::delete_option_values()
762 * Deletes all option values for a given context and contextid
763 * (used when e.g. a blog, member or category is deleted)
766 *@param String $context global/blog/category/item/member
767 *@param Integer $contextid ID
770 static public function delete_option_values($context, $contextid)
772 // delete all associated plugin options
775 $query = "SELECT oid FROM %s WHERE ocontext=%s;";
776 $query = sprintf($query, sql_table('plugin_option_desc'), DB::quoteValue($context));
778 $result = DB::getResult($query);
779 foreach ( $result as $row )
781 array_push($aOIDs, $row['oid']);
783 $result->closeCursor();
784 // delete those options. go go go
785 if ( count($aOIDs) > 0 )
787 $query = "DELETE FROM %s WHERE oid in (%s) and ocontextid=%d;";
788 $query = sprintf($query, sql_table('plugin_option'), implode(',',$aOIDs), (integer) $contextid);
795 * NucleusPlugin::getOptionMeta()
796 * splits the option's typeextra field (at ;'s) to split the meta collection
799 * @param string $typeExtra the value of the typeExtra field of an option
800 * @return array array of the meta-key/value-pairs
802 static public function getOptionMeta($typeExtra)
806 /* 1. if $typeExtra includes delimiter ';', split it to tokens */
807 $tokens = preg_split('#;#', $typeExtra);
810 * 2. if each of tokens includes "=", it consists of key => value
811 * else it's 'select' option
813 foreach ( $tokens as $token )
816 if ( preg_match("#^([^=]+)?=([^=]+)?$#", $token, $matches) )
818 $meta[$matches[1]] = $matches[2];
822 $meta['select'] = $token;
829 * NucleusPlugin::getOptionSelectValues()
830 * filters the selectlists out of the meta collection
833 * @param string $typeExtra the value of the typeExtra field of an option
834 * @return string the selectlist
836 static public function getOptionSelectValues($typeExtra)
838 $meta = NucleusPlugin::getOptionMeta($typeExtra);
840 if ( array_key_exists('select', $meta) )
842 return $meta['select'];
848 * checks if the eventlist in the database is up-to-date
849 * @return bool if it is up-to-date it return true, else false
852 public function subscribtionListIsUptodate()
854 $res = DB::getResult('SELECT event FROM '.sql_table('plugin_event').' WHERE pid = '.$this->plugid);
856 foreach ( $res as $row )
858 array_push($ev, $row['event']);
860 if ( count($ev) != count($this->getEventList()) )
864 $d = array_diff($ev, $this->getEventList());
867 // there are differences so the db is not up-to-date
874 * NucleusPlugin::apply_plugin_options()
875 * Update its entry in database table
878 * @param $options: array ( 'oid' => array( 'contextid' => 'value'))
879 * (taken from request using requestVar())
880 * @param $new_contextid: integer (accepts a contextid when it is for a new
881 * contextid there was no id available at the moment of writing the
882 * formcontrols into the page (by ex: itemOptions for new item)
885 static public function apply_plugin_options(&$options, $new_contextid = 0)
889 if ( !is_array($options) )
894 foreach ( $options as $oid => $values )
896 // get option type info
897 $query = "SELECT opid, oname, ocontext, otype, oextra, odef FROM %s WHERE oid=%d;";
898 $query = sprintf($query, sql_table('plugin_option_desc'), (integer) $oid);
899 $result = DB::getRow($query);
902 foreach ( $values as $id => $value )
904 // decide wether we are using the contextid of newContextid
905 if ( $new_contextid != 0 )
907 $contextid = $new_contextid;
914 // retreive any metadata
915 $meta = NucleusPlugin::getOptionMeta($result['oextra']);
917 // if the option is readonly or hidden it may not be saved
918 if ( array_key_exists('access', $meta)
919 && in_array($meta['access'], array('readonly', 'hidden')) )
924 // value comes from request
925 $value = undoMagic($value);
927 /* validation the value according to its type */
928 switch ( $result['otype'] )
931 if ( ($value != 'yes') && ($value != 'no') )
938 if ( array_key_exists('datatype', $meta)
939 && ($meta['datatype'] == 'numerical') && ($value != (integer) $value) )
941 $value = (integer) $result['odef'];
951 * trigger event PrePluginOptionsUpdate to give the plugin the
952 * possibility to change/validate the new value for the option
955 'context' => $result['ocontext'],
956 'plugid' => $result['opid'],
957 'optionname' => $result['oname'],
958 'contextid' => $contextid,
960 $manager->notify('PrePluginOptionsUpdate', $data);
962 // delete and insert its fields of table in database
963 $query = "DELETE FROM %s WHERE oid=%d AND ocontextid=%d;";
964 $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid);
966 $query = "INSERT INTO %s (oid, ocontextid, ovalue) VALUES (%d, %d, %s);";
967 $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid, DB::quoteValue($value));
970 // clear option value cache if the plugin object is already loaded
971 $plugin=& $manager->pidLoaded($result['opid']);
974 $plugin->clearOptionValueCache();