SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Helpers/Register.php

  1. <?php
  2. /*
  3.  * Copyright (C) 2017 Karmabunny Pty Ltd.
  4.  *
  5.  * This file is a part of SproutCMS.
  6.  *
  7.  * SproutCMS is free software: you can redistribute it and/or modify it under the terms
  8.  * of the GNU General Public License as published by the Free Software Foundation, either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * For more information, visit <http://getsproutcms.com>.
  12.  */
  13.  
  14. namespace Sprout\Helpers;
  15.  
  16. use Kohana;
  17. use Exception;
  18. use InvalidArgumentException;
  19.  
  20.  
  21.  
  22. /**
  23. * This will one day have methods for various different aspects of the cms
  24. * incl. widgets, search handlers, tabs, etc.
  25. * Dare to dream
  26. **/
  27. class Register
  28. {
  29. private static $moderators = array();
  30. private static $extra_pages = array();
  31. private static $pageattrs = array();
  32. private static $linkspecs = array();
  33. private static $docimports = array();
  34. private static $rtelibraries = array();
  35. private static $sitemap_generators = [];
  36. private static $emailtexts = array();
  37. private static $modules = [];
  38. private static $admin_controllers = [];
  39. private static $admin_tiles = [];
  40. private static $widget_tiles = [];
  41. private static $front_end_controllers = [];
  42. private static $features = [];
  43. private static $content_replace_chains = [];
  44. private static $cron_jobs = [];
  45. private static $display_conditions = [];
  46. private static $search_handlers = [];
  47. private static $dbtool_apis = [];
  48.  
  49.  
  50. /**
  51.   * Register a moderation class
  52.   **/
  53. public static function moderator($class_name)
  54. {
  55. self::$moderators[] = $class_name;
  56. }
  57.  
  58. /**
  59.   * Get all moderation classes
  60.   **/
  61. public static function getModerators()
  62. {
  63. return self::$moderators;
  64. }
  65.  
  66.  
  67.  
  68. /**
  69.   * Register a type for an extra page
  70.   **/
  71. public static function extraPage($name, $label)
  72. {
  73. self::$extra_pages[$name] = $label;
  74. }
  75.  
  76. /**
  77.   * Get all extra pages
  78.   **/
  79. public static function getExtraPages()
  80. {
  81. return self::$extra_pages;
  82. }
  83.  
  84.  
  85. /**
  86.   * Register a type for an extra page
  87.   * @param string $name Name of the attribute (lowercase, no spaces), e.g 'sprout.lang'
  88.   * @param string $label Human-readable label for the attribute, e.g. 'Language'
  89.   * @param string $editor Class name of the AttrEditor used to edit the attribute. If not specified using a
  90.   * namespace, the 'Sprout\Helpers' namespace is assumed.
  91.   **/
  92. public static function pageattr($name, $label, $editor = 'Sprout\\Helpers\\AttrEditorTextbox')
  93. {
  94. if (strpos($editor, '\\') === false) $editor = "Sprout\\Helpers\\{$editor}";
  95.  
  96. self::$pageattrs[$name] = array($label, $editor);
  97. }
  98.  
  99. /**
  100.   * Get all extra pages
  101.   **/
  102. public static function getPageattrs()
  103. {
  104. return self::$pageattrs;
  105. }
  106.  
  107.  
  108. /**
  109.   * Register a front-end controller (also called a tool page)
  110.   * @param string $name Fully namespaced class name of Controller which MUST implement FrontEndEntrance,
  111.   * e.g. 'SproutModules\\Karmabunny\\Users\\Controllers\\UserController'
  112.   * @param string $label Human-readable short name, e.g. 'Users'
  113.   * @return void
  114.   * @throws InvalidArgumentException If controller doesn't implement FrontEndEntrance
  115.   */
  116. public static function frontEndController($name, $label)
  117. {
  118. $reflect = new \ReflectionClass($name);
  119. if (!$reflect->implementsInterface('Sprout\\Helpers\\FrontEndEntrance')) {
  120. throw new InvalidArgumentException($name . " doesn't implement FrontEndEntrance");
  121. }
  122. self::$front_end_controllers[$name] = $label;
  123. }
  124.  
  125. /**
  126.   * Get all front-end controllers
  127.   **/
  128. public static function getFrontEndControllers()
  129. {
  130. return self::$front_end_controllers;
  131. }
  132.  
  133.  
  134. /**
  135.   * Register a LinkSpec
  136.   **/
  137. public static function linkspec($name, $label)
  138. {
  139. self::$linkspecs[$name] = $label;
  140. }
  141.  
  142. /**
  143.   * Get all `LinkSpec`s
  144.   **/
  145. public static function getLinkspecs()
  146. {
  147. return self::$linkspecs;
  148. }
  149.  
  150.  
  151. /**
  152.   * Register a DocImport class
  153.   **/
  154. public static function docImport($ext, $class, $label)
  155. {
  156. self::$docimports[$ext] = array($class, $label);
  157. }
  158.  
  159. /**
  160.   * Get all `DocImport`s
  161.   **/
  162. public static function getDocImports()
  163. {
  164. return self::$docimports;
  165. }
  166.  
  167.  
  168. /**
  169.   * Register a RteLibrary class
  170.   **/
  171. public static function rteLibrary($class_name)
  172. {
  173. self::$rtelibraries[] = $class_name;
  174. }
  175.  
  176. /**
  177.   * Get all `RteLibrary`s
  178.   **/
  179. public static function getRteLibraries()
  180. {
  181. return self::$rtelibraries;
  182. }
  183.  
  184.  
  185. /**
  186.   * Register a sitemap.xml generator class
  187.   */
  188. public static function sitemapGen($class_name)
  189. {
  190. self::$sitemap_generators[] = $class_name;
  191. }
  192.  
  193. /**
  194.   * Get a list of all sitemap.xml generator classes
  195.   */
  196. public static function getSitemapGens()
  197. {
  198. return self::$sitemap_generators;
  199. }
  200.  
  201.  
  202. /**
  203.   * Register an email text
  204.   *
  205.   * @param string $code The template code
  206.   * e.g. user.welcome
  207.   *
  208.   * @param array $field_defs An array of name => description field definitions, for the admin
  209.   * e.g. array('first_name' => 'First name of the new user)
  210.   *
  211.   * @param string $default_html_view The view name for the default text. Must be a .htm view
  212.   * e.g 'email/user_welcome'
  213.   **/
  214. public static function emailText($code, array $field_defs, $default_html_view)
  215. {
  216. self::$emailtexts[$code] = new EmailTextReg($field_defs, $default_html_view);
  217. }
  218.  
  219. /**
  220.   * Get all registered email texts. Returns an array of EmailTextReg objects
  221.   **/
  222. public static function getEmailTexts()
  223. {
  224. return self::$emailtexts;
  225. }
  226.  
  227. /**
  228.   * Get a single registered email texts. Returns an EmailTextReg object
  229.   **/
  230. public static function getEmailText($code)
  231. {
  232. return self::$emailtexts[$code];
  233. }
  234.  
  235.  
  236. /**
  237.   * Registers a list of modules
  238.   * @param array $names The names of the modules, e.g. ['HomePage', 'Users']
  239.   * @return void
  240.   */
  241. public static function modules(array $names)
  242. {
  243. foreach ($names as $name) {
  244. self::module($name);
  245. }
  246. }
  247.  
  248. /**
  249.   * Registers a module
  250.   * @param string $name The name of the module, e.g. 'home_page'
  251.   * @return void
  252.   */
  253. public static function module($name)
  254. {
  255. if (!preg_match('/^[-_a-z0-9]+$/i', $name)) {
  256. throw new Exception('Invalid module name');
  257. }
  258. if (in_array($name, self::$modules)) return;
  259. self::$modules[] = $name;
  260. }
  261.  
  262. /**
  263.   * Gets the list of active modules
  264.   * @return array
  265.   */
  266. public static function getModules()
  267. {
  268. return self::$modules;
  269. }
  270.  
  271. /**
  272.   * Gets a list of paths to the active modules
  273.   * @return array
  274.   */
  275. public static function getModuleDirs()
  276. {
  277. $dirs = [];
  278. foreach (self::$modules as $module) {
  279. $dirs[] = DOCROOT . 'modules/' . $module;
  280. }
  281. return $dirs;
  282. }
  283.  
  284.  
  285. /**
  286.   * Register the various "content" controllers which are provided by the SproutCMS core
  287.   * This will allow these controllers to have permissions (e.g. per-tab or per-record)
  288.   */
  289. public static function coreContentControllers()
  290. {
  291. self::$admin_controllers['document_type'] = '\\Sprout\\Controllers\\Admin\\DocumentTypeAdminController';
  292. self::$admin_controllers['email_text'] = '\\Sprout\\Controllers\\Admin\\EmailTextAdminController';
  293. self::$admin_controllers['extra_page'] = '\\Sprout\\Controllers\\Admin\\ExtraPageAdminController';
  294. self::$admin_controllers['file'] = '\\Sprout\\Controllers\\Admin\\FileAdminController';
  295. self::$admin_controllers['page'] = '\\Sprout\\Controllers\\Admin\\PageAdminController';
  296. self::$admin_controllers['redirect'] = '\\Sprout\\Controllers\\Admin\\RedirectAdminController';
  297. }
  298.  
  299.  
  300. /**
  301.   * Registers a module's shorthand controller names for the admin controller
  302.   * @param string $namespace namespace including both developer and module
  303.   * name but not 'Controllers' segment, e.g. Karmabunny\HomePage\Admin
  304.   * @param array $controllers map of lowercased shorthand names to class
  305.   * names within the specified namespace, e.g. ['home' => 'Admin\HomePageAdminController']
  306.   */
  307. public static function adminControllers($namespace, array $controllers)
  308. {
  309. if (strpos($namespace, '\\') === false) {
  310. throw new Exception('Invalid namespace');
  311. }
  312.  
  313. foreach ($controllers as $shorthand => $class) {
  314. if (isset(self::$admin_controllers[$shorthand])) {
  315. throw new Exception("Duplicate shorthand: {$shorthand}");
  316. }
  317. $full_class = "SproutModules\\{$namespace}\\Controllers\\{$class}";
  318. self::$admin_controllers[$shorthand] = $full_class;
  319. }
  320. }
  321.  
  322. /**
  323.   * Converts an shorthand admin controller name to its full class name,
  324.   * including modular namespace
  325.   * @param string $shorthand
  326.   * @return string
  327.   */
  328. public static function getAdminController($shorthand)
  329. {
  330. if (!isset(self::$admin_controllers[$shorthand])) {
  331. throw new Exception("Unrecognised shorthand: {$shorthand}");
  332. }
  333. return self::$admin_controllers[$shorthand];
  334. }
  335.  
  336. /**
  337.   * Gets the list of modular admin controllers with registered shorthands
  338.   * @return array shorthand => full class name
  339.   */
  340. public static function getAdminControllers()
  341. {
  342. return self::$admin_controllers;
  343. }
  344.  
  345.  
  346. /**
  347.   * Register a "tile", which is shown in the modules menu in the admin
  348.   *
  349.   * A list of available icons can be found in the file {@see sprout/media/fonts/iconfont/demo.html}
  350.   *
  351.   * @param string $name The name (title) of the tile
  352.   * @param string $icon The icon class, sans the 'icon-' part. Examples: 'list', 'grid', 'home', etc.
  353.   * @param string $text Explanation text. Plaintext.
  354.   * @param array $controllers Controllers to show as links, in format 'shorthand' => 'label'
  355.   * @param int $sort_order Tiles are sorted by the sort order, then by the name alphabetically. Lower = earlier.
  356.   */
  357. public static function adminTile($name, $icon, $text, array $controllers, $sort_order = 10)
  358. {
  359. $hidden = \Kohana::config('sprout.admin_tile_hidden');
  360. if (AdminAuth::isSuper()) {
  361. foreach ($hidden as $shorthand) {
  362. if (isset($controllers[$shorthand])) $controllers[$shorthand] .= ' [hidden]';
  363. }
  364. } else {
  365. foreach ($hidden as $shorthand) {
  366. unset($controllers[$shorthand]);
  367. }
  368. }
  369.  
  370. if (count($controllers) === 0) return;
  371.  
  372. self::$admin_tiles[$sort_order . $name] = [
  373. 'name' => $name,
  374. 'icon' => $icon,
  375. 'text' => $text,
  376. 'controllers' => $controllers,
  377. ];
  378. }
  379.  
  380.  
  381. /**
  382.   * Return an array of admin tiles.
  383.   * Each tile has four keys, 'name', 'icon', 'text', and 'controllers'.
  384.   *
  385.   * @return array
  386.   */
  387. public static function getAdminTiles()
  388. {
  389. return self::$admin_tiles;
  390. }
  391.  
  392.  
  393. /**
  394.   * Register a widget "tile", which is shown when adding widgets (content blocks) to a page
  395.   *
  396.   * If a tile already exists with that name, the widgets will be added to that tile
  397.   *
  398.   * A list of available icons can be found in the file {@see sprout/media/fonts/iconfont/demo.html}
  399.   *
  400.   * @param string $area_name The area to register the tile (e.g. 'embedded')
  401.   * @param string $name The name (title) of the tile
  402.   * @param string $icon The icon class, sans the 'icon-' part. Examples: 'list', 'grid', 'home', etc.
  403.   * @param string $text Explanation text. Plaintext.
  404.   * @param array $widgets Widgets to show as links, in format 'name' => 'label'
  405.   */
  406. public static function widgetTile($area_name, $name, $icon, $text, array $widgets)
  407. {
  408. $area = WidgetArea::findAreaByName($area_name);
  409. if (!$area) return;
  410.  
  411. if (!isset(self::$widget_tiles[$area_name])) {
  412. self::$widget_tiles[$area_name] = [];
  413. }
  414.  
  415. if (isset(self::$widget_tiles[$area_name][$name])) {
  416. self::$widget_tiles[$area_name][$name]['widgets'] = array_merge(
  417. self::$widget_tiles[$area_name][$name]['widgets'],
  418. $widgets
  419. );
  420. } else {
  421. self::$widget_tiles[$area_name][$name] = [
  422. 'name' => $name,
  423. 'icon' => $icon,
  424. 'text' => $text,
  425. 'widgets' => $widgets,
  426. ];
  427. }
  428.  
  429. foreach ($widgets as $name => $label) {
  430. $area->addWidget($name);
  431. }
  432. }
  433.  
  434.  
  435. /**
  436.   * Return an array of widget tiles for a given widget area
  437.   * Each tile has four keys, 'name', 'icon', 'text', and 'widgets'.
  438.   *
  439.   * @return array
  440.   */
  441. public static function getWidgetTiles($area_name)
  442. {
  443. if (!isset(self::$widget_tiles[$area_name])) {
  444. self::$widget_tiles[$area_name] = [];
  445. }
  446.  
  447. return self::$widget_tiles[$area_name];
  448. }
  449.  
  450.  
  451. /**
  452.   * Register an available feature, and the root namespace where the helpers are found
  453.   *
  454.   * Features allow the Sprout core to access code provided by modules without needing
  455.   * to hard-code the namespace of these modules
  456.   *
  457.   * @example
  458.   * Register::feature('users', 'SproutModules\Karmabunny\Users');
  459.   *
  460.   * @param string $code The feature code. Only one in use at this time, 'users'
  461.   * @param string $namespace The root namespace (without 'Helpers\' part) of the feature
  462.   */
  463. public static function feature($code, $namespace)
  464. {
  465. self::$features[$code] = $namespace;
  466. }
  467.  
  468.  
  469. /**
  470.   * Is the given feature currently available?
  471.   *
  472.   * @param string $code The feature code. Only one in use at this time, 'users'
  473.   * @return bool True if the feature is available
  474.   */
  475. public static function hasFeature($code)
  476. {
  477. return isset(self::$features[$code]);
  478. }
  479.  
  480.  
  481. /**
  482.   * Return the namespace for a given feature
  483.   *
  484.   * @param string $code The feature code. Only one in use at this time, 'users'
  485.   * @return string Namespace
  486.   */
  487. public static function getFeatureNamespace($code)
  488. {
  489. return self::$features[$code];
  490. }
  491.  
  492.  
  493. /**
  494.   * Register a method for a content replacement chain
  495.   *
  496.   * Registered method will receive one string argument and should return a string.
  497.   * For the 'inner_html' chain, the input and output should be HTML
  498.   *
  499.   * Common chains:
  500.   * inner_html The "inside" HTML of widgets (richtext, blog content, etc)
  501.   * main_content The main content of inner and wide templates
  502.   *
  503.   * @example
  504.   * // register method
  505.   * Register::contentReplace('inner_html', ['Sprout\\Helpers\\ContentReplace', 'intlinks']);
  506.   *
  507.   * // execute chain
  508.   * $new_html = ContentReplace::executeChain('inner_html', $old_html);
  509.   *
  510.   * @param string $chain The chain to register the method for, e.g. 'inner_html'
  511.   * @param string callable $func The method to register
  512.   */
  513. public static function contentReplace($chain, callable $func)
  514. {
  515. if (!isset(self::$content_replace_chains[$chain])) {
  516. self::$content_replace_chains[$chain] = [];
  517. }
  518. self::$content_replace_chains[$chain][] = $func;
  519. }
  520.  
  521.  
  522. /**
  523.   * Return the methods for a given content replace chain
  524.   *
  525.   * @param string $chain The chain to return, e.g. 'inner_html'
  526.   * @return array Callables
  527.   */
  528. public static function getContentReplaceMethods($chain)
  529. {
  530. return self::$content_replace_chains[$chain];
  531. }
  532.  
  533.  
  534. /**
  535.   * Register a method to be called for cron job processing
  536.   *
  537.   * By default there is only one schedule, 'daily', but it's easy to add
  538.   * another schedule by duplicating the cron_daily.sh file and making the new
  539.   * file call the new schedule
  540.   *
  541.   * @example
  542.   * Register::cronJob('daily', 'Sprout\\Controllers\\Admin\\PageAdminController', 'cronPageActivate');
  543.   *
  544.   * @param string $schedule The schedule for the job; corresponds to call in the shell script
  545.   * e.g. 'daily' will be called by the request 'cron_job/run/daily'
  546.   * @param string $class The class containing the cron job method. Must be a controller
  547.   * @param string $func The funciton name to call
  548.   */
  549. public static function cronJob($schedule, $class, $func)
  550. {
  551. if (!isset(self::$cron_jobs[$schedule])) {
  552. self::$cron_jobs[$schedule] = [];
  553. }
  554. self::$cron_jobs[$schedule][] = [$class, $func];
  555. }
  556.  
  557.  
  558. /**
  559.   * Return the methods for a given cron job schedule
  560.   *
  561.   * @param string $schedule The jobs to return, e.g. 'daily' or 'weekly'
  562.   * @return array Each row has [0] => class, [1] => func
  563.   */
  564. public static function getCronJobs($schedule)
  565. {
  566. return @self::$cron_jobs[$schedule] ?: [];
  567. }
  568.  
  569.  
  570. /**
  571.   * Return all registered cron jobs
  572.   *
  573.   * @return array Key is schedule, and array is a list of jobs like {@see Register::getCronJobs}
  574.   */
  575. public static function getAllCronJobs()
  576. {
  577. return self::$cron_jobs;
  578. }
  579.  
  580.  
  581. /**
  582.   * Register an helper to expose to twig templates via the 'sprout' namespace.
  583.   *
  584.   * @param string $name
  585.   * @param mixed $item
  586.   * @return void
  587.   */
  588. public static function templateVariable($name, $item)
  589. {
  590. SproutVariable::register($name, $item);
  591. }
  592.  
  593.  
  594. /**
  595.   * Register a display condition (this is part of the Context Engine on widgets)
  596.   *
  597.   * Condition classes must extend the base class {@see Sprout\Helpers\DisplayConditions\DisplayCondition}
  598.   *
  599.   * @param string $class Class name, including namespace
  600.   * @param string $group Label of the group, e.g. 'Platform'
  601.   * @param string $label Label of the condtion, e.g. 'Device category'
  602.   */
  603. public static function displayCondition($class, $group, $label)
  604. {
  605. if (empty(self::$display_conditions[$group])) {
  606. self::$display_conditions[$group] = [];
  607. }
  608.  
  609. self::$display_conditions[$group][$class] = $label;
  610. }
  611.  
  612.  
  613. /**
  614.   * Return a list of all display condition registrations
  615.   *
  616.   * @return array Key is group label, value is array of conditions [ class => label ]
  617.   */
  618. public static function getDisplayConditions()
  619. {
  620. return self::$display_conditions;
  621. }
  622.  
  623.  
  624. /**
  625.   * Register search handler
  626.   *
  627.   * @param string $class Controller to register which implements the
  628.   * FrontEndSearch interface. Must be fully namespaced.
  629.   * @param string $table The name of the keywords table, e.g. page_keywords
  630.   * @param array $where Optional list of where clauses @see SearchHandler->addWhere()
  631.   * @return void
  632.   */
  633. public static function searchHandler($class, $table, $where = [])
  634. {
  635. $handler = new SearchHandler($table, $class);
  636.  
  637. if (!empty($where) and count($where) > 0) {
  638. foreach ($where as $clause) {
  639. $handler->addWhere($clause);
  640. }
  641. }
  642.  
  643. self::$search_handlers[] = $handler;
  644. }
  645.  
  646.  
  647. /**
  648.   * Return list of SearchHandler objects
  649.   *
  650.   * @return array List of SearchHandler instances
  651.   */
  652. public static function getSearchHandlers()
  653. {
  654. $handlers = self::$search_handlers;
  655.  
  656. $conf = Kohana::config('sprout.search_handlers');
  657. if (is_array($conf)) {
  658. $handlers = array_merge($handlers, $conf);
  659. }
  660.  
  661. return $handlers;
  662. }
  663.  
  664.  
  665. /**
  666.   * Register an API test form
  667.   *
  668.   * @param array $api [
  669.   * title => (string) API name,
  670.   * desc => (string) subtitle,
  671.   * class => (string) => class name,
  672.   * method => (string) class method to load form
  673.   * ]
  674.   * @return void
  675.   */
  676. public static function addDbtoolsApi($api)
  677. {
  678. self::$dbtool_apis[] = $api;
  679. }
  680.  
  681.  
  682. /**
  683.   * Return list of dbtool api tests
  684.   * @return array
  685.   */
  686. public static function getDbtoolsApi()
  687. {
  688. usort(self::$dbtool_apis, function ($a, $b)
  689. {
  690. return strcmp($a['title'], $b['title']);
  691. });
  692. return self::$dbtool_apis;
  693. }
  694. }
  695.