PageRenderTime 40ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/core/page.class.php

https://gitlab.com/dali99/shimmie2-Material-Theme
PHP | 384 lines | 174 code | 49 blank | 161 comment | 13 complexity | a03235efcdeba37ecb4fab482b5bdfbf MD5 | raw file
  1. <?php
  2. /**
  3. * \page themes Themes
  4. *
  5. * Each extension has a theme with a specific name -- eg. the extension Setup
  6. * which is stored in ext/setup/main.php will have a theme called SetupTheme
  7. * stored in ext/setup/theme.php. If you want to customise it, create a class
  8. * in the file themes/mytheme/setup.theme.php called CustomSetupTheme which
  9. * extends SetupTheme and overrides some of its methods.
  10. *
  11. * Generally an extension should only deal with processing data; whenever it
  12. * wants to display something, it should pass the data to be displayed to the
  13. * theme object, and the theme will add the data into the global $page
  14. * structure.
  15. *
  16. * A page should make sure that all the data it outputs is free from dangerous
  17. * data by using html_escape(), url_escape(), or int_escape() as appropriate.
  18. *
  19. * Because some HTML can be placed anywhere according to the theme, coming up
  20. * with the correct way to link to a page can be hard -- thus we have the
  21. * make_link() function, which will take a path like "post/list" and turn it
  22. * into a full and correct link, eg /myboard/post/list, /foo/index.php?q=post/list,
  23. * etc depending on how things are set up. This should always be used to link
  24. * to pages rather than hardcoding a path.
  25. *
  26. * Various other common functions are available as part of the Themelet class.
  27. */
  28. /**
  29. * Class Page
  30. *
  31. * A data structure for holding all the bits of data that make up a page.
  32. *
  33. * The various extensions all add whatever they want to this structure,
  34. * then Layout turns it into HTML.
  35. */
  36. class Page {
  37. /** @name Overall */
  38. //@{
  39. /** @var string */
  40. public $mode = "page";
  41. /** @var string */
  42. public $type = "text/html; charset=utf-8";
  43. /**
  44. * Set what this page should do; "page", "data", or "redirect".
  45. * @param string $mode
  46. */
  47. public function set_mode($mode) {
  48. $this->mode = $mode;
  49. }
  50. /**
  51. * Set the page's MIME type.
  52. * @param string $type
  53. */
  54. public function set_type($type) {
  55. $this->type = $type;
  56. }
  57. //@}
  58. // ==============================================
  59. /** @name "data" mode */
  60. //@{
  61. /** @var string; public only for unit test */
  62. public $data = "";
  63. /** @var string; public only for unit test */
  64. public $filename = null;
  65. /**
  66. * Set the raw data to be sent.
  67. * @param string $data
  68. */
  69. public function set_data($data) {
  70. $this->data = $data;
  71. }
  72. /**
  73. * Set the recommended download filename.
  74. * @param string $filename
  75. */
  76. public function set_filename($filename) {
  77. $this->filename = $filename;
  78. }
  79. //@}
  80. // ==============================================
  81. /** @name "redirect" mode */
  82. //@{
  83. /** @var string */
  84. private $redirect = "";
  85. /**
  86. * Set the URL to redirect to (remember to use make_link() if linking
  87. * to a page in the same site).
  88. * @param string $redirect
  89. */
  90. public function set_redirect($redirect) {
  91. $this->redirect = $redirect;
  92. }
  93. //@}
  94. // ==============================================
  95. /** @name "page" mode */
  96. //@{
  97. /** @var int */
  98. public $code = 200;
  99. /** @var string */
  100. public $title = "";
  101. /** @var string */
  102. public $heading = "";
  103. /** @var string */
  104. public $subheading = "";
  105. /** @var string */
  106. public $quicknav = "";
  107. /** @var string[] */
  108. public $html_headers = array();
  109. /** @var string[] */
  110. public $http_headers = array();
  111. /** @var string[][] */
  112. public $cookies = array();
  113. /** @var Block[] */
  114. public $blocks = array();
  115. /**
  116. * Set the HTTP status code
  117. * @param int $code
  118. */
  119. public function set_code($code) {
  120. $this->code = $code;
  121. }
  122. /**
  123. * Set the window title.
  124. * @param string $title
  125. */
  126. public function set_title($title) {
  127. $this->title = $title;
  128. }
  129. /**
  130. * Set the main heading.
  131. * @param string $heading
  132. */
  133. public function set_heading($heading) {
  134. $this->heading = $heading;
  135. }
  136. /**
  137. * Set the sub heading.
  138. * @param string $subheading
  139. */
  140. public function set_subheading($subheading) {
  141. $this->subheading = $subheading;
  142. }
  143. /**
  144. * Add a line to the HTML head section.
  145. * @param string $line
  146. * @param int $position
  147. */
  148. public function add_html_header($line, $position=50) {
  149. while(isset($this->html_headers[$position])) $position++;
  150. $this->html_headers[$position] = $line;
  151. }
  152. /**
  153. * Add a http header to be sent to the client.
  154. * @param string $line
  155. * @param int $position
  156. */
  157. public function add_http_header($line, $position=50) {
  158. while(isset($this->http_headers[$position])) $position++;
  159. $this->http_headers[$position] = $line;
  160. }
  161. /**
  162. * The counterpart for get_cookie, this works like php's
  163. * setcookie method, but prepends the site-wide cookie prefix to
  164. * the $name argument before doing anything.
  165. *
  166. * @param string $name
  167. * @param string $value
  168. * @param int $time
  169. * @param string $path
  170. */
  171. public function add_cookie($name, $value, $time, $path) {
  172. $full_name = COOKIE_PREFIX."_".$name;
  173. $this->cookies[] = array($full_name, $value, $time, $path);
  174. }
  175. /**
  176. * @param string $name
  177. * @return string|null
  178. */
  179. public function get_cookie(/*string*/ $name) {
  180. $full_name = COOKIE_PREFIX."_".$name;
  181. if(isset($_COOKIE[$full_name])) {
  182. return $_COOKIE[$full_name];
  183. }
  184. else {
  185. return null;
  186. }
  187. }
  188. /**
  189. * Get all the HTML headers that are currently set and return as a string.
  190. * @return string
  191. */
  192. public function get_all_html_headers() {
  193. $data = '';
  194. foreach ($this->html_headers as $line) {
  195. $data .= $line . "\n";
  196. }
  197. return $data;
  198. }
  199. /**
  200. * Removes all currently set HTML headers (Be careful..).
  201. */
  202. public function delete_all_html_headers() {
  203. $this->html_headers = array();
  204. }
  205. /**
  206. * Add a Block of data to the page.
  207. * @param Block $block
  208. */
  209. public function add_block(Block $block) {
  210. $this->blocks[] = $block;
  211. }
  212. //@}
  213. // ==============================================
  214. /**
  215. * Display the page according to the mode and data given.
  216. */
  217. public function display() {
  218. global $page, $user;
  219. header("HTTP/1.0 {$this->code} Shimmie");
  220. header("Content-type: ".$this->type);
  221. header("X-Powered-By: SCore-".SCORE_VERSION);
  222. if (!headers_sent()) {
  223. foreach($this->http_headers as $head) {
  224. header($head);
  225. }
  226. foreach($this->cookies as $c) {
  227. setcookie($c[0], $c[1], $c[2], $c[3]);
  228. }
  229. } else {
  230. print "Error: Headers have already been sent to the client.";
  231. }
  232. switch($this->mode) {
  233. case "page":
  234. if(CACHE_HTTP) {
  235. header("Vary: Cookie, Accept-Encoding");
  236. if($user->is_anonymous() && $_SERVER["REQUEST_METHOD"] == "GET") {
  237. header("Cache-control: public, max-age=600");
  238. header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 600) . ' GMT');
  239. }
  240. else {
  241. #header("Cache-control: private, max-age=0");
  242. header("Cache-control: no-cache");
  243. header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 600) . ' GMT');
  244. }
  245. }
  246. #else {
  247. # header("Cache-control: no-cache");
  248. # header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 600) . ' GMT');
  249. #}
  250. if($this->get_cookie("flash_message")) {
  251. $this->add_cookie("flash_message", "", -1, "/");
  252. }
  253. usort($this->blocks, "blockcmp");
  254. $this->add_auto_html_headers();
  255. $layout = new Layout();
  256. $layout->display_page($page);
  257. break;
  258. case "data":
  259. header("Content-Length: ".strlen($this->data));
  260. if(!is_null($this->filename)) {
  261. header('Content-Disposition: attachment; filename='.$this->filename);
  262. }
  263. print $this->data;
  264. break;
  265. case "redirect":
  266. header('Location: '.$this->redirect);
  267. print 'You should be redirected to <a href="'.$this->redirect.'">'.$this->redirect.'</a>';
  268. break;
  269. default:
  270. print "Invalid page mode";
  271. break;
  272. }
  273. }
  274. /**
  275. * This function grabs all the CSS and JavaScript files sprinkled throughout Shimmie's folders,
  276. * concatenates them together into two large files (one for CSS and one for JS) and then stores
  277. * them in the /cache/ directory for serving to the user.
  278. *
  279. * Why do this? Two reasons:
  280. * 1. Reduces the number of files the user's browser needs to download.
  281. * 2. Allows these cached files to be compressed/minified by the admin.
  282. *
  283. * TODO: This should really be configurable somehow...
  284. */
  285. public function add_auto_html_headers() {
  286. global $config;
  287. $data_href = get_base_href();
  288. $theme_name = $config->get_string('theme', 'default');
  289. $this->add_html_header("<script type='text/javascript'>base_href = '$data_href';</script>", 40);
  290. # 404/static handler will map these to themes/foo/bar.ico or lib/static/bar.ico
  291. $this->add_html_header("<link rel='icon' type='image/x-icon' href='$data_href/favicon.ico'>", 41);
  292. $this->add_html_header("<link rel='apple-touch-icon' href='$data_href/apple-touch-icon.png'>", 42);
  293. $config_latest = 0;
  294. foreach(zglob("data/config/*") as $conf) {
  295. $config_latest = max($config_latest, filemtime($conf));
  296. }
  297. $css_files = array();
  298. $css_latest = $config_latest;
  299. foreach(array_merge(zglob("lib/*.css"), zglob("ext/*/style.css"), zglob("themes/$theme_name/style.css")) as $css) {
  300. $css_files[] = $css;
  301. $css_latest = max($css_latest, filemtime($css));
  302. }
  303. $css_cache_file = data_path("cache/style.$theme_name.$css_latest.css");
  304. if(!file_exists($css_cache_file)) {
  305. $css_data = "";
  306. foreach($css_files as $file) {
  307. $file_data = file_get_contents($file);
  308. $pattern = '/url[\s]*\([\s]*["\']?([^"\'\)]+)["\']?[\s]*\)/';
  309. $replace = 'url("../../'.dirname($file).'/$1")';
  310. $file_data = preg_replace($pattern, $replace, $file_data);
  311. $css_data .= $file_data . "\n";
  312. }
  313. file_put_contents($css_cache_file, $css_data);
  314. }
  315. $this->add_html_header("<link rel='stylesheet' href='$data_href/$css_cache_file' type='text/css'>", 43);
  316. $js_files = array();
  317. $js_latest = $config_latest;
  318. foreach(array_merge(zglob("lib/*.js"), zglob("ext/*/script.js"), zglob("themes/$theme_name/script.js")) as $js) {
  319. $js_files[] = $js;
  320. $js_latest = max($js_latest, filemtime($js));
  321. }
  322. $js_cache_file = data_path("cache/script.$theme_name.$js_latest.js");
  323. if(!file_exists($js_cache_file)) {
  324. $js_data = "";
  325. foreach($js_files as $file) {
  326. $js_data .= file_get_contents($file) . "\n";
  327. }
  328. file_put_contents($js_cache_file, $js_data);
  329. }
  330. $this->add_html_header("<script src='$data_href/$js_cache_file' type='text/javascript'></script>", 44);
  331. }
  332. }
  333. class MockPage extends Page {
  334. }