PageRenderTime 37ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/system/classes/pluggable.php

https://github.com/HabariMag/habarimag-old
PHP | 357 lines | 218 code | 22 blank | 117 comment | 46 complexity | e9057f3d6fd2577b440a582d42333634 MD5 | raw file
Possible License(s): Apache-2.0
  1. <?php
  2. /**
  3. * @package Habari
  4. *
  5. */
  6. /**
  7. * Pluggable class
  8. * Implements methods that allow descendant classes to register functions to plugin hooks
  9. *
  10. * @version $Id$
  11. * @copyright 2008
  12. */
  13. abstract class Pluggable
  14. {
  15. private $_class_name = null;
  16. public $info;
  17. public $plugin_id;
  18. private $_new_rules = array();
  19. protected $_added_templates = array();
  20. /**
  21. * Pluggable constructor.
  22. * This function creates some internal structures that are required for plugin processing
  23. * Plugins should not define their own constructors, because they are instantiated
  24. * to extract plugin info. Instead, include a sink for a "init" hook
  25. * which is executed immediately after the plugin is loaded during normal execution.
  26. */
  27. public function __construct()
  28. {
  29. $this->info = $this->info();
  30. $this->plugin_id = $this->plugin_id();
  31. }
  32. /**
  33. * Gets the filename that contains this pluggable class
  34. * @return string The filename of the file that contains the pluggable class.
  35. */
  36. final public function get_file()
  37. {
  38. if ( empty( $this->_class_name ) ) {
  39. $class = new ReflectionClass( get_class( $this ) );
  40. $this->_class_name = $class->getFileName();
  41. }
  42. return $this->_class_name;
  43. }
  44. /**
  45. * Gets a database schema associated with this pluggable
  46. * @return string The database schema
  47. */
  48. final public function get_db_schema()
  49. {
  50. $db = DB::get_driver_name();
  51. $schema = dirname( $this->get_file() ) . '/schema/' . $db . '.sql';
  52. return file_get_contents( $schema );
  53. }
  54. /**
  55. * Get a fully-qualified URL directory that contains this pluggable class
  56. *
  57. * @param bool whether to include a trailing slash. Default: No
  58. * @return string URL
  59. */
  60. public function get_url( $trail = false )
  61. {
  62. return URL::get_from_filesystem( $this->get_file(), $trail );
  63. }
  64. /**
  65. * Returns a unique id for this pluggable
  66. * @return string A plugin id
  67. */
  68. final public function plugin_id()
  69. {
  70. static $id;
  71. if ( !isset( $id ) ) {
  72. $id = Plugins::id_from_file( str_replace( '\\', '/', $this->get_file() ) );
  73. }
  74. return $id;
  75. }
  76. /**
  77. * Load a translation domain/file for this pluggable
  78. * @return boolean true if data was successfully loaded, false otherwise
  79. */
  80. public function load_text_domain( $domain )
  81. {
  82. $base_dir = realpath( dirname( $this->get_file() ) );
  83. return HabariLocale::load_pluggable_domain( $domain, $base_dir );
  84. }
  85. /**
  86. * Registers all of this class' action_ and filter_ functions with the Plugins dispatcher
  87. * Registers xmlrpc_ functions with the Plugins dispatcher, and turns '__' into '.'
  88. * for the purposes of matching dotted XMLRPC requests.
  89. * If the class is an instance of Pluggable, registers the hooks with a plugin id also.
  90. * @param mixed $object The object or class name to register the hooks of
  91. **/
  92. public static function load_hooks($object)
  93. {
  94. static $registered = array();
  95. if(is_object($object)) {
  96. $hash = spl_object_hash($object);
  97. if(isset($registered[$hash])) {
  98. return;
  99. }
  100. else {
  101. $registered[$hash] = true;
  102. }
  103. }
  104. else {
  105. $registered[$object] = true;
  106. }
  107. // combine the array so we can have hooks => function
  108. $methods = get_class_methods( $object );
  109. $methods = array_combine( $methods, $methods );
  110. // get the specific priority values for functions, as needed
  111. if ( method_exists( $object, 'set_priorities' ) ) {
  112. $priorities = $object->set_priorities();
  113. }
  114. // get the aliases.
  115. if ( method_exists( $object, 'alias' ) ) {
  116. $methods = array_merge_recursive( $methods, $object->alias() );
  117. }
  118. // loop over all the methods in this class
  119. foreach ( $methods as $fn => $hooks ) {
  120. // loop hooks and register callback for each
  121. foreach ( (array) $hooks as $hook ) {
  122. // make sure the method name is of the form
  123. // action_foo or filter_foo of xmlrpc_foo or theme_foo
  124. if ( preg_match( '#^(action|filter|xmlrpc|theme)_#i', $hook ) ) {
  125. $priority = 8;
  126. if(isset($priorities[$hook])) {
  127. $priority = $priorities[$hook];
  128. }
  129. elseif(preg_match('#^(.+)_(\d+)$#', $hook, $priority_match)) {
  130. $hook = $priority_match[1];
  131. $priority = intval($priority_match[2]);
  132. }
  133. elseif(isset( $priorities[$fn])) {
  134. $priority = $priorities[$fn];
  135. }
  136. list( $type, $hook ) = explode( '_', $hook, 2 );
  137. if ( $type === 'xmlrpc' ) {
  138. $hook = str_replace( '__', '.', $hook );
  139. }
  140. Plugins::register( array( $object, $fn ), $type, $hook, $priority );
  141. if($object instanceof Pluggable) {
  142. Plugins::register( array( $object, $fn ), $type, $hook . ':' . $object->plugin_id(), $priority );
  143. }
  144. }
  145. }
  146. }
  147. }
  148. /**
  149. * Called when a pluggable is loaded to register its actions and filters.
  150. */
  151. public function load()
  152. {
  153. self::load_hooks($this);
  154. // look for help with this
  155. if ( method_exists( $this, 'help' ) ) {
  156. Plugins::register( array( $this, '_help_plugin_config' ), 'filter', 'plugin_config:' . $this->plugin_id(), 8 );
  157. Plugins::register( array( $this, '_help_plugin_ui' ), 'action', 'plugin_ui:' . $this->plugin_id(), 8 );
  158. }
  159. // look for a basic configure method
  160. if ( method_exists( $this, 'configure' ) ) {
  161. Plugins::register( array( $this, '_configure_plugin_config' ), 'filter', 'plugin_config:' . $this->plugin_id(), 8 );
  162. Plugins::register( array( $this, '_configure_plugin_ui' ), 'action', 'plugin_ui:' . $this->plugin_id(), 8 );
  163. }
  164. }
  165. /**
  166. * Registered to the plugin_config hook to supply help via a plugin's help() method
  167. *
  168. * @param array $actions An array of actions applicable to this plugin
  169. * @param string $plugin_id The plugin id to which the actions belong
  170. * @return array The modified array of actions
  171. */
  172. public function _help_plugin_config( $actions, $plugin_id )
  173. {
  174. if ( $plugin_id == $this->plugin_id() ) {
  175. $actions['_help'] = _t( '?' );
  176. }
  177. return $actions;
  178. }
  179. /**
  180. * Registered to the plugin_ui hook to supply help via a plugin's help() method
  181. *
  182. * @param string $plugin_id The id of the plugin whose action was triggered
  183. * @param string $action The action triggered
  184. */
  185. public function _help_plugin_ui( $plugin_id, $action )
  186. {
  187. if ( $plugin_id == $this->plugin_id() && $action == '_help' ) {
  188. $output = $this->help();
  189. if ( $output instanceof FormUI ) {
  190. $output->out();
  191. }
  192. else {
  193. echo "<div class=\"help\">{$output}</div>";
  194. }
  195. }
  196. }
  197. /**
  198. * Registered to the plugin_config hook to supply a config via a plugin's configure() method
  199. *
  200. * @param array $actions An array of actions applicable to this plugin
  201. * @param string $plugin_id The plugin id to which the actions belong
  202. * @return array The modified array of actions
  203. */
  204. public function _configure_plugin_config( $actions, $plugin_id )
  205. {
  206. if ( $plugin_id == $this->plugin_id() ) {
  207. $actions['_configure'] = _t( 'Configure' );
  208. }
  209. return $actions;
  210. }
  211. /**
  212. * Registered to the plugin_ui hook to supply a config via a plugin's configure() method
  213. *
  214. * @param string $plugin_id The id of the plugin whose action was triggered
  215. * @param string $action The action triggered
  216. */
  217. public function _configure_plugin_ui( $plugin_id, $action )
  218. {
  219. if ( $plugin_id == $this->plugin_id() && $action == '_configure' ) {
  220. $output = $this->configure();
  221. if ( $output instanceof FormUI ) {
  222. $output->out();
  223. }
  224. else {
  225. echo $output;
  226. }
  227. }
  228. }
  229. /**
  230. * Add a rewrite rule that dispatches entirely to a plugin hook
  231. *
  232. * @param mixed $rule An old-style rewrite rule string, where quoted segments are literals and unquoted segments are variable names, OR a RewriteRule object
  233. * @param string $hook The suffix of the hook function: action_plugin_act_{$suffix}
  234. */
  235. public function add_rule( $rule, $hook )
  236. {
  237. if ( count( $this->_new_rules ) == 0 ) {
  238. Plugins::register( array( $this, '_filter_rewrite_rules' ), 'filter', 'rewrite_rules', 7 );
  239. }
  240. if ( $rule instanceof RewriteRule ) {
  241. $this->_new_rules[] = $rule;
  242. }
  243. else {
  244. $this->_new_rules[] = RewriteRule::create_url_rule( $rule, 'PluginHandler', $hook );
  245. }
  246. }
  247. /**
  248. * Add the rewrite rules queued by add_rule() to the full rule set
  249. *
  250. * @param array $rules The array of current RewriteRules
  251. * @return array The appended array of RewriteRules
  252. */
  253. public function _filter_rewrite_rules( $rules )
  254. {
  255. $rules = array_merge( $rules, $this->_new_rules );
  256. return $rules;
  257. }
  258. /**
  259. * Adds a template to the default theme that is stored in a specified path.
  260. * Use this function as a shortcut to make available additional templates to a theme
  261. * from within the plugin directory.
  262. *
  263. * @param string $name The name of the template that will be displayed, sans extension
  264. * @param string $filename The full path of the template file used for the specified name
  265. * @param boolean $override If false, allow a template with the same name in the active theme directory to override this one.
  266. * If true, always override the active theme's template with this one.
  267. */
  268. protected function add_template( $name, $filename, $override = false )
  269. {
  270. if ( count( $this->_added_templates ) == 0 ) {
  271. Plugins::register( array( &$this, '_plugin_available_templates' ), 'filter', 'available_templates' );
  272. Plugins::register( array( &$this, '_plugin_include_template_file' ), 'filter', 'include_template_file' );
  273. }
  274. $this->_added_templates[$name] = array( $filename, $override );
  275. }
  276. /**
  277. * Add plugin templates to the list of templates that are present in the current theme
  278. *
  279. * @param array $list List of template names in the current theme
  280. * @return array The modified list of template names
  281. */
  282. public function _plugin_available_templates( $list )
  283. {
  284. $list = array_merge( $list, array_keys( $this->_added_templates ) );
  285. return $list;
  286. }
  287. /**
  288. * Potentially serve a different file for the requested template name
  289. *
  290. * @param string $file The filename of the template the theme will display
  291. * @param string $name The name of the template requested
  292. * @return string The potentially modified filename to use for the requested template.
  293. */
  294. public function _plugin_include_template_file( $file, $name )
  295. {
  296. if ( isset( $this->_added_templates[$name] ) ) {
  297. if ( $this->_added_templates[$name][1] || !file_exists( $file ) ) {
  298. $file = $this->_added_templates[$name][0];
  299. }
  300. }
  301. return $file;
  302. }
  303. /**
  304. * Provide a method to return the version number from a pluggable's info
  305. * @return string The version of the pluggable
  306. **/
  307. public abstract function get_version();
  308. /**
  309. * Execute the upgrade action on any pluggable that has a version number change
  310. * Update the version number of the pluggable in the database to what is installed
  311. */
  312. public function upgrade()
  313. {
  314. if(DB::is_connected() && @ Options::get( 'installed' )) {
  315. $pluggable_class = get_class($this);
  316. $versions = Options::get( 'pluggable_versions' );
  317. if(isset($versions[$pluggable_class])) {
  318. $old_version = $versions[$pluggable_class];
  319. if($old_version != $this->get_version()) {
  320. Plugins::act_id('upgrade', $this->plugin_id(), $old_version);
  321. $versions[$pluggable_class] = $this->get_version();
  322. Options::set( 'pluggable_versions', $versions );
  323. }
  324. }
  325. else {
  326. $versions[$pluggable_class] = $this->get_version();
  327. Options::set( 'pluggable_versions', $versions );
  328. }
  329. }
  330. }
  331. }
  332. ?>