PageRenderTime 57ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/application/modules/menu/models/menu.php

http://github.com/tcm-project/tangocms
PHP | 358 lines | 221 code | 17 blank | 120 comment | 36 complexity | c13609e71bef9c06511824cac28bf3e7 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. /**
  3. * Zula Framework Module
  4. *
  5. * @patches submit all patches to patches@tangocms.org
  6. *
  7. * @author Alex Cartwright
  8. * @copyright Copyright (C) 2007, 2008, 2009, 2010 Alex Cartwright
  9. * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU/GPL 2
  10. * @package TangoCMS_Menu
  11. */
  12. class Menu_model extends Zula_ModelBase {
  13. /**
  14. * Returns every menu category there is. Either from SQL
  15. * or the stored array
  16. *
  17. * @param bool $aclCheck Check category permissions
  18. * @return array
  19. */
  20. public function getAllCategories( $aclCheck=true ) {
  21. if ( !($categories = $this->_cache->get('menu_categories')) ) {
  22. $categories = array();
  23. foreach( $this->_sql->query( 'SELECT * FROM {PREFIX}mod_menu_cats', PDO::FETCH_ASSOC ) as $category ) {
  24. $categories[ $category['id'] ] = $category;
  25. }
  26. $this->_cache->add( 'menu_categories', $categories );
  27. }
  28. if ( $aclCheck ) {
  29. foreach( $categories as $category ) {
  30. $aclResource = 'menu-cat-'.$category['id'];
  31. if ( !$this->_acl->resourceExists( $aclResource ) || !$this->_acl->check( $aclResource ) ) {
  32. unset( $categories[ $category['id'] ] );
  33. }
  34. }
  35. }
  36. return $categories;
  37. }
  38. /**
  39. * Checks if a menu category exists by ID only.
  40. *
  41. * @param int $category
  42. * @oaram bool $aclCheck
  43. * @return bool
  44. */
  45. public function categoryExists( $category, $aclCheck=true ) {
  46. return array_key_exists( $category, $this->getAllCategories($aclCheck) );
  47. }
  48. /**
  49. * Gets details for a menu category via ID. If set to check ACL
  50. * it will only return details if user has permission to the
  51. * category.
  52. *
  53. * @param int $category
  54. * @param bool $aclCheck
  55. * @return array
  56. */
  57. public function getCategory( $category, $aclCheck=true ) {
  58. $categories = $this->getAllCategories( $aclCheck );
  59. if ( isset( $categories[ $category ] ) ) {
  60. return $categories[ $category ];
  61. } else {
  62. throw new Menu_CategoryNoExist( $category );
  63. }
  64. }
  65. /**
  66. * Gets every menu item for a menu category. Can also return all children
  67. * that the menu item has as well.
  68. *
  69. * Can also check permission of the menu items, if user does not
  70. * have permission they wont be returned.
  71. *
  72. * @param int $cid
  73. * @param bool $flat Flattern the 'children' key?
  74. * @param bool $withChildren
  75. * @param $aclCheck
  76. * @return array
  77. */
  78. public function getAllItems( $cid, $flat=false, $withChildren=true, $aclCheck=true ) {
  79. $category = $this->getCategory( $cid, $aclCheck );
  80. if ( !($items = $this->_cache->get('menu_items_'.$category['id'])) ) {
  81. $items = array();
  82. $pdoSt = $this->_sql->prepare( 'SELECT * FROM {PREFIX}mod_menu
  83. WHERE cat_id = ? AND heading_id = 0 ORDER BY `order` ASC ' );
  84. $pdoSt->execute( array($category['id']) );
  85. $tmpItems = $pdoSt->fetchAll( PDO::FETCH_ASSOC );
  86. $count = count( $tmpItems );
  87. for( $i=0; $i < $count; $i++ ) {
  88. // Add in some more array keys
  89. $tmpItems[ $i ]['depth'] = 0;
  90. $tmpItems[ $i ]['order_range'] = $count;
  91. $items[ $tmpItems[$i]['id'] ] = $tmpItems[ $i ];
  92. }
  93. $this->_cache->add( 'menu_items_'.$category['id'], $items );
  94. }
  95. // Gather all subitems/children for this menu item
  96. foreach( $items as &$item ) {
  97. $resource = 'menu-item-'.$item['id'];
  98. if ( $aclCheck && (!$this->_acl->resourceExists( $resource ) || !$this->_acl->check( $resource )) ) {
  99. unset( $items[ $item['id'] ] );
  100. } else if ( $withChildren ) {
  101. $item['children'] = $this->getChildItems( $item['id'], $aclCheck );
  102. }
  103. }
  104. return $flat ? zula_array_flatten($items, 'children') : $items;
  105. }
  106. /**
  107. * Gets all child menu items for a given item ID. Can check ACL resource
  108. * as well to limit the result set.
  109. *
  110. * @param int $itemId
  111. * @param bool $aclCheck
  112. * @return array
  113. */
  114. public function getChildItems( $itemId, $aclCheck=true, $depth=1 ) {
  115. $itemId = abs( $itemId );
  116. if ( !($items = $this->_cache->get( 'menu_child_items_'.$itemId )) ) {
  117. $items = array();
  118. $query = $this->_sql->query( 'SELECT * FROM {PREFIX}mod_menu
  119. WHERE heading_id = '.(int) $itemId.' ORDER BY `order` ASC' );
  120. $tmpItems = $query->fetchAll( PDO::FETCH_ASSOC );
  121. $count = count( $tmpItems );
  122. for( $i = 0; $i < $count; $i++ ) {
  123. $tmpItems[ $i ]['depth'] = $depth;
  124. $tmpItems[ $i ]['order_range'] = $count;
  125. $items[ $tmpItems[$i]['id'] ] = $tmpItems[ $i ];
  126. }
  127. $this->_cache->add( 'menu_child_items_'.$itemId, $items );
  128. }
  129. foreach( $items as $key=>$item ) {
  130. $resource = 'menu-item-'.$item['id'];
  131. if ( $aclCheck && (!$this->_acl->resourceExists( $resource ) || !$this->_acl->check( $resource )) ) {
  132. unset( $items[ $key ]);
  133. } else {
  134. $items[ $key ]['children'] = $this->getChildItems( $item['id'], $aclCheck, $depth+1 );
  135. }
  136. }
  137. return $items;
  138. }
  139. /**
  140. * Checks if a menu item exists by ID
  141. *
  142. * @param int $itemId
  143. * @return bool
  144. */
  145. public function itemExists( $itemId ) {
  146. try {
  147. $this->getItem( $itemId );
  148. return true;
  149. } catch ( Menu_ItemNoExist $e ) {
  150. return false;
  151. }
  152. }
  153. /**
  154. * Gets details for a single menu item by ID
  155. *
  156. * @param int $itemId
  157. * @return array
  158. */
  159. public function getItem( $itemId ) {
  160. $query = $this->_sql->query( 'SELECT * FROM {PREFIX}mod_menu WHERE id = '.(int) $itemId );
  161. $item = $query->fetch( PDO::FETCH_ASSOC );
  162. $query->closeCursor();
  163. if ( $item ) {
  164. return $item;
  165. } else {
  166. throw new Menu_ItemNoExist( $itemId );
  167. }
  168. }
  169. /**
  170. * Adds a new menu category and returns the id of it
  171. *
  172. * @param string $name
  173. * @return int|bool
  174. */
  175. public function addCategory( $name ) {
  176. $pdoSt = $this->_sql->prepare( 'INSERT INTO {PREFIX}mod_menu_cats (name) VALUES (?)' );
  177. $pdoSt->execute( array($name) );
  178. if ( $pdoSt->rowCount() ) {
  179. $id = $this->_sql->lastInsertId();
  180. $this->_cache->delete( 'menu_categories' );
  181. Hooks::notifyAll( 'menu_add_category', $id, $name );
  182. return $id;
  183. } else {
  184. return false;
  185. }
  186. }
  187. /**
  188. * Edits a menu category
  189. *
  190. * @param string $cid
  191. * @param string $name
  192. * @return bool
  193. */
  194. public function editCategory( $cid, $name ) {
  195. $category = $this->getCategory( $cid );
  196. $pdoSt = $this->_sql->prepare( 'UPDATE {PREFIX}mod_menu_cats SET name = ? WHERE ID = ?' );
  197. $result = $pdoSt->execute( array($name, $category['id']) );
  198. if ( $result ) {
  199. $this->_cache->delete( 'menu_categories' );
  200. Hooks::notifyAll( 'menu_edit_category', $category['id'], $name );
  201. return true;
  202. } else {
  203. return false;
  204. }
  205. }
  206. /**
  207. * Deletes a menu category by ID, and all menu items under it.
  208. *
  209. * @param int $cit
  210. * @return bool
  211. */
  212. public function deleteCategory( $cid ) {
  213. $category = $this->getCategory( $cid );
  214. // Gather menu items, so we can remove their ACL resource and cache later
  215. $itemIds = $this->_sql->query( 'SELECT id FROM {PREFIX}mod_menu WHERE cat_id = '.(int) $category['id'] )
  216. ->fetchAll( PDO::FETCH_COLUMN );
  217. // Remove the category and menu items
  218. $catRowCount = $this->_sql->query( 'DELETE FROM {PREFIX}mod_menu_cats WHERE id = '.(int) $category['id'] )
  219. ->rowCount();
  220. $this->_sql->query( 'DELETE FROM {PREFIX}mod_menu WHERE cat_id = '.(int) $category['id'] )
  221. ->closeCursor();
  222. if ( $catRowCount ) {
  223. $aclResources = array('menu-cat-'.$category['id']);
  224. $cacheKeys = array('menu_categories', 'menu_items_'.$category['id']);
  225. foreach( $itemIds as $id ) {
  226. $aclResources[] = 'menu-item-'.$id;
  227. $cacheKeys[] = 'menu_child_items_'.$id;
  228. }
  229. $this->_acl->deleteResource( $aclResources );
  230. $this->_cache->delete( $cacheKeys );
  231. Hooks::notifyAll( 'menu_delete_category', $category['id'] );
  232. return true;
  233. } else {
  234. return false;
  235. }
  236. }
  237. /**
  238. * Adds a new menu item to a category
  239. *
  240. * @param int $cid
  241. * @param string $name
  242. * @param int $heading
  243. * @param string $url
  244. * @param string $attrTitle
  245. * @return int|bool
  246. */
  247. public function addItem( $cid, $name, $heading=0, $url='', $attrTitle='' ) {
  248. $category = $this->getCategory( $cid );
  249. // Get next order id
  250. $order = $this->_sql->query( 'SELECT MAX(`order`)+1 AS item_order FROM {PREFIX}mod_menu
  251. WHERE cat_id = '.(int) $category['id'] )
  252. ->fetch( PDO::FETCH_COLUMN );
  253. // Insert new item
  254. $heading = abs( $heading );
  255. $pdoSt = $this->_sql->prepare( 'INSERT INTO {PREFIX}mod_menu (cat_id, name, heading_id, url, attr_title, `order`)
  256. VALUES(?, ?, ?, ?, ?, ?)' );
  257. $pdoSt->execute( array(
  258. $category['id'], $name, $heading,
  259. $url, $attrTitle, (empty($order) ? 1 : $order)
  260. ));
  261. $pdoSt->closeCursor();
  262. if ( $pdoSt->rowCount() ) {
  263. $id = $this->_sql->lastInsertId();
  264. $this->_cache->delete( 'menu_items_'.$category['id'] );
  265. if ( $heading > 0 ) {
  266. $this->_cache->delete( 'menu_child_items_'.$heading );
  267. }
  268. Hooks::notifyAll( 'menu_add_item', $id );
  269. return $id;
  270. } else {
  271. return false;
  272. }
  273. }
  274. /**
  275. * Edits a single menu item by ID
  276. *
  277. * @param int $id
  278. * @param string $name
  279. * @param int $heading
  280. * @param string $url
  281. * @param string $attrTitle
  282. * @param int $order
  283. * @return bool
  284. */
  285. public function editItem( $id, $name, $heading=0, $url='', $attrTitle='', $order=null ) {
  286. $item = $this->getItem( $id );
  287. $details = array(
  288. 'id' => $item['id'],
  289. 'name' => $name,
  290. 'heading' => abs($heading),
  291. 'url' => $url,
  292. 'attr_title'=> $attrTitle,
  293. 'order' => $order,
  294. );
  295. $stmt = 'UPDATE {PREFIX}mod_menu SET name = :name, heading_id = :heading, url = :url, attr_title = :attr_title';
  296. if ( is_null( $details['order'] ) ) {
  297. unset( $details['order'] );
  298. } else {
  299. $stmt .= ', `order` = :order';
  300. }
  301. $pdoSt = $this->_sql->prepare( $stmt.' WHERE id = :id' );
  302. if ( $pdoSt->execute( $details ) ) {
  303. $this->_cache->delete( array('menu_items_'.$item['cat_id'], 'menu_child_items_'.$item['heading_id']) );
  304. Hooks::notifyAll( 'menu_edit_item', $item['id'], $details );
  305. return true;
  306. } else {
  307. return false;
  308. }
  309. }
  310. /**
  311. * Deletes a menu item, and all items under it.
  312. *
  313. * @param int $id
  314. * @return bool
  315. */
  316. public function deleteItem( $id ) {
  317. $item = $this->getItem( $id );
  318. // Get all menu item IDs to delete ACL resources and cache later on
  319. $itemIds = $this->_sql->query( 'SELECT id, heading_id FROM {PREFIX}mod_menu
  320. WHERE id = '.(int) $item['id'].' OR heading_id = '.(int) $item['id'] )
  321. ->fetchAll( PDO::FETCH_ASSOC );
  322. // Remove all menu items
  323. $pdoSt = $this->_sql->prepare( 'DELETE FROM {PREFIX}mod_menu WHERE id = :id OR heading_id = :id' );
  324. $pdoSt->execute( array(':id' => $item['id']) );
  325. if ( $pdoSt->rowCount() ) {
  326. $aclResources = array();
  327. $cacheKeys = array('menu_items_'.$item['cat_id']);
  328. foreach( $itemIds as $tmpItem ) {
  329. $aclResources[] = 'menu-item-'.$tmpItem['id'];
  330. $cacheKeys[] = 'menu_child_items_'.$tmpItem['heading_id'];
  331. }
  332. $this->_acl->deleteResource( $aclResources );
  333. $this->_cache->delete( $cacheKeys );
  334. Hooks::notifyAll( 'menu_delete_item', $item['id'], $item );
  335. return true;
  336. } else {
  337. return false;
  338. }
  339. }
  340. }
  341. ?>