/library/iPMS/Widgets/Container.php

https://github.com/nuxwin/i-PMS · PHP · 373 lines · 161 code · 38 blank · 174 comment · 16 complexity · 86c4287eca12af81bc9dffcbcee48181 MD5 · raw file

  1. <?php
  2. /**
  3. * i-PMS - internet Project Management System
  4. * Copyright (C) 2011 by Laurent Declercq
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program 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 General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. *
  20. * @category iPMS
  21. * @copyright 2011 by Laurent Declercq
  22. * @author Laurent Declercq <l.declercq@nuxwin.com>
  23. * @version 0.0.1
  24. * @link http://www.i-pms.net i-PMS Home Site
  25. * @license http://www.gnu.org/licenses/gpl-2.0.html GPL v2
  26. */
  27. /**
  28. * Widgets container
  29. *
  30. * @package iPMS
  31. * @subpackage iPMS_Widgets
  32. * @author Laurent Declercq <l.declercq@nuxwin.com>
  33. * @version 0.0.1
  34. */
  35. class iPMS_Widgets_Container implements Iterator, Countable
  36. {
  37. /**
  38. * Contains Widgets
  39. *
  40. * @var array
  41. */
  42. protected $_widgets = array();
  43. /**
  44. * An index that contains the order in which to iterate Widgets
  45. *
  46. * @var array
  47. */
  48. protected $_index = array();
  49. /**
  50. * Whether index is dirty and needs to be re-arranged
  51. *
  52. * @var bool
  53. */
  54. protected $_dirtyIndex = false;
  55. /**
  56. * Creates a new Widgets container
  57. *
  58. * @param array|Zend_Config $widgets [optional] Widgets to add
  59. * @throws iPMS_Widgets_Exception if $Widgets is invalid
  60. */
  61. public function __construct($widgets = null)
  62. {
  63. if (is_array($widgets) || $widgets instanceof Zend_Config) {
  64. $this->addWidgets($widgets);
  65. } elseif (null !== $widgets) {
  66. require_once 'iPMS/Widgets/Exception.php';
  67. throw new iPMS_Widgets_Exception(
  68. 'Invalid argument: $Widgets must be an array, an instance of Zend_Config, or null'
  69. );
  70. }
  71. }
  72. /**
  73. * Sorts the widget index according to widget order
  74. *
  75. * @return void
  76. */
  77. protected function _sort()
  78. {
  79. if ($this->_dirtyIndex) {
  80. $newIndex = array();
  81. $index = 0;
  82. /**
  83. * @var $widget iPMS_Widget
  84. */
  85. foreach ($this->_widgets as $hash => $widget) {
  86. $order = $widget->getOrder();
  87. if ($order === null) {
  88. $newIndex[$hash] = $index;
  89. $index++;
  90. } else {
  91. $newIndex[$hash] = $order;
  92. }
  93. }
  94. asort($newIndex);
  95. $this->_index = $newIndex;
  96. $this->_dirtyIndex = false;
  97. }
  98. }
  99. /**
  100. * Notifies container that the order of widget are updated
  101. *
  102. * @return void
  103. */
  104. public function notifyOrderUpdated()
  105. {
  106. $this->_dirtyIndex = true;
  107. }
  108. /**
  109. * Adds a widget to the container
  110. *
  111. * This method will inject the container as the given page's parent by
  112. * calling {@link iPMS_Widget::setParent()}.
  113. *
  114. * @param iPMS_Widget|array|Zend_Config $widget widget to add
  115. * @return iPMS_Widgets_Container fluent interface, returns self
  116. * @throws iPMS_Widgets_Exception if widget is invalid
  117. */
  118. public function addWidget($widget)
  119. {
  120. if (is_array($widget) || $widget instanceof Zend_Config) {
  121. require_once 'iPMS/Widget.php';
  122. $widget = iPMS_Widget::factory($widget);
  123. } elseif (!$widget instanceof iPMS_Widget) {
  124. require_once 'iPMS/Widgets/Exception.php';
  125. throw new iPMS_Widgets_Exception(
  126. 'Invalid argument: $widget must be an instance of iPMS_Widget or Zend_Config, or an array'
  127. );
  128. }
  129. // Retrieve the widget hash
  130. $hash = $widget->hashCode();
  131. if (array_key_exists($hash, $this->_index)) {
  132. // widget is already in container
  133. return $this;
  134. }
  135. // adds page to container and sets dirty flag
  136. $this->_widgets[$hash] = $widget;
  137. $this->_index[$hash] = $widget->getOrder();
  138. $this->_dirtyIndex = true;
  139. // inject self as widget parent
  140. //$widget->setParent($this);
  141. return $this;
  142. }
  143. /**
  144. * Adds several Widgets at once
  145. *
  146. * @param array $widgets Widgets to add
  147. * @return iPMS_Widgets_Container fluent interface, returns self
  148. * @throws iPMS_Widgets_Exception if $Widgets is not array
  149. */
  150. public function addWidgets($widgets)
  151. {
  152. if (!is_array($widgets)) {
  153. require_once 'iPMS/Widgets/Exception.php';
  154. throw new iPMS_Widgets_Exception('Invalid argument: $widget must be an array');
  155. }
  156. foreach ($widgets as $widget) {
  157. $this->addWidget($widget);
  158. }
  159. return $this;
  160. }
  161. /**
  162. * Sets widgets this container should have, removing existing widgets
  163. *
  164. * @param array $widgets widgets to set
  165. * @return iPMS_Widget_Container fluent interface, returns self
  166. */
  167. public function setWidgets(array $widgets)
  168. {
  169. $this->removeWidgets();
  170. return $this->addWidgets($widgets);
  171. }
  172. /**
  173. * Returns widgets in the container
  174. *
  175. * @return array array of iPMS_Widget instances
  176. */
  177. public function getWidgets()
  178. {
  179. return $this->_widgets;
  180. }
  181. /**
  182. * Removes the given widget from the container
  183. *
  184. * @param iPMS_Widget|int $widget widget to remove, either a widget instance or a specific widget order
  185. * @return bool whether the removal was successful
  186. */
  187. public function removeWidget($widget)
  188. {
  189. if ($widget instanceof iPMS_Widget) {
  190. $hash = $widget->hashCode();
  191. } elseif (is_int($widget)) {
  192. $this->_sort();
  193. if (!$hash = array_search($widget, $this->_index)) {
  194. return false;
  195. }
  196. } else {
  197. return false;
  198. }
  199. if (isset($this->_widgets[$hash])) {
  200. unset($this->_widgets[$hash]);
  201. unset($this->_index[$hash]);
  202. $this->_dirtyIndex = true;
  203. return true;
  204. }
  205. return false;
  206. }
  207. /**
  208. * Removes all widget in container
  209. *
  210. * @return iPMS_Widgets_Container fluent interface, returns self
  211. */
  212. public function removeWidgets()
  213. {
  214. $this->_widgets = array();
  215. $this->_index = array();
  216. return $this;
  217. }
  218. /**
  219. * Returns true if container contains any widgets
  220. *
  221. * @return bool whether container has any widgets
  222. */
  223. public function hasWidgets()
  224. {
  225. return count($this->_index) > 0;
  226. }
  227. /**
  228. * Returns current widget
  229. *
  230. * Implements Iterator interface.
  231. *
  232. * @return iPMS_Widget current widget or null
  233. * @throws iPMS_Widgets_Exception if the index is invalid
  234. */
  235. public function current()
  236. {
  237. $this->_sort();
  238. current($this->_index);
  239. $hash = key($this->_index);
  240. if (isset($this->_widgets[$hash])) {
  241. return $this->_widgets[$hash];
  242. } else {
  243. require_once 'iPMS/Widgets/Exception.php';
  244. throw new iPMS_Widgets_Exception(
  245. 'Corruption detected in container; invalid key found in internal iterator'
  246. );
  247. }
  248. }
  249. /**
  250. * Returns hash code of current widget
  251. *
  252. * Implements Iterator interface.
  253. *
  254. * @return string hash code of current widget
  255. */
  256. public function key()
  257. {
  258. $this->_sort();
  259. return key($this->_index);
  260. }
  261. /**
  262. * Moves index pointer to next widget in the container
  263. *
  264. * Implements Iterator interface.
  265. *
  266. * @return void
  267. */
  268. public function next()
  269. {
  270. $this->_sort();
  271. next($this->_index);
  272. }
  273. /**
  274. * Sets index pointer to first widget in the container
  275. *
  276. * Implements Iterator interface.
  277. *
  278. * @return void
  279. */
  280. public function rewind()
  281. {
  282. $this->_sort();
  283. reset($this->_index);
  284. }
  285. /**
  286. * Checks if container index is valid
  287. *
  288. * Implements Iterator interface.
  289. *
  290. * @return bool
  291. */
  292. public function valid()
  293. {
  294. $this->_sort();
  295. return current($this->_index) !== false;
  296. }
  297. /**
  298. * Proxy to hasWidgets()
  299. *
  300. * Implements Iterator interface.
  301. *
  302. * @return bool whether container has any widgets
  303. */
  304. public function hasChildren()
  305. {
  306. return $this->hasWidgets();
  307. }
  308. /**
  309. * Returns the child container.
  310. *
  311. * Implements Iterator interface.
  312. *
  313. * @return iPMS_Widget null
  314. */
  315. public function getChildren()
  316. {
  317. $hash = key($this->_index);
  318. if (isset($this->_widgets[$hash])) {
  319. return $this->_widgets[$hash];
  320. }
  321. return null;
  322. }
  323. // Countable interface:
  324. /**
  325. * Returns number of widgets in container
  326. *
  327. * Implements Countable interface.
  328. *
  329. * @return int number of widgets in the container
  330. */
  331. public function count()
  332. {
  333. return count($this->_index);
  334. }
  335. }