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