PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/textpattern/lib/txplib_theme.php

https://github.com/briefwriter/textpattern
PHP | 392 lines | 168 code | 70 blank | 154 comment | 34 complexity | f4d2703164bc7f2a4d7ecab26ef99254 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  1. <?php
  2. /*
  3. * Textpattern Content Management System
  4. * http://textpattern.com
  5. *
  6. * Copyright (C) 2014 The Textpattern Development Team
  7. *
  8. * This file is part of Textpattern.
  9. *
  10. * Textpattern is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation, version 2.
  13. *
  14. * Textpattern is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with Textpattern. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. /**
  23. * Base for admin-side themes.
  24. *
  25. * @package Theme
  26. */
  27. if (!defined('THEME')) {
  28. /**
  29. * Relative path to themes directory.
  30. */
  31. define('THEME', 'theme/');
  32. }
  33. /**
  34. * Admin-side theme.
  35. *
  36. * @package Theme
  37. */
  38. class theme
  39. {
  40. /**
  41. * The theme name.
  42. *
  43. * @var string
  44. */
  45. public $name;
  46. /**
  47. * Stores a menu.
  48. *
  49. * @var array
  50. */
  51. public $menu;
  52. /**
  53. * Theme location.
  54. *
  55. * @var string
  56. */
  57. public $url;
  58. /**
  59. * Just a popup window.
  60. *
  61. * @var bool
  62. */
  63. public $is_popup;
  64. /**
  65. * Stores an activity message.
  66. *
  67. * @var bool
  68. * @see theme::announce()
  69. * @see theme::announce_async()
  70. */
  71. public $message;
  72. /**
  73. * Constructor.
  74. *
  75. * @param string $name Theme name
  76. */
  77. public function __construct($name)
  78. {
  79. $this->name = $name;
  80. $this->menu = array();
  81. $this->url = THEME.rawurlencode($name).'/';
  82. $this->is_popup = false;
  83. $this->message = '';
  84. }
  85. /**
  86. * Gets a theme's source path.
  87. *
  88. * @param string $name Theme name
  89. * @return string Source file path for named theme
  90. */
  91. public static function path($name)
  92. {
  93. return txpath.DS.THEME.$name.DS.$name.'.php';
  94. }
  95. /**
  96. * Theme factory.
  97. *
  98. * @param string $name Theme name
  99. * @return obj|bool An initialised theme object or FALSE on failure
  100. */
  101. public static function factory($name)
  102. {
  103. $path = theme::path($name);
  104. if (is_readable($path)) {
  105. require_once($path);
  106. } else {
  107. return false;
  108. }
  109. $t = "{$name}_theme";
  110. if (class_exists($t)) {
  111. return new $t($name);
  112. } else {
  113. return false;
  114. }
  115. }
  116. /**
  117. * Initialise the theme singleton.
  118. *
  119. * @param string $name Theme name
  120. * @return obj A valid theme object
  121. */
  122. public static function init($name = '')
  123. {
  124. static $instance;
  125. if ($name === '') {
  126. $name = pluggable_ui('admin_side', 'theme_name', get_pref('theme_name', 'hive'));
  127. }
  128. if ($instance && is_object($instance) && ($name == $instance->name)) {
  129. return $instance;
  130. } else {
  131. $instance = null;
  132. }
  133. $instance = theme::factory($name);
  134. if (!$instance) {
  135. set_pref('theme_name', 'hive');
  136. die(gTxt('cannot_instantiate_theme', array('{name}' => $name, '{class}' => "{$name}_theme", '{path}' => theme::path($name))));
  137. }
  138. return $instance;
  139. }
  140. /**
  141. * Get a list of all theme names.
  142. *
  143. * @return array Alphabetically sorted array of all available theme names
  144. */
  145. public static function names()
  146. {
  147. $dirs = glob(txpath.DS.THEME.'*');
  148. if (is_array($dirs)) {
  149. foreach ($dirs as $d) {
  150. // Extract trailing directory name.
  151. preg_match('#(.*)[\\/]+(.*)$#', $d, $m);
  152. $name = $m[2];
  153. // Accept directories containing an equally named .php file.
  154. if (is_dir($d) && ($d != '.') && ($d != '..') && isset($name) && is_file($d.DS.$name.'.php')) {
  155. $out[] = $name;
  156. }
  157. }
  158. sort($out, SORT_STRING);
  159. return $out;
  160. }
  161. return array();
  162. }
  163. /**
  164. * Inherit from an ancestor theme.
  165. *
  166. * @param string $name Name of ancestor theme
  167. * @return bool TRUE on success, FALSE on unavailable/invalid ancestor theme
  168. */
  169. public static function based_on($name)
  170. {
  171. global $production_status;
  172. $theme = theme::factory($name);
  173. if (!$theme) {
  174. set_pref('theme_name', 'hive');
  175. if ($production_status === 'debug') {
  176. echo gTxt('cannot_instantiate_theme', array('{name}' => $name, '{class}' => "{$name}_theme", '{path}' => theme::path($name)));
  177. }
  178. return false;
  179. }
  180. return true;
  181. }
  182. /**
  183. * Sets Textpatterns menu structure, message contents and other application states.
  184. *
  185. * @param string $area Currently active top level menu
  186. * @param string $event Currently active second level menu
  187. * @param bool $is_popup Just a popup window for tag builder et cetera
  188. * @param array $message The contents of the notification message pane
  189. * @return obj This theme object
  190. */
  191. public function set_state($area, $event, $is_popup, $message)
  192. {
  193. $this->is_popup = $is_popup;
  194. $this->message = $message;
  195. if ($is_popup) {
  196. return $this;
  197. }
  198. // Use legacy areas() for b/c.
  199. $areas = areas();
  200. $defaults = array(
  201. 'content' => 'article',
  202. 'presentation' => 'page',
  203. 'admin' => 'admin'
  204. );
  205. if (empty($areas['start'])) {
  206. unset($areas['start']);
  207. }
  208. if (empty($areas['extensions'])) {
  209. unset($areas['extensions']);
  210. }
  211. $dflt_tab = get_pref('default_event', '');
  212. foreach ($areas as $ar => $items) {
  213. $l_ = gTxt('tab_'.$ar);
  214. $e_ = (array_key_exists($ar, $defaults)) ? $defaults[$ar] : reset($areas[$ar]);
  215. $i_ = array();
  216. if (has_privs('tab.'.$ar)) {
  217. if (!has_privs($e_)) {
  218. $e_ = '';
  219. }
  220. foreach ($items as $a => $b) {
  221. if (has_privs($b)) {
  222. if ($e_ === '') {
  223. $e_ = $b;
  224. }
  225. if ($b == $dflt_tab) {
  226. $this->menu[$ar]['event'] = $dflt_tab;
  227. }
  228. $i_[] = array('label' => $a, 'event' => $b, 'active' => ($b == $event));
  229. }
  230. }
  231. if ($e_) {
  232. $this->menu[$ar] = array(
  233. 'label' => $l_,
  234. 'event' => $e_,
  235. 'active' => ($ar == $area),
  236. 'items' => $i_,
  237. );
  238. }
  239. }
  240. }
  241. return $this;
  242. }
  243. /**
  244. * HTML &lt;head&gt; section.
  245. *
  246. * Returned value is rendered into the head element of
  247. * all admin pages.
  248. *
  249. * @return string
  250. */
  251. public function html_head()
  252. {
  253. trigger_error(__FUNCTION__.' is abstract.', E_USER_ERROR);
  254. }
  255. /**
  256. * Draw the theme's header.
  257. *
  258. * @return string
  259. */
  260. public function header()
  261. {
  262. trigger_error(__FUNCTION__.' is abstract.', E_USER_ERROR);
  263. }
  264. /**
  265. * Draw the theme's footer.
  266. *
  267. * @return string
  268. */
  269. public function footer()
  270. {
  271. trigger_error(__FUNCTION__.' is abstract.', E_USER_ERROR);
  272. }
  273. /**
  274. * Output notification message for synchronous HTML views.
  275. *
  276. * @param array $thing Message text and status flag
  277. * @param bool $modal If TRUE, immediate user interaction suggested
  278. * @return string HTML
  279. * @example
  280. * global $theme;
  281. * echo $theme->announce(array('my_message', E_ERROR));
  282. */
  283. public function announce($thing = array('', 0), $modal = false)
  284. {
  285. trigger_error(__FUNCTION__.' is abstract.', E_USER_ERROR);
  286. }
  287. /**
  288. * Output notification message for asynchronous JavaScript views.
  289. *
  290. * @param array $thing Message text and status flag
  291. * @param bool $modal If TRUE, immediate user interaction suggested
  292. * @return string JavaScript
  293. * @since 4.5.0
  294. * @example
  295. * global $theme;
  296. * echo script_js(
  297. * $theme->announce_async(array('my_message', E_ERROR))
  298. * );
  299. */
  300. public function announce_async($thing = array('', 0), $modal = false)
  301. {
  302. trigger_error(__FUNCTION__.' is abstract.', E_USER_ERROR);
  303. }
  304. /**
  305. * Define bureaucratic details of this theme.
  306. *
  307. * All returned items are optional.
  308. *
  309. * @return array
  310. */
  311. public function manifest()
  312. {
  313. return array(
  314. 'title' => '', // Human-readable title of this theme. No HTML, keep it short.
  315. 'author' => '', // Name(s) of this theme's creator(s).
  316. 'author_uri' => '', // URI of the theme's site. Decent vanity is accepted.
  317. 'version' => '', // Version numbering. Mind version_compare().
  318. 'description' => '', // Human readable short description. No HTML.
  319. 'help' => '', // URI of the theme's help and docs. Strictly optional.
  320. );
  321. }
  322. }