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 1630 2012-01-28 12:16:14Z sakamocchi $
22 abstract class NucleusPlugin
24 // these final public functions _have_ 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_* 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 = sql_query($query);
267 while ( $row = sql_fetch_object($result) )
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 $result = sql_query($query);
467 $o = sql_fetch_array($result);
469 if ( ($this->optionCanBeNumeric($o['otype'])) && ($o['oextra'] == 'number' ) )
471 $orderby = 'CAST(ovalue AS SIGNED)';
477 $query = "SELECT ovalue value, ocontextid id FROM %s WHERE oid = %d ORDER BY %s %s LIMIT 0,%d;";
478 $query = sprintf($query, sql_table('plugin_option'), $oid, $orderby, $sort, (integer) $amount);
479 $result = sql_query($query);
484 while( $row = sql_fetch_array($result) )
489 // return the array (duh!)
494 * Creates an option in the database table plugin_option_desc
498 final protected function create_option($context, $name, $desc, $type, $defValue, $typeExtras = '')
500 // create in plugin_option_desc
501 $query = 'INSERT INTO ' . sql_table('plugin_option_desc')
502 .' (opid, oname, ocontext, odesc, otype, odef, oextra)'
503 .' VALUES ('.intval($this->plugid)
504 .', \''.sql_real_escape_string($name).'\''
505 .', \''.sql_real_escape_string($context).'\''
506 .', \''.sql_real_escape_string($desc).'\''
507 .', \''.sql_real_escape_string($type).'\''
508 .', \''.sql_real_escape_string($defValue).'\''
509 .', \''.sql_real_escape_string($typeExtras).'\');';
511 $oid = sql_insert_id();
513 $key = $context . '_' . $name;
514 $this->option_info[$key] = array('oid' => $oid, 'default' => $defValue);
519 * Deletes an option from the database tables
520 * plugin_option and plugin_option_desc
524 final protected function delete_option($context, $name)
526 $oid = $this->get_option_id($context, $name);
529 return 0; // no such option
532 // delete all things from plugin_option
533 $query = "DELETE FROM %s WHERE oid=%d;";
534 $query = sprintf($query, sql_table('plugin_option'), (integer) $oid);
537 // delete entry from plugin_option_desc
538 $query = "DELETE FROM %s WHERE oid=%d;";
539 $query = sprintf($query, sql_table('plugin_option_desc'), $oid);
543 unset($this->option_info["{$context}_{$name}"]);
544 $this->option_values = array();
549 * Update an option in the database table plugin_option
551 * returns: 1 on success, 0 on failure
554 final protected function set_option($context, $contextid, $name, $value)
558 $oid = $this->get_option_id($context, $name);
564 // check if context id exists
568 if ( !MEMBER::existsID($contextid) )
574 if ( !$manager->existsBlogID($contextid) )
580 if ( !$manager->existsCategory($contextid) )
586 if ( !$manager->existsItem($contextid, true, true) )
592 if ( $contextid != 0 )
599 // update plugin_option
600 $query = "DELETE FROM %s WHERE oid=%d and ocontextid=%d;";
601 $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid);
604 $query = "INSERT INTO %s (ovalue, oid, ocontextid) VALUES ('%s', %d, %d);";
605 $query = sprintf($query, sql_table('plugin_option'), sql_real_escape_string($value), $oid, $contextid);
609 $this->option_values["{$oid}_{$contextid}"] = $value;
610 if ( $context == 'global' )
612 $this->plugin_options[strtolower($name)] = $value;
619 * Get an option from Cache or database
620 * - if not in the option Cache read it from the database
621 * - if not in the database write default values into the database
625 final protected function get_option($context, $contextid, $name)
627 $oid = $this->get_option_id($context, $name);
633 $key = "{$oid}_{$contextid}";
635 if ( isset($this->option_values[$key]) )
637 return $this->option_values[$key];
641 $query = "SELECT ovalue FROM %s WHERE oid=%d and ocontextid=%d;";
642 $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid);
643 $result = sql_query($query);
645 if ( !$result || (sql_num_rows($result) == 0) )
647 // fill DB with default value
648 $this->option_values[$key] = $this->get_default_value($context, $name);
649 $query = "INSERT INTO %s (oid, ocontextid, ovalue) VALUES (%d, %d, '%s');";
650 $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid, sql_real_escape_string($defVal));
655 $o = sql_fetch_object($result);
656 $this->option_values[$key] = $o->ovalue;
659 return $this->option_values[$key];
663 * Returns assoc array with all values for a given option
664 * (one option per possible context id)
668 final protected function get_all_options($context, $name)
670 $oid = $this->get_option_id($context, $name);
675 $default_value = $this->get_default_value($context, $name);
678 $query = "SELECT %s as contextid FROM %s;";
682 $query = sprintf($query, 'bnumber', sql_table('blog'));
685 $query = sprintf($query, 'catid', sql_table('category'));
688 $query = sprintf($query, 'mnumber', sql_table('member'));
691 $query = sprintf($query, 'inumber', sql_table('item'));
695 $result = sql_query($query);
698 while ( $o = sql_fetch_object($r) )
700 $options[$o->contextid] = $default_value;
704 $query = "SELECT ocontextid, ovalue FROM %s WHERE oid=%d;";
705 $query = sprintf($query, sql_table('plugin_option'), $oid);
706 $result = sql_query($query);
707 while ( $o = sql_fetch_object($result) )
709 $options[$o->ocontextid] = $o->ovalue;
716 * NucleusPlugin::get_option_id
718 * Gets the 'option identifier' that corresponds to a given option name.
719 * When this method is called for the first time, all the OIDs for the plugin
720 * are loaded into memory, to avoid re-doing the same query all over.
722 * @param string $context option context
723 * @param string $name plugin name
724 * @return integer option id
726 final protected function get_option_id($context, $name)
728 $key = "{$context}_{$name}";
730 if ( array_key_exists($key, $this->option_info)
731 && array_key_exists('oid', $this->option_info[$key]) )
733 return $this->option_info[$key]['oid'];
736 // load all OIDs for this plugin from the database
737 $this->option_info = array();
738 $query = "SELECT oid, oname, ocontext, odef FROM %s WHERE opid=%d;";
739 $query = sprintf($query, sql_table('plugin_option_desc'), $this->plugid);
740 $result = sql_query($query);
741 while ( $o = sql_fetch_object($result) )
743 $k = $o->ocontext . '_' . $o->oname;
744 $this->option_info[$k] = array('oid' => $o->oid, 'default' => $o->odef);
746 sql_free_result($result);
748 return $this->option_info[$key]['oid'];
750 final protected function get_default_value($context, $name)
752 $key = $context . '_' . $name;
754 if ( array_key_exists($key, $this->option_info)
755 && array_key_exists('default', $this->option_info[$key]) )
757 return $this->option_info[$key]['default'];
763 * Deletes all option values for a given context and contextid
764 * (used when e.g. a blog, member or category is deleted)
768 final protected function delete_option_values($context, $contextid)
770 // delete all associated plugin options
773 $query = "SELECT oid FROM %s WHERE ocontext='%s';";
774 $query = sprintf($query, sql_table('plugin_option_desc'), sql_real_escape_string($context));
776 $result = sql_query($query);
777 while ( $o = sql_fetch_object($result) )
779 array_push($aOIDs, $o->oid);
781 sql_free_result($result);
782 // delete those options. go go go
783 if ( count($aOIDs) > 0 )
785 $query = "DELETE FROM %s WHERE oid in (%s) and ocontextid=%d;";
786 $query = sprintf($query, sql_table('plugin_option'), implode(',',$aOIDs), (integer) $contextid);
793 * NucleusPlugin::getOptionMeta()
794 * splits the option's typeextra field (at ;'s) to split the meta collection
797 * @param string $typeExtra the value of the typeExtra field of an option
798 * @return array array of the meta-key/value-pairs
800 static public function getOptionMeta($typeExtra)
804 /* 1. if $typeExtra includes delimiter ';', split it to tokens */
805 $tokens = i18n::explode(';', $typeExtra);
808 * 2. if each of tokens includes "=", it consists of key => value
809 * else it's 'select' option
811 foreach ( $tokens as $token )
814 if ( preg_match("#^([^=]+)?=([^=]+)?$#", $token, $matches) )
816 $meta[$matches[1]] = $matches[2];
820 $meta['select'] = $token;
827 * NucleusPlugin::getOptionSelectValues()
828 * filters the selectlists out of the meta collection
831 * @param string $typeExtra the value of the typeExtra field of an option
832 * @return string the selectlist
834 static public function getOptionSelectValues($typeExtra)
836 $meta = NucleusPlugin::getOptionMeta($typeExtra);
838 if ( array_key_exists('select', $meta) )
840 return $meta['select'];
846 * checks if the eventlist in the database is up-to-date
847 * @return bool if it is up-to-date it return true, else false
850 public function subscribtionListIsUptodate()
852 $res = sql_query('SELECT event FROM '.sql_table('plugin_event').' WHERE pid = '.$this->plugid);
854 while( $a = sql_fetch_array($res) )
856 array_push($ev, $a['event']);
858 if ( count($ev) != count($this->getEventList()) )
862 $d = array_diff($ev, $this->getEventList());
865 // there are differences so the db is not up-to-date
872 * NucleusPlugin::apply_plugin_options()
873 * Update its entry in database table
876 * @param $options: array ( 'oid' => array( 'contextid' => 'value'))
877 * (taken from request using requestVar())
878 * @param $newContextid: integer (accepts a contextid when it is for a new
879 * contextid there was no id available at the moment of writing the
880 * formcontrols into the page (by ex: itemOptions for new item)
883 static public function apply_plugin_options(&$options, $newContextid = 0)
886 if ( !is_array($options) )
891 foreach ( $options as $oid => $values )
893 // get option type info
894 $query = "SELECT opid, oname, ocontext, otype, oextra, odef FROM %s WHERE oid=%d;";
895 $query = sprintf($query, sql_table('plugin_option_desc'), (integer) $oid);
896 $result = sql_query($query);
897 if ( $info = sql_fetch_object($result) )
899 foreach ( $values as $key => $value )
901 // avoid overriding the key used by foreach statement
904 // retreive any metadata
905 $meta = NucleusPlugin::getOptionMeta($info->oextra);
907 // if the option is readonly or hidden it may not be saved
908 if ( array_key_exists('access', $meta)
909 && in_array($meta['access'], array('readonly', 'hidden')) )
914 // value comes from request
915 $value = undoMagic($value);
917 /* validation the value according to its type */
918 switch ( $info->otype )
921 if ( ($value != 'yes') && ($value != 'no') )
928 if ( array_key_exists('datatype', $meta)
929 && ($meta['datatype'] == 'numerical') && ($value != (integer) $value) )
931 $value = (integer) $info->odef;
940 // decide wether we are using the contextid of newContextid
941 if ( $newContextid != 0 )
943 $contextid = $newContextid;
947 * trigger event PrePluginOptionsUpdate to give the plugin the
948 * possibility to change/validate the new value for the option
951 'context' => $info->ocontext,
952 'plugid' => $info->opid,
953 'optionname' => $info->oname,
954 'contextid' => $contextid,
956 $manager->notify('PrePluginOptionsUpdate', $data);
958 // delete and insert its fields of table in database
959 $query = "DELETE FROM %s WHERE oid=%d AND ocontextid=%d;";
960 $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid);
962 $query = "INSERT INTO %s (oid, ocontextid, ovalue) VALUES (%d, %d, '%s');";
963 $query = sprintf($query, sql_table('plugin_option'), (integer) $oid, (integer) $contextid, sql_real_escape_string($value));
967 // clear option value cache if the plugin object is already loaded
968 if ( is_object($info) )
970 $plugin=& $manager->pidLoaded($info->opid);
973 $plugin->clearOptionValueCache();