KXStudio Website https://kx.studio/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Theme.php 13KB

9 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. <?php
  2. /**
  3. * Class: Theme
  4. * Various helper functions for the theming engine.
  5. */
  6. class Theme {
  7. # String: $title
  8. # The title for the current page.
  9. public $title = "";
  10. /**
  11. * Function: __construct
  12. * Loads the Twig parser into <Theme>, and sets up the theme l10n domain.
  13. */
  14. private function __construct() {
  15. $config = Config::current();
  16. # Load the theme translator
  17. if (file_exists(THEME_DIR."/locale/".$config->locale.".mo"))
  18. load_translator("theme", THEME_DIR."/locale/".$config->locale.".mo");
  19. # Load the theme's info into the Theme class.
  20. foreach (YAML::load(THEME_DIR."/info.yaml") as $key => $val)
  21. $this->$key = $val;
  22. $this->url = THEME_URL;
  23. }
  24. /**
  25. * Function: pages_list
  26. * Returns a simple array of list items to be used by the theme to generate a recursive array of pages.
  27. *
  28. * Parameters:
  29. * $start - Page ID or slug to start at.
  30. * $exclude - Page ID to exclude from the list. Used in the admin area.
  31. */
  32. public function pages_list($start = 0, $exclude = null) {
  33. if (isset($this->pages_list[$start]))
  34. return $this->pages_list[$start];
  35. $this->linear_children = array();
  36. $this->pages_flat = array();
  37. $this->children = array();
  38. $this->end_tags_for = array();
  39. if ($start and !is_numeric($start))
  40. $begin_page = new Page(array("url" => $start));
  41. $start = ($start and !is_numeric($start)) ? $begin_page->id : $start ;
  42. $where = ADMIN ? array("id not" => $exclude) : array("show_in_list" => true) ;
  43. $pages = Page::find(array("where" => $where, "order" => "list_order ASC"));
  44. if (empty($pages))
  45. return $this->pages_list[$start] = array();
  46. foreach ($pages as $page)
  47. $this->end_tags_for[$page->id] = $this->children[$page->id] = array();
  48. foreach ($pages as $page)
  49. if ($page->parent_id != 0)
  50. $this->children[$page->parent_id][] = $page;
  51. foreach ($pages as $page)
  52. if ((!$start and $page->parent_id == 0) or ($start and $page->id == $start))
  53. $this->recurse_pages($page);
  54. $array = array();
  55. foreach ($this->pages_flat as $page) {
  56. $array[$page->id]["has_children"] = !empty($this->children[$page->id]);
  57. if ($array[$page->id]["has_children"])
  58. $this->end_tags_for[$this->get_last_linear_child($page->id)][] = array("</ul>", "</li>");
  59. $array[$page->id]["end_tags"] =& $this->end_tags_for[$page->id];
  60. $array[$page->id]["page"] = $page;
  61. }
  62. return $this->pages_list[$start] = $array;
  63. }
  64. /**
  65. * Function: get_last_linear_child
  66. * Gets the last linear child of a page.
  67. *
  68. * Parameters:
  69. * $page - Page to get the last linear child of.
  70. * $origin - Where to start.
  71. */
  72. public function get_last_linear_child($page, $origin = null) {
  73. fallback($origin, $page);
  74. $this->linear_children[$origin] = $page;
  75. foreach ($this->children[$page] as $child)
  76. $this->get_last_linear_child($child->id, $origin);
  77. return $this->linear_children[$origin];
  78. }
  79. /**
  80. * Function: recurse_pages
  81. * Prepares the pages into <Theme.$pages_flat>.
  82. *
  83. * Parameters:
  84. * $page - Page to start recursion at.
  85. */
  86. public function recurse_pages($page) {
  87. $page->depth = oneof(@$page->depth, 1);
  88. $this->pages_flat[] = $page;
  89. foreach ($this->children[$page->id] as $child) {
  90. $child->depth = $page->depth + 1;
  91. $this->recurse_pages($child);
  92. }
  93. }
  94. /**
  95. * Function: archive_list
  96. * Generates an array of all of the archives, by month.
  97. *
  98. * Parameters:
  99. * $limit - Amount of months to list
  100. * $order_by - What to sort it by
  101. * $order - "asc" or "desc"
  102. *
  103. * Returns:
  104. * The array. Each entry as "month", "year", and "url" values, stored as an array.
  105. */
  106. public function archives_list($limit = 0, $order_by = "created_at", $order = "desc") {
  107. if (isset($this->archives_list["$limit,$order_by,$order"]))
  108. return $this->archives_list["$limit,$order_by,$order"];
  109. $sql = SQL::current();
  110. $dates = $sql->select("posts",
  111. array("DISTINCT YEAR(created_at) AS year",
  112. "MONTH(created_at) AS month",
  113. "created_at AS created_at",
  114. "COUNT(id) AS posts"),
  115. array("status" => "public", Post::feathers()),
  116. $order_by." ".strtoupper($order),
  117. array(),
  118. ($limit == 0) ? null : $limit,
  119. null,
  120. array("created_at"));
  121. $archives = array();
  122. $grouped = array();
  123. while ($date = $dates->fetchObject())
  124. if (isset($grouped[$date->month." ".$date->year]))
  125. $archives[$grouped[$date->month." ".$date->year]]["count"]++;
  126. else {
  127. $grouped[$date->month." ".$date->year] = count($archives);
  128. $archives[] = array("month" => $date->month,
  129. "year" => $date->year,
  130. "when" => $date->created_at,
  131. "url" => url("archive/".when("Y/m/", $date->created_at)),
  132. "count" => $date->posts);
  133. }
  134. return $this->archives_list["$limit,$order_by,$order"] = $archives;
  135. }
  136. /**
  137. * Function: file_exists
  138. * Returns whether the specified Twig file exists or not.
  139. *
  140. * Parameters:
  141. * $file - The file's name
  142. */
  143. public function file_exists($file) {
  144. return file_exists(THEME_DIR."/".$file.".twig");
  145. }
  146. /**
  147. * Function: stylesheets
  148. * Outputs the default stylesheet links.
  149. */
  150. public function stylesheets() {
  151. $visitor = Visitor::current();
  152. $config = Config::current();
  153. $trigger = Trigger::current();
  154. $stylesheets = array();
  155. Trigger::current()->filter($stylesheets, "stylesheets");
  156. if (!empty($stylesheets))
  157. $stylesheets = '<link rel="stylesheet" href="'.
  158. implode('" type="text/css" media="screen" charset="utf-8" /'."\n\t\t".'<link rel="stylesheet" href="', $stylesheets).
  159. '" type="text/css" media="screen" charset="utf-8" />';
  160. else
  161. $stylesheets = "";
  162. if (file_exists(THEME_DIR."/style.css"))
  163. $stylesheets = '<link rel="stylesheet" href="'.THEME_URL.'/style.css" type="text/css" media="screen" charset="utf-8" />'."\n\t\t";
  164. if (!file_exists(THEME_DIR."/stylesheets/") and !file_exists(THEME_DIR."/css/"))
  165. return $stylesheets;
  166. $long = (array) glob(THEME_DIR."/stylesheets/*");
  167. $short = (array) glob(THEME_DIR."/css/*");
  168. $total = array_merge($long, $short);
  169. foreach($total as $file) {
  170. $path = preg_replace("/(.+)\/themes\/(.+)/", "/themes/\\2", $file);
  171. $file = basename($file);
  172. if (substr_count($file, ".inc.css") or (substr($file, -4) != ".css" and substr($file, -4) != ".php"))
  173. continue;
  174. if ($file == "ie.css")
  175. $stylesheets.= "<!--[if IE]>";
  176. if (preg_match("/^ie([0-9\.]+)\.css/", $file, $matches))
  177. $stylesheets.= "<!--[if IE ".$matches[1]."]>";
  178. elseif (preg_match("/(lte?|gte?)ie([0-9\.]+)\.css/", $file, $matches))
  179. $stylesheets.= "<!--[if ".$matches[1]." IE ".$matches[2]."]>";
  180. $stylesheets.= '<link rel="stylesheet" href="'.$config->chyrp_url.$path.'" type="text/css" media="'.($file == "print.css" ? "print" : "screen").'" charset="utf-8" />';
  181. if ($file == "ie.css" or preg_match("/(lt|gt)?ie([0-9\.]+)\.css/", $file))
  182. $stylesheets.= "<![endif]-->";
  183. $stylesheets.= "\n\t\t";
  184. }
  185. return $stylesheets;
  186. }
  187. /**
  188. * Function: javascripts
  189. * Outputs the default JavaScript script references.
  190. */
  191. public function javascripts() {
  192. $route = Route::current();
  193. $args = "";
  194. foreach ($_GET as $key => $val)
  195. if (!empty($val) and $val != $route->action)
  196. $args.= "&amp;".$key."=".urlencode($val);
  197. $config = Config::current();
  198. $trigger = Trigger::current();
  199. $javascripts = array($config->chyrp_url."/includes/lib/gz.php?file=jquery.js",
  200. $config->chyrp_url."/includes/lib/gz.php?file=plugins.js",
  201. $config->chyrp_url.'/includes/javascript.php?action='.$route->action.$args);
  202. Trigger::current()->filter($javascripts, "scripts");
  203. $javascripts = '<script src="'.
  204. implode('" type="text/javascript" charset="utf-8"></script>'."\n\t\t".'<script src="', $javascripts).
  205. '" type="text/javascript" charset="utf-8"></script>';
  206. if (file_exists(THEME_DIR."/javascripts/") or file_exists(THEME_DIR."/js/")) {
  207. $long = (array) glob(THEME_DIR."/javascripts/*.js");
  208. $short = (array) glob(THEME_DIR."/js/*.js");
  209. foreach(array_merge($long, $short) as $file)
  210. if ($file and !substr_count($file, ".inc.js"))
  211. $javascripts.= "\n\t\t".'<script src="'.$config->chyrp_url.'/includes/lib/gz.php?file='.preg_replace("/(.+)\/themes\/(.+)/", "/themes/\\2", $file).'" type="text/javascript" charset="utf-8"></script>';
  212. $long = (array) glob(THEME_DIR."/javascripts/*.php");
  213. $short = (array) glob(THEME_DIR."/js/*.php");
  214. foreach(array_merge($long, $short) as $file)
  215. if ($file)
  216. $javascripts.= "\n\t\t".'<script src="'.$config->chyrp_url.preg_replace("/(.+)\/themes\/(.+)/", "/themes/\\2", $file).'" type="text/javascript" charset="utf-8"></script>';
  217. }
  218. return $javascripts;
  219. }
  220. /**
  221. * Function: feeds
  222. * Outputs the Feed references.
  223. */
  224. public function feeds() {
  225. // Compute the URL of the per-page feed (if any):
  226. $config = Config::current();
  227. $request = ($config->clean_urls) ? rtrim(Route::current()->request, "/") : fix($_SERVER['REQUEST_URI']) ;
  228. $append = $config->clean_urls ?
  229. "/feed" :
  230. ((count($_GET) == 1 and Route::current()->action == "index") ?
  231. "/?feed" :
  232. "&amp;feed") ;
  233. $append.= $config->clean_urls ?
  234. "/".urlencode($this->title) :
  235. "&amp;title=".urlencode($this->title) ;
  236. # Create basic list of links (site and page Atom feeds):
  237. $feedurl = oneof(@$config->feed_url, url("feed"));
  238. $pagefeedurl = $config->url.$request.$append;
  239. $links = array(array("href" => $feedurl, "type" => "application/atom+xml", "title" => $config->name));
  240. if ($pagefeedurl != $feedurl)
  241. $links[] = array("href" => $pagefeedurl, "type" => "application/atom+xml", "title" => "Current Page (if applicable)");
  242. # Ask modules to pitch in by adding their own <link> tag items to $links.
  243. # Each item must be an array with "href" and "rel" properties (and optionally "title" and "type"):
  244. Trigger::current()->filter($links, "links");
  245. # Generate <link> tags:
  246. $tags = array();
  247. foreach ($links as $link) {
  248. $rel = oneof(@$link["rel"], "alternate");
  249. $href = $link["href"];
  250. $type = @$link["type"];
  251. $title = @$link["title"];
  252. $tag = '<link rel="'.$rel.'" href="'.$link["href"].'"';
  253. if ($type)
  254. $tag.= ' type="'.$type.'"';
  255. if ($title)
  256. $tag.= ' title="'.$title.'"';
  257. $tags[] = $tag.' />';
  258. }
  259. return implode("\n\t", $tags);
  260. }
  261. public function load_time() {
  262. return timer_stop();
  263. }
  264. /**
  265. * Function: current
  266. * Returns a singleton reference to the current class.
  267. */
  268. public static function & current() {
  269. static $instance = null;
  270. return $instance = (empty($instance)) ? new self() : $instance ;
  271. }
  272. }