PageRenderTime 47ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/class.plugin.php

https://github.com/jeffrydegrande/sideposts
PHP | 542 lines | 226 code | 57 blank | 259 comment | 40 complexity | 01e469c6b4b53fe4485d64480d4a0929 MD5 | raw file
  1. <?php
  2. /**
  3. * Plugins related functions and classes.
  4. *
  5. * @version $Rev: 139 $
  6. * @author Jordi Canals
  7. * @package AOC
  8. * @subpackage Library
  9. * @link http://alkivia.org
  10. * @license http://www.gnu.org/licenses/gpl.html GNU General Public License v3
  11. Copyright 2009 Jordi Canals <alkivia@jcanals.net>
  12. This program is free software: you can redistribute it and/or modify
  13. it under the terms of the GNU General Public License as published by
  14. the Free Software Foundation, either version 3 of the License, or
  15. (at your option) any later version.
  16. This program is distributed in the hope that it will be useful,
  17. but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. GNU General Public License for more details.
  20. You should have received a copy of the GNU General Public License
  21. along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. */
  23. require_once ( SPOSTS_LIB . '/plugins.php' );
  24. /**
  25. * Abtract class to be used as a plugin template.
  26. * Must be implemented before using this class and it's recommended to prefix the class to prevent collissions.
  27. * There are some special functions thay can declared (as protected) in implementations to perform main actions:
  28. * - activate: (Protected) Actions to run when activating the plugin.
  29. * - _deactivate: (Hook, must be public) Actions to run when deactivating the plugin.
  30. * - update: (Protected) Actions to update the plugin to a new version. (Updating version on DB is done after this).
  31. * Takes plugin running version as a parameter.
  32. * - setDefaults: (Protected) Fills the $defaults class var with the default settings.
  33. * - init: (Protected) Actions to run when plugins initialization is performed (In plugins loaded).
  34. * - widgetsInit: (Protected) Actions to init plugin widgets (In widgets_init).
  35. * - startUp: (Protected) Actions to run at system startup (before plugins are loaded).
  36. * - _adminMenus: (Hook, must be public) Set the menus in WordPress Dashboard.
  37. *
  38. * @uses plugins.php
  39. * @author Jordi Canals
  40. * @package AOC
  41. * @subpackage Library
  42. * @link http://alkivia.org
  43. */
  44. abstract class spostsPlugin
  45. {
  46. /**
  47. * Plugin ID. Is the plugin short name.
  48. * Filled in constructor (as a constructor param).
  49. * @var string
  50. */
  51. protected $ID;
  52. /**
  53. * Plugin main file.
  54. * Filled in constructor (as a constructor param).
  55. * Cointains the full path to the main plugin's file. The one cointaining plugin's data header.
  56. * @var string
  57. */
  58. protected $p_file;
  59. /**
  60. * Plugin data. Readed from the main plugin file header and the readme file.
  61. * Filled in loadPluginData(). Called in constructor.
  62. * From the filename:
  63. * - 'ID' - Plugin internal short name. Taken from main plugin's file name.
  64. * Fom the file header:
  65. * - 'Name' - Name of the plugin, must be unique.
  66. * - 'Title' - Title of the plugin and the link to the plugin's web site.
  67. * - 'Description' - Description of what the plugin does and/or notes from the author.
  68. * - 'Author' - The author's name
  69. * - 'AuthorURI' - The author's web site address.
  70. * - 'Version' - The plugin's version number.
  71. * - 'PluginURI' - Plugin's web site address.
  72. * - 'TextDomain' - Plugin's text domain for localization.
  73. * - 'DomainPath' - Plugin's relative directory path to .mo files.
  74. * From readme.txt file :
  75. * - 'Contributors' - An array with all contributors nicknames.
  76. * - 'Tags' - An array with all plugin tags.
  77. * - 'DonateURI' - The donations page address.
  78. * - 'Requires' - Minimum required WordPress version.
  79. * - 'Tested' - Higher WordPress version this plugin has been tested.
  80. * - 'Stable' - Last stable tag when this was released.
  81. * @var array
  82. */
  83. protected $p_data;
  84. /**
  85. * Plugin paths: folder name, absolute path to plugin's folder and folder url.
  86. * Filled in loadPaths(). Called in constructor.
  87. * - 'subdir' - The base plugin subdirectory.
  88. * - 'path' - The full path to plugin's folder.
  89. * - 'url' - The full URL to plugin's folder.
  90. * @var array
  91. */
  92. protected $p_dirs;
  93. /**
  94. * Plugin saved data.
  95. * - 'post' - Saves the current post.
  96. * - 'more' - Saves the read more status.
  97. * @var array
  98. */
  99. protected $saved;
  100. /**
  101. * Plugin settings (from DB)
  102. * @var array
  103. */
  104. protected $settings;
  105. /**
  106. * Plugin default settings.
  107. * This settings can difer from install defaults and are used to fill settings loaded from DB
  108. * @var array
  109. */
  110. protected $defaults = array();
  111. /**
  112. * Flag to see if we are installing (activating for first time) or reactivating the plugin.
  113. * @var boolean
  114. */
  115. protected $installing = false;
  116. /**
  117. * Flag to see if plugin needs to be updated.
  118. * @var boolean
  119. */
  120. protected $needs_update = false;
  121. /**
  122. * Class constructor.
  123. * Calls the implementated method 'startUp' if it exists. This is done at plugins loading time.
  124. * Prepares admin menus by seting an action for the implemented method '_adminMenus' if it exists.
  125. *
  126. * @param string $plugin_file Full main plugin's filename (absolute to root).
  127. * @param string $ID Plugin short name (known as plugin ID).
  128. * @return spostsPlugin|false The plugin object or false if not compatible.
  129. */
  130. final function __construct( $plugin_file, $ID = '' ) {
  131. $this->p_file = trim($plugin_file);
  132. $this->ID = ( empty($ID) ) ? strtolower(basename($this->p_file, '.php')) : trim($ID) ;
  133. // Load component data and settings.
  134. if ( method_exists($this, 'setDefaults') ) {
  135. $this->setDefaults();
  136. }
  137. $this->loadPluginData();
  138. $this->loadPaths();
  139. if ( $this->isCompatible() ) {
  140. // Activation and deactivation hooks.
  141. register_activation_hook($this->p_file, array($this, '_activatePlugin'));
  142. if ( method_exists($this, '_deactivate') ) {
  143. register_deactivation_hook($this->p_file, array($this, '_deactivate'));
  144. }
  145. // Load style files.
  146. if ( is_admin() ) {
  147. add_action('admin_print_styles', array($this, '_enqueueStyles')); // For Compatibility with WP 2.8
  148. } else {
  149. add_action('wp_print_styles', array($this, '_enqueueStyles'));
  150. }
  151. // Init plugins at plugins and widgets
  152. add_action('plugins_loaded', array($this, '_initPlugin'));
  153. add_action('widgets_init', array($this, '_initWidgets'));
  154. // Add administration menus.
  155. if ( method_exists($this, '_adminMenus') ) {
  156. add_action('admin_menu', array($this, '_adminMenus')); // Add Panel menus.
  157. }
  158. // Startup the plugin.
  159. if ( method_exists($this, 'startUp') ) {
  160. $this->startUp();
  161. }
  162. }
  163. }
  164. /**
  165. * Activates the plugin. Only runs on first activation.
  166. * Saves the plugin version in DB, and calls the 'activate' method.
  167. *
  168. * @hook register_activation_hook
  169. * @access private
  170. * @return void
  171. */
  172. final function _activatePlugin() {
  173. if ( method_exists($this, 'setDefaults') ) {
  174. $this->setDefaults();
  175. }
  176. // If there is an additional function to perform on activate.
  177. if ( method_exists($this, 'activate') ) {
  178. $this->activate();
  179. }
  180. $this->settings = $this->defaults;
  181. add_option($this->ID . '_settings', $this->settings);
  182. add_option($this->ID . '_version', $this->p_data['Version']);
  183. }
  184. /**
  185. * Init the plugin (In action 'plugins_loaded')
  186. * Here whe call the 'update' and 'init' functions. This is done after the plugins are loaded.
  187. * Also the plugin version and settings are updated here.
  188. *
  189. * @hook action plugins_loaded
  190. * @access private
  191. * @return void
  192. */
  193. final function _initPlugin() {
  194. $this->loadTranslations();
  195. // First, check if the plugin needs to be updated.
  196. if ( $this->needs_update ) {
  197. if ( method_exists($this, 'update') ) {
  198. $version = get_option($this->ID . '_version');
  199. $this->update($version);
  200. }
  201. update_option($this->ID . '_version', $this->p_data['Version']);
  202. update_option($this->ID . '_settings', $this->settings);
  203. }
  204. // Call the custom init for the plugin.
  205. if ( method_exists($this, 'init') ) {
  206. $this->init();
  207. }
  208. }
  209. /**
  210. * Inits the widgets (In action 'widgets_init')
  211. * Before loading the widgets, we check that standard sidebar is present.
  212. *
  213. * @hook action 'widgets_init'
  214. * @return void
  215. */
  216. final function _initWidgets() {
  217. if ( method_exists($this, 'widgetsInit') && $this->isStandardSidebar() ) {
  218. $this->widgetsInit();
  219. }
  220. }
  221. /**
  222. * Loads translations file, located on the plugin's lang subdir.
  223. *
  224. * @return void
  225. */
  226. final protected function loadTranslations() {
  227. load_plugin_textdomain($this->ID, false, $this->p_dirs['subdir'] . '/lang');
  228. }
  229. /**
  230. * Prepares and enqueues plugin styles.
  231. * Filters used:
  232. * - 'pluginID_style_admin' - For the admin style URL.
  233. * - 'pluginID_style_url' - For the public style URL.
  234. *
  235. * @uses apply_filters() Calls the 'ID_style_url' and 'ID_style_admin' on the style file URL.
  236. * @hook action wp_print_styles and admin_print_styles
  237. * @access private
  238. * @return void
  239. */
  240. final function _enqueueStyles() {
  241. $url = '';
  242. if ( is_admin() ) {
  243. if ( file_exists($this->p_dirs['path'] . 'admin.css') ) {
  244. $url = $this->p_dirs['url'] . 'admin.css';
  245. }
  246. $url = apply_filters($this->ID . '_style_admin', $url);
  247. } else {
  248. if ( file_exists($this->p_dirs['path'] . 'style.css') ) {
  249. $url = $this->p_dirs['url'] . 'style.css';
  250. }
  251. $url = apply_filters($this->ID . '_style_url', $url);
  252. }
  253. if ( ! empty($url) ) {
  254. wp_register_style($this->ID, $url, false, $this->p_data['Version']);
  255. wp_enqueue_style($this->ID);
  256. }
  257. }
  258. /**
  259. * Returns the plguin Folder basename.
  260. *
  261. * @return string
  262. */
  263. final public function getFolder() {
  264. if ( empty($p_dirs) ) {
  265. $this->loadPaths();
  266. }
  267. return $this->p_dirs['subdir'];
  268. }
  269. /**
  270. * Returns the URL to the plugin folder (with trailing slash).
  271. *
  272. * @return string
  273. */
  274. final public function getURL() {
  275. if ( empty($p_dirs) ) {
  276. $this->loadPaths();
  277. }
  278. return $this->p_dirs['url'];
  279. }
  280. /**
  281. * Returns the Absolute path to plugin folder (with trailing slash).
  282. *
  283. * @return string
  284. */
  285. final public function getPath() {
  286. if ( empty($p_dirs) ) {
  287. $this->loadPaths();
  288. }
  289. return $this->p_dirs['path'];
  290. }
  291. /**
  292. * Returns private or protected values.
  293. * @since 0.6
  294. *
  295. * @param $name Name of the value.
  296. * @return mixed Requested value.
  297. */
  298. public function __get( $name ) {
  299. if ( empty($this->p_data) ) {
  300. $this->loadPluginData();
  301. }
  302. $name = strtolower($name);
  303. switch ( $name ) {
  304. case 'id':
  305. return $this->ID;
  306. break;
  307. case 'file':
  308. return $this->p_file;
  309. break;
  310. case 'version':
  311. return $this->p_data['Version'];
  312. break;
  313. default:
  314. return false;
  315. }
  316. }
  317. /**
  318. * Returns a plugin setting.
  319. * If no specific settings is requested, returns all settings.
  320. * If requested a non existent settings, returns $default.
  321. *
  322. * @param $name Name for the settings to return.
  323. * @param $default Default value to use if setting does not exists.
  324. * @return mixed The settings value or an array with all settings.
  325. */
  326. public function getOption( $name = '', $default = false ) {
  327. if ( empty($name) ) {
  328. return $this->settings;
  329. } elseif ( isset($this->settings[$name]) ) {
  330. return $this->settings[$name];
  331. } else {
  332. return $default;
  333. }
  334. }
  335. /**
  336. * Returns plugin data.
  337. * This data is loaded from the main plugin's file.
  338. *
  339. * @see $p_data
  340. * @return mixed The parameter requested or an array wil all data.
  341. */
  342. final public function getPluginData( $name = '' ) {
  343. if ( empty($name) ) {
  344. return $this->p_data;
  345. } elseif ( isset( $this->p_data[$name]) ) {
  346. return $this->p_data['name'];
  347. } else {
  348. return false;
  349. }
  350. }
  351. /**
  352. * Loads plugin data and settings.
  353. * Data is loaded from plugin and readme file headers. Settings from Database.
  354. *
  355. * @return void
  356. */
  357. final private function loadPluginData() {
  358. if ( empty($this->p_data) ) {
  359. if ( ! function_exists('get_plugin_data') ) {
  360. require_once ( ABSPATH . 'wp-admin/includes/plugin.php' );
  361. }
  362. $p_data = get_plugin_data($this->p_file);
  363. $r_data = sposts_plugin_readme_data($this->p_file);
  364. $this->p_data = array_merge($r_data, $p_data);
  365. }
  366. $this->settings = get_option($this->ID . '_settings');
  367. if ( ! empty($this->defaults) && is_array($this->defaults) ) {
  368. if ( is_array($this->settings) ) {
  369. $this->settings = array_merge($this->defaults, $this->settings);
  370. } else {
  371. $this->settings = $this->defaults;
  372. }
  373. }
  374. $ver = get_option($this->ID . '_version');
  375. if ( false === $ver ) {
  376. $this->installing = true;
  377. } elseif ( version_compare($ver, $this->p_data['Version'], 'ne') ) {
  378. $this->needs_update = true;
  379. }
  380. }
  381. /**
  382. * Saves the current post state.
  383. *
  384. * @return void
  385. */
  386. final public function savePost() {
  387. global $post, $more;
  388. $this->saved['post'] = $post;
  389. $this->saved['more'] = $more;
  390. }
  391. /**
  392. * Restores the current post state.
  393. * Saved in savePost()
  394. *
  395. * @return void
  396. */
  397. final public function restorePost() {
  398. global $post, $more;
  399. $more = $this->saved['more'];
  400. $post = $this->saved['post'];
  401. if ( $post ) {
  402. setup_postdata($post);
  403. }
  404. }
  405. /**
  406. * Checks if the plugin is compatible with the current WordPress version.
  407. * If it's not compatible, sets an admin warning.
  408. *
  409. * @return boolean Plugin is compatible with this WordPress version or not.
  410. */
  411. final private function isCompatible() {
  412. global $wp_version;
  413. if ( version_compare($wp_version, $this->p_data['Requires'] , '>=') ) {
  414. return true;
  415. } else {
  416. add_action('admin_notices', array($this, '_compatibleWarning'));
  417. return false;
  418. }
  419. }
  420. /**
  421. * Shows a warning message when the plugin is not compatible with current WordPress version.
  422. * This is used by calling the action 'admin_notices' in isCompatible()
  423. *
  424. * @hook action admin_notices
  425. * @access private
  426. * @return void
  427. */
  428. final function _compatibleWarning() {
  429. $this->loadTranslations(); // We have not loaded translations yet.
  430. echo '<div class="error"><p><strong>' . __('Warning:', $this->ID) . '</strong> '
  431. . sprintf(__('The active plugin %s is not compatible with your WordPress version.', $this->ID),
  432. '&laquo;' . $this->p_data['Name'] . ' ' . $this->p_data['Version'] . '&raquo;')
  433. . '</p><p>' . sprintf(__('WordPress %s is required to run this plugin.', $this->ID), $this->p_data['Requires'])
  434. . '</p></div>';
  435. }
  436. /**
  437. * Checks if standard functions for Widgets are present.
  438. * If them are not present, we are not using the standard sidebar: an admin warning is set.
  439. *
  440. * @return boolean Standard widget functions were found ot not.
  441. */
  442. final private function isStandardSidebar() {
  443. if ( class_exists('WP_Widget') &&
  444. function_exists('register_widget') &&
  445. function_exists('unregister_widget') )
  446. {
  447. return true;
  448. } else {
  449. add_action('admin_notices', array($this, '_standardSidebarWarning'));
  450. return false;
  451. }
  452. }
  453. /**
  454. * Shows an admin warning when not using the WordPress standard sidebar.
  455. * This is done by calling the action 'admin_notices' in isStandardSidebar()
  456. *
  457. * @hook action admin_notices
  458. * @access private
  459. * @return void
  460. */
  461. final function _standardSidebarWarning() {
  462. $this->loadTranslations(); // We have not loaded translations yet.
  463. echo '<div class="error"><p><strong>' . __('Warning:', $this->ID) . '</strong> '
  464. . __('Standard sidebar functions are not present.', $this->ID) . '</p><p>'
  465. . sprintf(__('It is required to use the standard sidebar to run %s', $this->ID),
  466. '&laquo;' . $this->p_data['Name'] . ' ' . $this->p_data['Version'] . '&raquo;')
  467. . '</p></div>';
  468. }
  469. /**
  470. * Loads the plugin paths based on the plugin main file.
  471. * Paths are set as $this->p_dirs.
  472. *
  473. * @see spostsPlugin::p_dirs
  474. * @return void
  475. */
  476. final private function loadPaths() {
  477. $this->p_dirs['path'] = dirname($this->p_file) .'/';
  478. $this->p_dirs['subdir'] = basename($this->p_dirs['path']);
  479. $this->p_dirs['url'] = WP_PLUGIN_URL . '/' . $this->p_dirs['subdir'] .'/';
  480. }
  481. }