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

/oowp.php

https://gitlab.com/outlandishideas/oowp
PHP | 470 lines | 297 code | 51 blank | 122 comment | 64 complexity | 6961f16e985ebedc8cf95dc775442161 MD5 | raw file
Possible License(s): GPL-3.0
  1. <?php
  2. /*
  3. Plugin Name: Object-oriented WordPress (OOWP)
  4. Plugin URI: https://github.com/outlandishideas/oowp
  5. Description: OOWP is a tool for WordPress theme developers that makes templating in WordPress more sensible. It replaces [The Loop](https://codex.wordpress.org/The_Loop) and contextless functions such as the_title() with object-oriented methods such as $event->title(), $event->parent() and $event->getConnected('people').
  6. Version: 0.9
  7. */
  8. require_once __DIR__ . '/classes/ooPost.class.php';
  9. require_once __DIR__ . '/classes/ooRoutemasterPost.class.php';
  10. require_once __DIR__ . '/classes/ooTheme.class.php';
  11. require_once __DIR__ . '/classes/ooWP_Query.class.php';
  12. $_registeredPostClasses = array();
  13. $_registeredConnections = array();
  14. $_knownOowpClasses = array();
  15. // include all matching classes in ./classes and [current theme]/classes directories,
  16. // and register any subclasses of ooPost using their static register() function
  17. add_action('init', '_oowp_init');
  18. function _oowp_init()
  19. {
  20. // initialise the oowp classes.
  21. oowp_initialiseClasses(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'classes');
  22. // initialise the classes in the theme.
  23. // use an autoloader to prevent dependency problems.
  24. $dir = get_stylesheet_directory() . DIRECTORY_SEPARATOR . 'classes';
  25. $autoloader = function($class) use ($dir) {
  26. $file = "$dir/$class.class.php";
  27. if (file_exists($file)) {
  28. require_once($file);
  29. }
  30. };
  31. spl_autoload_register($autoloader);
  32. oowp_initialiseClasses($dir);
  33. spl_autoload_unregister($autoloader);
  34. // call postRegistration on all registered post types, for e.g. creating p2p connections
  35. global $_registeredPostClasses;
  36. foreach ($_registeredPostClasses as $class) {
  37. $class::bruv();
  38. }
  39. // set up a singleton for the theme
  40. global $_knownOowpClasses;
  41. $themeClass = 'ooTheme';
  42. foreach ($_knownOowpClasses as $class) {
  43. if (is_subclass_of($class, 'ooTheme')) {
  44. $themeClass = $class;
  45. }
  46. }
  47. if (class_exists($themeClass)) {
  48. $oowpTheme = $themeClass::getInstance();
  49. $oowpTheme->init();
  50. do_action('oowp_theme_init', $oowpTheme);
  51. }
  52. // wordpress 3.5 makes unregister_post_type cause errors later on, so just hide the item in the menu instead
  53. // unregister_post_type('post');
  54. add_action('admin_menu', function() {
  55. remove_menu_page('edit.php');
  56. });
  57. // unregister_taxonomy('category');
  58. // unregister_taxonomy('post_tag');
  59. if (is_admin()) {
  60. add_action('admin_head', 'oowp_add_admin_styles');
  61. wp_enqueue_script('oowp_js', plugin_dir_url(__FILE__) . 'oowp-admin.js', array('jquery'), false, true);
  62. add_action('admin_menu', 'oowp_customise_admin_menu');
  63. } else {
  64. wp_enqueue_style('oowp_css', plugin_dir_url(__FILE__) . 'oowp.css');
  65. }
  66. }
  67. function oowp_customise_admin_menu() {
  68. remove_menu_page('link-manager.php');
  69. }
  70. /**
  71. * Attempts to style each post type menu item and posts page with its own icon, as found in the theme's 'images' directory.
  72. * In order to be automatically styled, icon names should have the following forms:
  73. * - icon-{post_type} (for posts pages, next to header)
  74. * - icon-menu-{post_type} (for menu items)
  75. * - icon-menu-active-{post_type} (for menu items when active/hovered)
  76. */
  77. function oowp_add_admin_styles() {
  78. $imagesDir = get_theme_root() . DIRECTORY_SEPARATOR . get_template() . DIRECTORY_SEPARATOR . 'images';
  79. $styles = array();
  80. global $_registeredPostClasses;
  81. if (is_dir($imagesDir)) {
  82. $handle = opendir($imagesDir);
  83. while (false !== ($file = readdir($handle))) {
  84. $fullFile = $imagesDir . DIRECTORY_SEPARATOR . $file;
  85. if (is_dir($fullFile) || !filesize($fullFile)) continue;
  86. $imageSize = @getimagesize($fullFile);
  87. if (!$imageSize || !$imageSize[0] || !$imageSize[1]) continue;
  88. foreach (array_keys($_registeredPostClasses) as $postType) {
  89. if (preg_match('/icon(-menu(-active)?)?-' . $postType . '\.\w+$/', $file, $matches)) {
  90. if (!array_key_exists($postType, $styles)) {
  91. $styles[$postType] = array();
  92. }
  93. if (count($matches) == 3) {
  94. $type = 'active-menu';
  95. } else if (count($matches) == 2) {
  96. $type = 'menu';
  97. } else {
  98. $type = 'page';
  99. }
  100. $styles[$postType][$type] = $file;
  101. }
  102. }
  103. }
  104. }
  105. if ($styles) {
  106. $patterns = array(
  107. 'menu' => '#adminmenu #menu-posts-{post_type} .wp-menu-image',
  108. 'active-menu' => '#adminmenu #menu-posts-{post_type}:hover .wp-menu-image, #adminmenu #menu-posts-{post_type}.wp-has-current-submenu .wp-menu-image',
  109. 'page' => '.icon32-posts-{post_type}'
  110. );
  111. echo '<style type="text/css">';
  112. foreach ($styles as $postType=>$icons) {
  113. foreach ($patterns as $type=>$pattern) {
  114. if (isset($icons[$type])) {
  115. $pattern = preg_replace('/{post_type}/', $postType, $pattern);
  116. echo $pattern . ' {
  117. background: url(' . get_bloginfo('template_url') . '/images/' . $icons[$type] . ') no-repeat center center !important;
  118. }';
  119. }
  120. }
  121. }
  122. echo '</style>';
  123. }
  124. }
  125. /**
  126. * Requires all files found in the given directory, and calls init() on any valid classes
  127. * @param $dir
  128. */
  129. function oowp_initialiseClasses($dir)
  130. {
  131. if (!is_dir($dir)) {
  132. return;
  133. }
  134. global $_knownOowpClasses;
  135. $handle = opendir($dir);
  136. while ($file = readdir($handle)) {
  137. $fullFile = $dir . DIRECTORY_SEPARATOR . $file;
  138. if (is_dir($fullFile) && !in_array($file, array('.', '..'))) {
  139. oowp_initialiseClasses($fullFile);
  140. } else if (preg_match("/(\w+)\.class\.php/", $file, $matches)) {
  141. require_once($fullFile);
  142. $className = $matches[1];
  143. if (class_exists($className)) {
  144. $_knownOowpClasses[] = $className;
  145. //init class if is is an ooPost. Note at of PHP 5.3.9 is_a() doesn't work as expected.
  146. if ($className == 'ooPost' || is_subclass_of($className, 'ooPost')) {
  147. $className::init();
  148. }
  149. }
  150. }
  151. }
  152. }
  153. /**
  154. * Gets the class name for the given identifier (eg a post type).
  155. * Searches through the known oowp classes for one whose name is a camel-case version of the argument (ignoring the prefix)
  156. * @param $data
  157. * @param string $default
  158. * @return string
  159. */
  160. function ooGetClassName($data, $default = 'ooMiscPost')
  161. {
  162. //TODO: this seems to do the same thing as ooTheme#postClass()
  163. global $_knownOowpClasses;
  164. $reversedClasses = array_reverse($_knownOowpClasses);
  165. // generate something to look for, eg my_post_type => MyPostType
  166. $classStem = to_camel_case($data, true);
  167. foreach ($reversedClasses as $registeredClass) {
  168. // extract the stem by removing the lower case prefix, eg ooMyPostType -> MyPostType
  169. if (preg_match('/([A-Z].*)/m', $registeredClass, $matches)) {
  170. $registeredStem = $matches[1];
  171. if ($classStem == $registeredStem) {
  172. return $registeredClass;
  173. }
  174. }
  175. }
  176. return $default;
  177. }
  178. function oofp($data, $title = null)
  179. {
  180. if (class_exists('FirePHP')) {
  181. FirePHP::getInstance(true)->log($data, $title);
  182. }
  183. }
  184. /**
  185. * Translates a camel case string into a string with underscores (e.g. firstName -> first_name)
  186. * @param string $str String in camel case format
  187. * @return string $str Translated into underscore format
  188. */
  189. function from_camel_case($str)
  190. {
  191. $str[0] = strtolower($str[0]);
  192. $func = create_function('$c', 'return "_" . strtolower($c[1]);');
  193. return preg_replace_callback('/([A-Z])/', $func, $str);
  194. }
  195. /**
  196. * Translates a string with underscores into camel case (e.g. first_name -> firstName)
  197. * @param string $str String in underscore format
  198. * @param bool $capitalise_first_char If true, capitalise the first char in $str
  199. * @return string $str translated into camel caps
  200. */
  201. function to_camel_case($str, $capitalise_first_char = false)
  202. {
  203. if ($capitalise_first_char) {
  204. $str[0] = strtoupper($str[0]);
  205. }
  206. $func = create_function('$c', 'return strtoupper($c[1]);');
  207. return preg_replace_callback('/_([a-z])/', $func, $str);
  208. }
  209. if (!function_exists('unregister_post_type')) :
  210. function unregister_post_type($post_type)
  211. {
  212. global $wp_post_types;
  213. if (isset($wp_post_types[$post_type])) {
  214. unset($wp_post_types[$post_type]);
  215. add_action('admin_menu', function() use ($post_type) {
  216. remove_menu_page('edit.php' . ($post_type == 'post' ? "" : "?post_type=$post_type"));
  217. }, $post_type);
  218. return true;
  219. }
  220. return false;
  221. }
  222. endif;
  223. /**
  224. * Reverse the effects of register_taxonomy()
  225. *
  226. * @package WordPress
  227. * @subpackage Taxonomy
  228. * @since 3.0
  229. * @uses $wp_taxonomies Modifies taxonomy object
  230. *
  231. * @param string $taxonomy Name of taxonomy object
  232. * @param array|string $object_type Name of the object type
  233. * @return bool True if successful, false if not
  234. */
  235. function unregister_taxonomy($taxonomy, $object_type = '')
  236. {
  237. global $wp_taxonomies;
  238. if (!isset($wp_taxonomies[$taxonomy]))
  239. return false;
  240. if (!empty($object_type)) {
  241. $i = array_search($object_type, $wp_taxonomies[$taxonomy]->object_type);
  242. if (false !== $i)
  243. unset($wp_taxonomies[$taxonomy]->object_type[$i]);
  244. if (empty($wp_taxonomies[$taxonomy]->object_type))
  245. unset($wp_taxonomies[$taxonomy]);
  246. } else {
  247. unset($wp_taxonomies[$taxonomy]);
  248. }
  249. return true;
  250. }
  251. /**
  252. * Inserts the (key, value) pair into the array, after the given key. If the given key is not found,
  253. * it is inserted at the end
  254. * @param $array
  255. * @param $afterKey
  256. * @param $key
  257. * @param $value
  258. * @return array
  259. */
  260. function array_insert_after($array, $afterKey, $key, $value) {
  261. if (array_key_exists($afterKey, $array)) {
  262. $output = array();
  263. foreach ($array as $a=>$b) {
  264. $output[$a] = $b;
  265. if ($a == $afterKey) {
  266. $output[$key] = $value;
  267. }
  268. }
  269. return $output;
  270. } else {
  271. $array[$key] = $value;
  272. return $array;
  273. }
  274. }
  275. /**
  276. * Inserts the (key, value) pair into the array, before the given key. If the given key is not found,
  277. * it is inserted at the beginning
  278. * @param $array
  279. * @param $beforeKey
  280. * @param $key
  281. * @param $value
  282. * @return array
  283. */
  284. function array_insert_before($array, $beforeKey, $key, $value) {
  285. $output = array();
  286. if (array_key_exists($beforeKey, $array)) {
  287. foreach ($array as $a=>$b) {
  288. if ($a == $beforeKey) {
  289. $output[$key] = $value;
  290. }
  291. $output[$a] = $b;
  292. }
  293. } else {
  294. $output[$key] = $value;
  295. foreach ($array as $a=>$b) {
  296. $output[$a] = $b;
  297. }
  298. }
  299. return $output;
  300. }
  301. function oowp_generate_labels($singular, $plural = null) {
  302. if (!$plural) {
  303. $plural = $singular . 's';
  304. }
  305. return array(
  306. 'name' => $plural,
  307. 'singular_name' => $singular,
  308. 'add_new' => 'Add New',
  309. 'add_new_item' => 'Add New ' . $singular,
  310. 'edit_item' => 'Edit ' . $singular,
  311. 'new_item' => 'New ' . $singular,
  312. 'all_items' => 'All ' . $plural,
  313. 'view_item' => 'View ' . $singular,
  314. 'search_items' => 'Search ' . $plural,
  315. 'not_found' => 'No ' . $plural . ' found',
  316. 'not_found_in_trash' => 'No ' . $plural . ' found in Trash',
  317. 'parent_item_colon' => 'Parent ' . $singular . ':',
  318. 'menu_name' => $plural
  319. );
  320. }
  321. function oowp_print_right_now_count($count, $postType, $singular, $plural, $status = null) {
  322. if (get_post_type_object($postType)->show_ui) {
  323. $num = number_format_i18n($count);
  324. $text = _n($singular, $plural, intval($count) );
  325. if ( current_user_can( 'edit_posts' )) {
  326. $link = 'edit.php?post_type=' . $postType;
  327. if ($status) {
  328. $link .= '&post_status='.$status;
  329. }
  330. $num = "<a href='$link'>$num</a>";
  331. $text = "<a href='$link'>$text</a>";
  332. }
  333. echo '<tr>';
  334. echo '<td class="first b b-' . $postType . '">' . $num . '</td>';
  335. echo '<td class="t ' . $postType . '">' . $text . '</td>';
  336. echo '</tr>';
  337. }
  338. }
  339. /**
  340. * @return string The full path of the wrapped template
  341. */
  342. function oowp_layout_template_file() {
  343. return OOWP_Layout::$innerTemplate;
  344. }
  345. /**
  346. * @return string The name of the wrapped template
  347. */
  348. function oowp_layout_template_name() {
  349. return OOWP_Layout::$templateName;
  350. }
  351. /**
  352. * This wraps all requested templates in a layout.
  353. *
  354. * Create layout.php in your root theme directory to use the same layout on all pages.
  355. * Create layout-{template}.php for specific versions
  356. *
  357. * Example layout: include header, sidebar and footer on all pages, and wrap standard template in a section and a div
  358. *
  359. * <?php get_header( oowp_layout_template_name() ); ?>
  360. *
  361. * <section id="primary">
  362. * <div id="content" role="main">
  363. * <?php include oowp_layout_template_file(); ?>
  364. * </div><!-- #content -->
  365. * </section><!-- #primary -->
  366. *
  367. * <?php get_sidebar( oowp_layout_template_name() ); ?>
  368. * <?php get_footer( oowp_layout_template_name() ); ?>
  369. *
  370. * See http://scribu.net/wordpress/theme-wrappers.html
  371. */
  372. add_filter( 'template_include', array( 'OOWP_Layout', 'wrap' ), 99 );
  373. class OOWP_Layout {
  374. /**
  375. * Stores the full path to the main template file
  376. */
  377. static $innerTemplate = null;
  378. /**
  379. * Stores the base name of the template file; e.g. 'page' for 'page.php' etc.
  380. */
  381. static $templateName = null;
  382. static function wrap( $template ) {
  383. self::$innerTemplate = $template;
  384. self::$templateName = substr( basename( self::$innerTemplate ), 0, -4 );
  385. $templates = array( 'layout.php' );
  386. if ( 'index' == self::$templateName ) {
  387. self::$templateName = null;
  388. } else {
  389. // prepend the more specific wrapper filename
  390. array_unshift( $templates, sprintf( 'layout-%s.php', self::$templateName ) );
  391. }
  392. // revert to the template passed in if no layout template is found
  393. return locate_template( $templates ) ?: $template;
  394. }
  395. }
  396. /**
  397. * Shortcode that allows access to the basic FetchAll functionality through the CMS
  398. * Example: [listContent type='event' posts_per_page=3]
  399. * @param $params
  400. * @param $content
  401. */
  402. function oowp_fetchAll_shortcode($params, $content) {
  403. $postType = $params['type']; //what kind of post are we querying
  404. unset($params['type']); //don't need this any more
  405. global $_registeredPostClasses;
  406. if(!array_key_exists($postType, $_registeredPostClasses)){
  407. if(WP_DEBUG) die('OOWP shortcode error: unknown post-type ('.$postType.')');
  408. else return;
  409. }
  410. //ok - we know it's a valid post type
  411. $className = $_registeredPostClasses[$postType];
  412. $query = $className::fetchAll($params);
  413. if($query){
  414. foreach($query as $post){
  415. $post->printItem();
  416. }
  417. }
  418. }
  419. add_shortcode('listContent', 'oowp_fetchAll_shortcode');