browsebrick.class.inc.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. <?php
  2. // Copyright (C) 2010-2015 Combodo SARL
  3. //
  4. // This file is part of iTop.
  5. //
  6. // iTop is free software; you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // iTop is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with iTop. If not, see <http://www.gnu.org/licenses/>
  18. namespace Combodo\iTop\Portal\Brick;
  19. use \DOMFormatException;
  20. use \Combodo\iTop\DesignElement;
  21. use \Combodo\iTop\Portal\Brick\PortalBrick;
  22. /**
  23. * Description of BrowseBrick
  24. *
  25. * @author Guillaume Lajarige
  26. */
  27. class BrowseBrick extends PortalBrick
  28. {
  29. const DEFAULT_HOME_ICON_CLASS = 'fa fa-map';
  30. const DEFAULT_NAVIGATION_MENU_ICON_CLASS = 'fa fa-map fa-2x';
  31. const ENUM_BROWSE_MODE_LIST = 'list';
  32. const ENUM_BROWSE_MODE_TREE = 'tree';
  33. const ENUM_BROWSE_MODE_GRID = 'grid';
  34. const ENUM_ACTION_VIEW = 'view';
  35. const ENUM_ACTION_EDIT = 'edit';
  36. const ENUM_ACTION_DRILLDOWN = 'drilldown';
  37. const ENUM_ACTION_CREATE_FROM_THIS = 'create_from_this';
  38. const ENUM_ACTION_ICON_CLASS_VIEW = 'glyphicon glyphicon-list-alt';
  39. const ENUM_ACTION_ICON_CLASS_EDIT = 'glyphicon glyphicon-pencil';
  40. const ENUM_ACTION_ICON_CLASS_DRILLDOWN = 'glyphicon glyphicon-menu-down';
  41. const ENUM_ACTION_ICON_CLASS_CREATE_FROM_THIS = 'glyphicon glyphicon-edit';
  42. const ENUM_FACTORY_TYPE_METHOD = 'method';
  43. const ENUM_FACTORY_TYPE_CLASS = 'class';
  44. const DEFAULT_DATA_LOADING = self::ENUM_DATA_LOADING_FULL;
  45. const DEFAULT_LEVEL_NAME_ATT = 'name';
  46. const DEFAULT_BROWSE_MODE = self::ENUM_BROWSE_MODE_LIST;
  47. const DEFAULT_ACTION = self::ENUM_ACTION_DRILLDOWN;
  48. const DEFAULT_COUNT_PER_PAGE_LIST = 20;
  49. static $sRouteName = 'p_browse_brick';
  50. protected $aLevels;
  51. protected $aAvailablesBrowseModes;
  52. protected $sDefaultBrowseMode;
  53. public function __construct()
  54. {
  55. parent::__construct();
  56. $this->aLevels = array();
  57. $this->aAvailablesBrowseModes = array();
  58. $this->sDefaultBrowseMode = static::DEFAULT_BROWSE_MODE;
  59. }
  60. /**
  61. * Returns the brick levels
  62. *
  63. * @return array
  64. */
  65. public function GetLevels()
  66. {
  67. return $this->aLevels;
  68. }
  69. /**
  70. * Returns the brick availables browse modes
  71. *
  72. * @return array
  73. */
  74. public function GetAvailablesBrowseModes()
  75. {
  76. return $this->aAvailablesBrowseModes;
  77. }
  78. /**
  79. * Returns the brick default browse mode
  80. *
  81. * @return string
  82. */
  83. public function GetDefaultBrowseMode()
  84. {
  85. return $this->sDefaultBrowseMode;
  86. }
  87. /**
  88. * Sets the levels of the brick
  89. *
  90. * @param array $aLevels
  91. */
  92. public function SetLevels($aLevels)
  93. {
  94. $this->aLevels = $aLevels;
  95. return $this;
  96. }
  97. /**
  98. * Sets the availables browse modes of the brick
  99. *
  100. * @param array $aAvailablesBrowseModes
  101. */
  102. public function SetAvailablesBrowseModes($aAvailablesBrowseModes)
  103. {
  104. $this->aAvailablesBrowseModes = $aAvailablesBrowseModes;
  105. return $this;
  106. }
  107. /**
  108. * Sets the adefault browse mode of the brick
  109. *
  110. * @param string $sDefaultBrowseMode
  111. */
  112. public function SetDefaultBrowseMode($sDefaultBrowseMode)
  113. {
  114. $this->sDefaultBrowseMode = $sDefaultBrowseMode;
  115. return $this;
  116. }
  117. /**
  118. * Returns true if the brick has levels
  119. *
  120. * @return boolean
  121. */
  122. public function HasLevels()
  123. {
  124. return !empty($this->aLevels);
  125. }
  126. /**
  127. * Adds $aLevel to the list of levels for that brick
  128. *
  129. * @param array $aLevel
  130. * @return \Combodo\iTop\Portal\Brick\AbstractBrick
  131. */
  132. public function AddLevel($aLevel)
  133. {
  134. $this->aLevels[] = $aLevel;
  135. return $this;
  136. }
  137. /**
  138. * Removes $aLevel from the list of levels browse modes
  139. *
  140. * @param array $aLevel
  141. * @return \Combodo\iTop\Portal\Brick\AbstractBrick
  142. */
  143. public function RemoveLevels($aLevel)
  144. {
  145. if (isset($this->aLevels[$aLevel]))
  146. {
  147. unset($this->aLevels[$aLevel]);
  148. }
  149. return $this;
  150. }
  151. /**
  152. * Adds $sModeId to the list of availables browse modes for that brick
  153. *
  154. * @param string $sModeId
  155. * @param array $aData Hash array containing 'template' => TEMPLATE_PATH
  156. * @return \Combodo\iTop\Portal\Brick\AbstractBrick
  157. */
  158. public function AddAvailableBrowseMode($sModeId, $aData = array())
  159. {
  160. $this->aAvailablesBrowseModes[$sModeId] = $aData;
  161. return $this;
  162. }
  163. /**
  164. * Removes $sModeId from the list of availables browse modes
  165. *
  166. * @param string $sModeId
  167. * @return \Combodo\iTop\Portal\Brick\AbstractBrick
  168. */
  169. public function RemoveAvailableBrowseMode($sModeId)
  170. {
  171. if (isset($this->aAvailablesBrowseModes[$sModeId]))
  172. {
  173. unset($this->aAvailablesBrowseModes[$sModeId]);
  174. }
  175. return $this;
  176. }
  177. /**
  178. * Load the brick's data from the xml passed as a ModuleDesignElement.
  179. * This is used to set all the brick attributes at once.
  180. *
  181. * @param \Combodo\iTop\DesignElement $oMDElement
  182. * @return BrowseBrick
  183. * @throws DOMFormatException
  184. */
  185. public function LoadFromXml(DesignElement $oMDElement)
  186. {
  187. parent::LoadFromXml($oMDElement);
  188. // Checking specific elements
  189. foreach ($oMDElement->GetNodes('./*') as $oBrickSubNode)
  190. {
  191. switch ($oBrickSubNode->nodeName)
  192. {
  193. case 'levels':
  194. foreach ($oBrickSubNode->childNodes as $oLevelNode)
  195. {
  196. if ($oLevelNode->nodeName === 'level')
  197. {
  198. $this->AddLevel($this->LoadLevelFromXml($oLevelNode));
  199. }
  200. }
  201. break;
  202. case 'browse_modes':
  203. foreach ($oBrickSubNode->childNodes as $oBrowseModeNode)
  204. {
  205. switch ($oBrowseModeNode->nodeName)
  206. {
  207. case 'availables':
  208. foreach ($oBrowseModeNode->childNodes as $oModeNode)
  209. {
  210. if (!$oModeNode->hasAttribute('id'))
  211. {
  212. throw new DOMFormatException('BrowseBrick : Browse mode must have a unique ID attribute', null, null, $oModeNode);
  213. }
  214. $sModeId = $oModeNode->getAttribute('id');
  215. $aModeData = array();
  216. // Checking if the browse mode has a specific template
  217. $oTemplateNode = $oModeNode->GetOptionalElement('template');
  218. if (($oTemplateNode !== null) && ($oTemplateNode->GetText() !== null))
  219. {
  220. $sTemplatePath = $oTemplateNode->GetText();
  221. }
  222. else
  223. {
  224. $sTemplatePath = 'itop-portal-base/portal/src/views/bricks/browse/mode_' . $sModeId . '.html.twig';
  225. }
  226. $aModeData['template'] = $sTemplatePath;
  227. $this->AddAvailableBrowseMode($sModeId, $aModeData);
  228. }
  229. break;
  230. case 'default':
  231. $this->SetDefaultBrowseMode($oBrowseModeNode->GetText(static::DEFAULT_BROWSE_MODE));
  232. break;
  233. }
  234. }
  235. break;
  236. }
  237. }
  238. // Checking that the brick has at least a browse mode
  239. if (count($this->GetAvailablesBrowseModes()) === 0)
  240. {
  241. throw new DOMFormatException('BrowseBrick : Must have at least one browse mode', null, null, $oMDElement);
  242. }
  243. // Checking that default browse mode in among the availables
  244. if (!in_array($this->sDefaultBrowseMode, array_keys($this->aAvailablesBrowseModes)))
  245. {
  246. throw new DOMFormatException('BrowseBrick : Default browse mode "' . $this->sDefaultBrowseMode . '" must be one of the available browse modes (' . implode(', ', $this->aAvailablesBrowseModes) . ')', null, null, $oMDElement);
  247. }
  248. // Checking that the brick has at least a level
  249. if (count($this->GetLevels()) === 0)
  250. {
  251. throw new DOMFormatException('BrowseBrick : Must have at least one level', null, null, $oMDElement);
  252. }
  253. return $this;
  254. }
  255. /**
  256. * Parses the ModuleDesignElement to recursivly load levels
  257. *
  258. * @param \Combodo\iTop\DesignElement $oMDElement
  259. * @return array
  260. * @throws DOMFormatException
  261. */
  262. protected function LoadLevelFromXml(DesignElement $oMDElement)
  263. {
  264. $aLevel = array(
  265. 'parent_att' => null,
  266. 'tooltip_att' => null,
  267. 'description_att' => null,
  268. 'image_att' => null,
  269. 'title' => null,
  270. 'name_att' => static::DEFAULT_LEVEL_NAME_ATT,
  271. 'fields' => array(),
  272. 'actions' => array('default' => array('type' => static::DEFAULT_ACTION, 'rules' => array()))
  273. );
  274. // Getting level ID
  275. if ($oMDElement->hasAttribute('id') && $oMDElement->getAttribute('id') !== '')
  276. {
  277. $aLevel['id'] = $oMDElement->getAttribute('id');
  278. }
  279. else
  280. {
  281. throw new DOMFormatException('BrowseBrick : level tag without "id" attribute. It must have one and it must not be empty', null, null, $oMDElement);
  282. }
  283. // Getting level properties
  284. foreach ($oMDElement->childNodes as $oLevelPropertyNode)
  285. {
  286. switch ($oLevelPropertyNode->nodeName)
  287. {
  288. case 'class':
  289. $sClass = $oLevelPropertyNode->GetText();
  290. if ($sClass === '')
  291. {
  292. throw new DOMFormatException('BrowseBrick : class tag is empty. Must contain Classname', null, null, $oLevelPropertyNode);
  293. }
  294. $aLevel['oql'] = 'SELECT ' . $sClass;
  295. break;
  296. case 'oql':
  297. $sOql = $oLevelPropertyNode->GetText();
  298. if ($sOql === '')
  299. {
  300. throw new DOMFormatException('BrowseBrick : oql tag is empty. Must contain OQL statement', null, null, $oLevelPropertyNode);
  301. }
  302. $aLevel['oql'] = $sOql;
  303. break;
  304. case 'parent_att':
  305. case 'tooltip_att':
  306. case 'description_att':
  307. case 'image_att':
  308. case 'title':
  309. $aLevel[$oLevelPropertyNode->nodeName] = $oLevelPropertyNode->GetText(null);
  310. break;
  311. case 'name_att':
  312. $aLevel[$oLevelPropertyNode->nodeName] = $oLevelPropertyNode->GetText(static::DEFAULT_LEVEL_NAME_ATT);
  313. break;
  314. case 'fields':
  315. $sTagName = $oLevelPropertyNode->nodeName;
  316. if ($oLevelPropertyNode->hasChildNodes())
  317. {
  318. $aLevel[$sTagName] = array();
  319. foreach ($oLevelPropertyNode->childNodes as $oFieldNode)
  320. {
  321. if ($oFieldNode->hasAttribute('id') && $oFieldNode->getAttribute('id') !== '')
  322. {
  323. $aLevel[$sTagName][$oFieldNode->getAttribute('id')] = array('hidden' => false);
  324. }
  325. else
  326. {
  327. throw new DOMFormatException('BrowseBrick : ' . $sTagName . '/* tag must have an "id" attribute and it must not be empty', null, null, $oFieldNode);
  328. }
  329. $oFieldSubNode = $oFieldNode->GetOptionalElement('hidden');
  330. if ($oFieldSubNode !== null)
  331. {
  332. $aLevel[$sTagName][$oFieldNode->getAttribute('id')]['hidden'] = ($oFieldSubNode->GetText() === 'true') ? true : false;
  333. }
  334. }
  335. }
  336. break;
  337. case 'actions':
  338. $sTagName = $oLevelPropertyNode->nodeName;
  339. if ($oLevelPropertyNode->hasChildNodes())
  340. {
  341. $aLevel[$sTagName] = array();
  342. foreach ($oLevelPropertyNode->childNodes as $oActionNode)
  343. {
  344. if ($oActionNode->hasAttribute('id') && $oActionNode->getAttribute('id') !== '')
  345. {
  346. $aTmpAction = array(
  347. 'type' => null,
  348. 'rules' => array()
  349. );
  350. // Action type
  351. $aTmpAction['type'] = ($oActionNode->hasAttribute('xsi:type') && $oActionNode->getAttribute('xsi:type') !== '') ? $oActionNode->getAttribute('xsi:type') : static::DEFAULT_ACTION;
  352. // Action destination class
  353. if ($aTmpAction['type'] === static::ENUM_ACTION_CREATE_FROM_THIS)
  354. {
  355. if ($oActionNode->GetOptionalElement('factory_method') !== null)
  356. {
  357. $aTmpAction['factory'] = array(
  358. 'type' => static::ENUM_FACTORY_TYPE_METHOD,
  359. 'value' => $oActionNode->GetOptionalElement('factory_method')->GetText()
  360. );
  361. }
  362. else
  363. {
  364. $aTmpAction['factory'] = array(
  365. 'type' => static::ENUM_FACTORY_TYPE_CLASS,
  366. 'value' => $oActionNode->GetUniqueElement('class')->GetText()
  367. );
  368. }
  369. }
  370. // Action title
  371. $oActionTitleNode = $oActionNode->GetOptionalElement('title');
  372. if ($oActionTitleNode !== null)
  373. {
  374. $aTmpAction['title'] = $oActionTitleNode->GetText();
  375. }
  376. // Action icon class
  377. $oActionIconClassNode = $oActionNode->GetOptionalElement('icon_class');
  378. if ($oActionIconClassNode !== null)
  379. {
  380. $aTmpAction['icon_class'] = $oActionIconClassNode->GetText();
  381. }
  382. // Action rules
  383. foreach ($oActionNode->GetNodes('./rules/rule') as $oRuleNode)
  384. {
  385. if ($oRuleNode->hasAttribute('id') && $oRuleNode->getAttribute('id') !== '')
  386. {
  387. $aTmpAction['rules'][] = $oRuleNode->getAttribute('id');
  388. }
  389. else
  390. {
  391. throw new DOMFormatException('BrowseBrick : ' . $sTagName . '/rules/rule tag must have an "id" attribute and it must not be empty', null, null, $oRuleNode);
  392. }
  393. }
  394. $aLevel[$sTagName][$oActionNode->getAttribute('id')] = $aTmpAction;
  395. }
  396. else
  397. {
  398. throw new DOMFormatException('BrowseBrick : ' . $sTagName . '/* tag must have an "id" attribute and it must not be empty', null, null, $oActionNode);
  399. }
  400. }
  401. }
  402. break;
  403. case 'levels':
  404. foreach ($oLevelPropertyNode->childNodes as $oSubLevelNode)
  405. {
  406. if ($oSubLevelNode->nodeName === 'level')
  407. {
  408. $aLevel['levels'][] = $this->LoadLevelFromXml($oSubLevelNode);
  409. }
  410. }
  411. break;
  412. }
  413. }
  414. // Checking if level has an oql
  415. if (!isset($aLevel['oql']) || $aLevel['oql'] === '')
  416. {
  417. throw new DOMFormatException('BrowseBrick : must have a valid <class|oql> tag', null, null, $oMDElement);
  418. }
  419. return $aLevel;
  420. }
  421. }