PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/system/cms/modules/navigation/plugin.php

https://github.com/asalem/pyrocms
PHP | 368 lines | 271 code | 42 blank | 55 comment | 25 complexity | 0105c9a03346f1c4cddfc012b3c8b847 MD5 | raw file
Possible License(s): CC-BY-3.0, BSD-3-Clause, CC0-1.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, MIT
  1. <?php
  2. use Pyro\Module\Navigation;
  3. /**
  4. * Navigation Plugin
  5. *
  6. * @author PyroCMS Dev Team
  7. * @package PyroCMS\Core\Modules\Navigation\Plugins
  8. */
  9. class Plugin_Navigation extends Plugin
  10. {
  11. public $version = '1.0.0';
  12. public $name = array(
  13. 'en' => 'Navigation',
  14. );
  15. public $description = array(
  16. 'en' => 'Build navigation links including links in dropdown menus.',
  17. );
  18. /**
  19. * Returns a PluginDoc array that PyroCMS uses
  20. * to build the reference in the admin panel
  21. *
  22. * All options are listed here but refer
  23. * to the Blog plugin for a larger example
  24. *
  25. * @todo fill the array with details about this plugin, then uncomment the return value.
  26. *
  27. * @return array
  28. */
  29. public function _self_doc()
  30. {
  31. $info = array(
  32. 'links' => array(
  33. 'description' => array(
  34. 'en' => 'Output links from a single navigation group. If [group_segment] is used it loads the group specified by that uri segment.'
  35. ),
  36. 'single' => true,
  37. 'double' => true,
  38. 'variables' => 'url|title|total|target|class|children }}{{ /children',
  39. 'attributes' => array(
  40. 'group' => array(
  41. 'type' => 'text',// Can be: slug, number, flag, text, array, any.
  42. 'flags' => '',
  43. 'default' => 'asc',
  44. 'required' => true,
  45. ),
  46. 'group_segment' => array(
  47. 'type' => 'number',
  48. 'flags' => '',
  49. 'default' => '',
  50. 'required' => false,
  51. ),
  52. 'top' => array(
  53. 'type' => 'flag',
  54. 'flags' => 'Y|N',
  55. 'default' => 'N',
  56. 'required' => false,
  57. ),
  58. 'separator' => array(
  59. 'type' => 'text',
  60. 'flags' => '',
  61. 'default' => '',
  62. 'required' => false,
  63. ),
  64. 'link_class' => array(
  65. 'type' => 'text',
  66. 'flags' => '',
  67. 'default' => '',
  68. 'required' => false,
  69. ),
  70. 'more_class' => array(
  71. 'type' => 'text',
  72. 'flags' => '',
  73. 'default' => 'has_children',
  74. 'required' => false,
  75. ),
  76. 'class' => array(
  77. 'type' => 'text',
  78. 'flags' => '',
  79. 'default' => 'current',
  80. 'required' => false,
  81. ),
  82. 'first_class' => array(
  83. 'type' => 'text',
  84. 'flags' => '',
  85. 'default' => 'first',
  86. 'required' => false,
  87. ),
  88. 'last_class' => array(
  89. 'type' => 'text',
  90. 'flags' => '',
  91. 'default' => 'last',
  92. 'required' => false,
  93. ),
  94. 'dropdown_class' => array(
  95. 'type' => 'text',
  96. 'flags' => '',
  97. 'default' => 'dropdown',
  98. 'required' => false,
  99. ),
  100. 'tag' => array(
  101. 'type' => 'text',
  102. 'flags' => '',
  103. 'default' => 'li',
  104. 'required' => false,
  105. ),
  106. 'list_tag' => array(
  107. 'type' => 'text',
  108. 'flags' => '',
  109. 'default' => 'ul',
  110. 'required' => false,
  111. ),
  112. 'wrap' => array(
  113. 'type' => 'text',
  114. 'flags' => '',
  115. 'default' => '',
  116. 'required' => false,
  117. ),
  118. 'max_depth' => array(
  119. 'type' => 'text',
  120. 'flags' => '',
  121. 'default' => '',
  122. 'required' => false,
  123. ),
  124. 'indent' => array(
  125. 'type' => 'text',
  126. 'flags' => 'Y|N',
  127. 'default' => 'N',
  128. 'required' => false,
  129. ),
  130. ),
  131. ),// end links method
  132. );
  133. return $info;
  134. }
  135. /**
  136. * Navigation
  137. *
  138. * Creates a list of menu items
  139. *
  140. * Usage:
  141. * {{ navigation:links group="header" }}
  142. * Optional: indent="", tag="li", list_tag="ul", top="text", separator="", group_segment="", class="", more_class="", wrap=""
  143. *
  144. * @param array
  145. * @return array
  146. */
  147. public function links()
  148. {
  149. // What are we after?
  150. $group = $this->attribute('group');
  151. // TODO Cache me please
  152. $links = Navigation\Model\Link::getTreeByGroup($group, array(
  153. // TODO Rethink group logic for sentry
  154. 'user_groups' => isset($this->current_user->id) ? $this->current_user->groups->lists('id') : false,
  155. 'front_end' => true,
  156. 'is_secure' => IS_SECURE,
  157. ));
  158. return $this->_build_links($links, $this->content());
  159. }
  160. /**
  161. * Builds the Page Tree into HTML
  162. *
  163. * @param array $links Page Tree array from `Link::getTreeByGroup`
  164. * @param bool $return_arr Return as an Array instead of HTML
  165. * @return array|string
  166. */
  167. private function _build_links($links = array(), $return_arr = true)
  168. {
  169. static $current_link = false;
  170. static $level = 0;
  171. $top = $this->attribute('top', false);
  172. $separator = $this->attribute('separator', '');
  173. $link_class = $this->attribute('link_class', '');
  174. $more_class = $this->attribute('more_class', 'has_children');
  175. $current_class = $this->attribute('class', 'current');
  176. $first_class = $this->attribute('first_class', 'first');
  177. $last_class = $this->attribute('last_class', 'last');
  178. $dropdown_class = $this->attribute('dropdown_class', 'dropdown');
  179. $output = $return_arr ? array() : '';
  180. $wrap = $this->attribute('wrap');
  181. $max_depth = $this->attribute('max_depth');
  182. $i = 1;
  183. $total = sizeof($links);
  184. if (! $return_arr) {
  185. $tag = $this->attribute('tag', 'li');
  186. $list_tag = $this->attribute('list_tag', 'ul');
  187. switch ($this->attribute('indent')) {
  188. case 't':
  189. case 'tab':
  190. case ' ':
  191. $indent = "\t";
  192. break;
  193. case 's':
  194. case 'space':
  195. case ' ':
  196. $indent = " ";
  197. break;
  198. default:
  199. $indent = false;
  200. break;
  201. }
  202. if ($indent) {
  203. $ident_a = repeater($indent, $level);
  204. $ident_b = $ident_a.$indent;
  205. $ident_c = $ident_b.$indent;
  206. }
  207. }
  208. foreach ($links as $link) {
  209. $item = array();
  210. $wrapper = array();
  211. // attributes of anchor
  212. $item['url'] = ci()->parser->parse_string($link->url, null, true);
  213. $item['uri'] = ci()->parser->parse_string($link->uri, null, true);
  214. $item['title'] = $link->title;
  215. $item['total'] = $total;
  216. $item['page_id'] = $link['page_id'];
  217. if ($wrap) {
  218. $item['title'] = '<'.$wrap.'>'.$item['title'].'</'.$wrap.'>';
  219. }
  220. $item['attributes']['target'] = $link->target ? 'target="'.$link->target.'"' : null;
  221. $item['attributes']['class'] = $link_class ? 'class="'.$link_class.'"' : '';
  222. $item['attributes']['level'] = $level;
  223. // attributes of anchor wrapper
  224. $wrapper['class'] = $link->class ? explode(' ', $link->class) : array();
  225. $wrapper['children'] = $return_arr ? array() : null;
  226. $wrapper['separator'] = $separator;
  227. $wrapper['level'] = $level;
  228. $wrapper['current'] = FALSE;
  229. // is single ?
  230. if ($total === 1) {
  231. $wrapper['class'][] = 'single';
  232. }
  233. // is first ?
  234. elseif ($i === 1) {
  235. $wrapper['class'][] = $first_class;
  236. }
  237. // is last ?
  238. elseif ($i === $total) {
  239. $wrapper['class'][] = $last_class;
  240. $wrapper['separator'] = '';
  241. }
  242. // has children ? build children
  243. if ($link->children) {
  244. ++$level;
  245. if (! $max_depth or $level < $max_depth) {
  246. $wrapper['class'][] = $more_class;
  247. $wrapper['children'] = $this->_build_links($link->children, $return_arr);
  248. }
  249. --$level;
  250. }
  251. // is this the link to the page that we're on?
  252. if (preg_match('@^'.current_url().'/?$@', $link->url) or ($link->link_type == 'page' and $link->is_home) and site_url() == current_url()) {
  253. $current_link = $link->url;
  254. $wrapper['class'][] = $current_class;
  255. $wrapper['current'] = TRUE;
  256. }
  257. // is the link we're currently working with found inside the children html?
  258. if ( ! in_array($current_class, $wrapper['class']) and
  259. isset($wrapper['children']) and
  260. $current_link and
  261. ((is_array($wrapper['children']) and in_array_r($current_link, $wrapper['children'])) or
  262. (is_string($wrapper['children']) and strpos($wrapper['children'], $current_link)))
  263. ) {
  264. // that means that this link is a parent
  265. $wrapper['class'][] = 'has_'.$current_class;
  266. }
  267. // if we are viewing something in a module (such as a blog post) that doesn't have a link then mark the link
  268. // to the module root with .has_current but not if it will already have .current
  269. elseif ($link->module_name === $this->module and ! preg_match('@^'.current_url().'/?$@', $link->url)) {
  270. $wrapper['class'][] = 'has_'.$current_class;
  271. }
  272. ++$i;
  273. if ($return_arr) {
  274. $item['target'] =& $item['attributes']['target'];
  275. $item['class'] =& $item['attributes']['class'];
  276. $item['level'] =& $item['attributes']['level'];
  277. $item['children'] = $wrapper['children'];
  278. $item['current'] = $wrapper['current'];
  279. if ($wrapper['class'] && $item['class']) {
  280. $item['class'] = implode(' ', $wrapper['class']).' '.substr($item['class'], 7, -1);
  281. } elseif ($wrapper['class']) {
  282. $item['class'] = implode(' ', $wrapper['class']);
  283. }
  284. if ($item['target']) {
  285. $item['target'] = substr($item['target'], 8, -1);
  286. }
  287. // assign attributes to level family
  288. $output[] = $item;
  289. } else {
  290. $add_first_tag = $level === 0 && ! in_array($this->attribute('items_only', 'true'), array('1', 'y', 'yes', 'true'));
  291. // render and indent or only render inline?
  292. if ($indent) {
  293. // remove all empty values so we don't have an empty class attribute
  294. $classes = implode(' ', array_filter($wrapper['class']));
  295. $output .= $add_first_tag ? "<{$list_tag}>".PHP_EOL : '';
  296. $output .= $ident_b.'<'.$tag.($classes > '' ? ' class="'.$classes.'">' : '>').PHP_EOL;
  297. $output .= $ident_c.((($level == 0) and $top == 'text' and $wrapper['children']) ? $item['title'] : anchor($item['url'], $item['title'], trim(implode(' ', $item['attributes'])))).PHP_EOL;
  298. if ($wrapper['children']) {
  299. $output .= $ident_c."<{$list_tag}>".PHP_EOL;
  300. $output .= $ident_c.$indent.str_replace(PHP_EOL, (PHP_EOL.$indent), trim($ident_c.$wrapper['children'])).PHP_EOL;
  301. $output .= $ident_c."</{$list_tag}>".PHP_EOL;
  302. }
  303. $output .= $wrapper['separator'] ? $ident_c.$wrapper['separator'].PHP_EOL : '';
  304. $output .= $ident_b."</{$tag}>".PHP_EOL;
  305. $output .= $add_first_tag ? $ident_a."</{$list_tag}>".PHP_EOL : '';
  306. } else {
  307. // remove all empty values so we don't have an empty class attribute
  308. $classes = implode(' ', array_filter($wrapper['class']));
  309. $output .= $add_first_tag ? "<{$list_tag}>" : '';
  310. $output .= '<'.$tag.($classes > '' ? ' class="'.$classes.'">' : '>');
  311. $output .= (($level == 0) and $top == 'text' and $wrapper['children']) ? $item['title'] : anchor($item['url'], $item['title'], trim(implode(' ', $item['attributes'])));
  312. if ($wrapper['children']) {
  313. $output .= "<{$list_tag} class=\"{$dropdown_class}\">";
  314. $output .= $wrapper['children'];
  315. $output .= "</{$list_tag}>";
  316. }
  317. $output .= $wrapper['separator'];
  318. $output .= "</{$tag}>";
  319. $output .= $add_first_tag ? "</{$list_tag}>" : '';
  320. }
  321. }
  322. }
  323. return $output;
  324. }
  325. }
  326. /* End of file plugin.php */