PageRenderTime 54ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/core/types/ParentGraphNode.php

https://bitbucket.org/audax/testmaker-mod
PHP | 499 lines | 332 code | 64 blank | 103 comment | 75 complexity | a1f799ad81349123583a221ab547e1c9 MD5 | raw file
Possible License(s): BSD-2-Clause, AGPL-1.0, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /* This file is part of testMaker.
  3. testMaker is free software; you can redistribute it and/or modify
  4. it under the terms of version 2 of the GNU General Public License as
  5. published by the Free Software Foundation.
  6. testMaker is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  12. /**
  13. * @package Core
  14. */
  15. /**
  16. * Needs the class it inherites from
  17. */
  18. require_once(dirname(__FILE__).'/GraphNode.php');
  19. /**
  20. * This class is an abstract concept for nodes.
  21. *
  22. * @abstract
  23. * @package Core
  24. */
  25. class ParentGraphNode extends GraphNode
  26. {
  27. /**#@+
  28. * @access private
  29. */
  30. var $childrenSequence;
  31. /**#@-*/
  32. /**
  33. * This constructor has to be overwritten.
  34. * In the overwriting constructor the variable $this->table, $this->connectTable, $this->connectId, $this->connectParentId and $this->childrenSequence have to be set.
  35. * After initializing this variable the overwring constructor has to call this overwritten constructor.
  36. * @param integer ID of the node
  37. */
  38. function ParentGraphNode($id)
  39. {
  40. $this->db = &$GLOBALS['dao']->getConnection();
  41. if(!isset($this->childrenSequence))
  42. {
  43. trigger_error('<b>ParentGraphNode</b>: $this->childrenSequence was not set');
  44. }
  45. $this->GraphNode($id);
  46. }
  47. function _getTableByType($type)
  48. {
  49. return $this->table;
  50. }
  51. /**
  52. * Returns the child nodes of the current node ordered by position
  53. * @return GraphNode[]
  54. */
  55. function &getChildren()
  56. {
  57. //Thr ObjectCache don't work correct. Until the problem is fixed its comment out.
  58. /*if ($res = retrieve(get_class($this) .'Children', $this->id)) {
  59. return $res;
  60. }*/
  61. $nodes = array();
  62. $query = 'SELECT '.$this->connectId.' FROM '.$this->connectTable.' WHERE '.$this->connectParentId.' = ? ORDER BY pos';
  63. $ids = $this->db->getAll($query, array($this->id));
  64. if($this->db->isError($ids)) {
  65. return false;
  66. }
  67. for($i = 0; $i < count($ids); $i++)
  68. {
  69. if($ids[$i][$this->connectId] != 0)
  70. {
  71. $nodes[] = $this->_returnNode($ids[$i][$this->connectId]);
  72. }
  73. }
  74. store(get_class($this) .'Children', $this->id, $nodes);
  75. return $nodes;
  76. }
  77. /**
  78. * Returns the number of children of this node.
  79. * @return integer
  80. */
  81. function getChildrenCount()
  82. {
  83. return $this->db->getOne('SELECT COUNT(*) FROM '.$this->connectTable.' WHERE '.$this->connectParentId.' = ?', array($this->id));
  84. }
  85. /**
  86. * Returns the parent nodes of the current node
  87. * @return GraphNode[]
  88. */
  89. function getChildById($child)
  90. {
  91. if(!$this->existsChild($child))
  92. {
  93. trigger_error('<b>ParentGraphNode:getChildById</b>: $parent is no parent of node');
  94. return false;
  95. }
  96. return $this->_returnNode($child);
  97. }
  98. /**
  99. * Returns the child at given position
  100. * @param integer position of the block
  101. * @return GraphNode
  102. */
  103. function getChildByPosition($position)
  104. {
  105. if(!$this->existsChildAtPosition($position)) {
  106. trigger_error('<b>ParentGraphNode:getChild</b>: $position at current is empty!');
  107. return false;
  108. }
  109. $query = 'SELECT '.$this->connectId.' FROM '.$this->connectTable.' WHERE '.$this->connectParentId.' = ? AND pos = ?';
  110. $id = $this->db->getOne($query, array($this->id, $position));
  111. if($this->db->isError($id)) {
  112. return false;
  113. }
  114. return $this->_returnNode($id);
  115. }
  116. /**
  117. * Returns if the given node is a child of current node
  118. * @param integer ID of the node to check if it is a child
  119. * @return integer
  120. */
  121. function existsChild($child)
  122. {
  123. $query = 'SELECT count('.$this->connectId.') FROM '.$this->connectTable.' WHERE '.$this->connectId.' = ? AND '.$this->connectParentId.' = ?';
  124. $num = $this->db->getOne($query, array($child, $this->id));
  125. if($this->db->isError($num)) {
  126. return false;
  127. }
  128. if($num == 1)
  129. {
  130. return true;
  131. }
  132. elseif ($num == 0)
  133. {
  134. return false;
  135. }
  136. else
  137. {
  138. trigger_error('<b>ParentGraphNode:existsChild</b>: database is inconsistent');
  139. return false;
  140. }
  141. }
  142. /**
  143. * Returns if any child exists at given position
  144. * @param integer position of node in current node to check if it exists
  145. * @return integer
  146. */
  147. function existsChildAtPosition($position)
  148. {
  149. $query = 'SELECT count('.$this->connectId.') FROM '.$this->connectTable.' WHERE '.$this->connectParentId.' = ? AND pos = ?';
  150. $num = $this->db->getOne($query, array($this->id, $position));
  151. if($this->db->isError($num)) {
  152. return false;
  153. }
  154. if($num == 1)
  155. {
  156. return true;
  157. }
  158. elseif ($num == 0)
  159. {
  160. return false;
  161. }
  162. else
  163. {
  164. trigger_error('<b>ParentGraphNode:existsChildAtPosition</b>: database is inconsistent');
  165. return false;
  166. }
  167. }
  168. /**
  169. * returns the next free position for a child node
  170. * @return integer
  171. */
  172. function getNextFreePosition()
  173. {
  174. $query = 'SELECT pos FROM '.$this->connectTable.' WHERE '.$this->connectParentId.' = ? ORDER BY pos DESC LIMIT 1';
  175. if(!($position = $this->db->getOne($query, array($this->id))))
  176. {
  177. return 1;
  178. }
  179. else
  180. {
  181. if($this->db->isError($position)) {
  182. return false;
  183. }
  184. return (((int) $position) + 1);
  185. }
  186. }
  187. /**
  188. * Creates and returns a new node of the given type
  189. * @param integer $position position of node in parent node
  190. * @param mixed[] $optionalinfo associative array with optionalinformations (title, description)
  191. * @return GraphNode
  192. */
  193. function _createChild($type, $data = array())
  194. {
  195. if(!is_array($data))
  196. {
  197. trigger_error('<b>ParentGraphNode::createChild</b>: $data is no array');
  198. return false;
  199. }
  200. if(!array_key_exists('pos', $data) || $data['pos'] == NULL)
  201. {
  202. $position = $this->getNextFreePosition();
  203. }
  204. elseif($this->existsChildAtPosition($data['pos']))
  205. {
  206. $tmpblock = $this->getChildByPosition($data['pos']);
  207. $tmpblock->setPosition(($tmpblock->getPosition($parent) + 1), $this->id);
  208. $position = $data['pos'];
  209. }
  210. else
  211. {
  212. $position = $data['pos'];
  213. }
  214. if(!array_key_exists('id', $data) || !preg_match('/^[0-9]+$/', $data['id']))
  215. {
  216. $id = $this->db->nextId($this->childrenSequence);
  217. if($this->db->isError($id)) {
  218. return false;
  219. }
  220. $data['id'] = $id;
  221. }
  222. $owner = $this->getOwner();
  223. $data['owner'] = $owner->getId();
  224. $data['t_created'] = time();
  225. $data['t_modified'] = time();
  226. $data['u_created'] = $GLOBALS['PORTAL']->getUserId();
  227. $data['u_modified'] = $GLOBALS['PORTAL']->getUserId();
  228. $values = '';
  229. $columns = '';
  230. for(reset($data); list($column, $value) = each($data); )
  231. {
  232. if(strlen($columns) > 0)
  233. {
  234. $columns .= ', ';
  235. }
  236. if(strlen($values) > 0)
  237. {
  238. $values .= ', ';
  239. }
  240. $columns .= $column;
  241. $values .= $this->db->quoteSmart($value);
  242. }
  243. $query = 'INSERT INTO '.$this->_getTableByType($type).' ('.$columns.') VALUES ('.$values.')';
  244. $result = $this->db->query($query);
  245. if($this->db->isError($result)) {
  246. return false;
  247. }
  248. $query = 'INSERT INTO '.$this->connectTable.' (id, parent_id, pos) VALUES (?, ?, ?)';
  249. $result = $this->db->query($query, array($data['id'], $this->id, $position));
  250. if($this->db->isError($result)) {
  251. return false;
  252. }
  253. return $this->_returnNode($data['id']);
  254. }
  255. /**
  256. * deletes the given node from the current node and removes all children if they are not children of any other block
  257. * @param integer $id id of node to delete
  258. * @return boolean
  259. */
  260. function deleteChild($id)
  261. {
  262. if(!$this->existsChild($id))
  263. {
  264. trigger_error('<b>ParentGraphNode:deleteChild</b>: $id does not exist');
  265. return false;
  266. }
  267. $block = $this->getChildById($id);
  268. $parentBlocks = $block->getParents();
  269. $foundDifferentParent = false;
  270. for($i = 0; $i < count($parentBlocks); $i++)
  271. {
  272. if($parentBlocks[$i]->getId() != $this->id)
  273. {
  274. $foundDifferentParent = true;
  275. }
  276. }
  277. $query = 'DELETE FROM '.$this->connectTable.' WHERE '.$this->connectId.' = ? AND '.$this->connectParentId.' = ?';
  278. $result = $this->db->query($query, array($id, $this->id));
  279. if($this->db->isError($result)) {
  280. return false;
  281. }
  282. if(!$foundDifferentParent)
  283. {
  284. $block->cleanUp();
  285. $query = 'DELETE FROM '.$this->_getTableByType($block->getBlockType()).' WHERE id = ?';
  286. $result = $this->db->query($query, array($id));
  287. if($this->db->isError($result)) {
  288. return false;
  289. }
  290. }
  291. return true;
  292. }
  293. /**
  294. * Returns an array of all nodes inside the current node in correct order
  295. * The modifications array has to be orgnaized as an 2 dimensional array,
  296. * in the first dimension all modifications are listed. In the second dimension the fields
  297. * 'id', 'parent', 'position' and 'type' have to be given. In 'id', 'parent' (and 'position') is the new/old position of the added/removed node given.
  298. * The field 'type' is the kind of modification (MODIFICATION_ADD = add, MODIFICATION_REMOVE = remove)
  299. * Example: $modifications = array(0 => array('id' => 5, 'parent' => 1, 'type' = 2), 1 => array('id' => 5, 'parent' => 1, 'position' => 2, 'type' = 1));
  300. * @param $modifications array of modifications to be checked
  301. * @return mixed[]
  302. */
  303. function listModifiedNode($modifications)
  304. {
  305. if(!is_array($modifications)) {
  306. trigger_error('<b>GraphNode:listModifiedNode()</b>: $modifications is no valid array');
  307. return false;
  308. }
  309. usort($modifications, array(get_class($this), '_compareModifications'));
  310. $children = $this->getChildren();
  311. $newIds = array();
  312. //add insert blocks at beginning
  313. for($j = 0; $j < count($modifications); $j++)
  314. {
  315. if(!isset($modifications[$j]['id']) || !isset($modifications[$j]['parent'])) {
  316. trigger_error('<b>GraphNode:listModifiedNode()</b>: $modifications['.$j.'] is no valid modification array');
  317. return false;
  318. }
  319. if($modifications[$j]['type'] == MODIFICATION_ADD) {
  320. if($modifications[$j]['parent'] == $this->id) {
  321. if(!isset($modifications[$j]['position'])) {
  322. trigger_error('<b>GraphNode:listModifiedNode()</b>: $modifications['.$j.'] is no valid modification array');
  323. return false;
  324. }
  325. if($modifications[$j]['position'] == 1)
  326. {
  327. $newIds[] = $modifications[$j]['id'];
  328. }
  329. }
  330. }
  331. }
  332. $lastPosition = 0;
  333. for($i = 0; $i < count($children); $i++)
  334. {
  335. $lastPosition = $children[$i]->getPosition($this->id);
  336. $continue = false;
  337. for($j = 0; $j < count($modifications); $j++)
  338. {
  339. if(!isset($modifications[$j]['id']) || !isset($modifications[$j]['parent'])) {
  340. trigger_error('<b>GraphNode:listModifiedNode()</b>: $modifications['.$j.'] is no valid modification array');
  341. return false;
  342. }
  343. switch($modifications[$j]['type']) {
  344. case MODIFICATION_ADD:
  345. if($modifications[$j]['parent'] == $this->id) {
  346. if(!isset($modifications[$j]['position'])) {
  347. trigger_error('<b>GraphNode:listModifiedNode()</b>: $modifications['.$j.'] is no valid modification array');
  348. return false;
  349. }
  350. if(($modifications[$j]['position'] >= $children[$i]->getPosition($this->id)) && ($modifications[$j]['position'] < $children[$i]->getNextUsedPosition($this->id)))
  351. {
  352. $newIds[] = $modifications[$j]['id'];
  353. }
  354. }
  355. break;
  356. case MODIFICATION_REMOVE:
  357. if($modifications[$j]['parent'] == $this->id) {
  358. if($modifications[$j]['id'] == $children[$i]->getId())
  359. {
  360. $continue = true;
  361. }
  362. }
  363. break;
  364. }
  365. }
  366. if($continue == true) {
  367. continue;
  368. }
  369. $newIds[] = $children[$i]->getId();
  370. }
  371. //add insert blocks at end
  372. for($j = 0; $j < count($modifications); $j++)
  373. {
  374. if(!isset($modifications[$j]['id']) || !isset($modifications[$j]['parent'])) {
  375. trigger_error('<b>GraphNode:listModifiedNode()</b>: $modifications['.$j.'] is no valid modification array');
  376. return false;
  377. }
  378. if($modifications[$j]['type'] == MODIFICATION_ADD)
  379. {
  380. if($modifications[$j]['parent'] == $this->id) {
  381. if(!isset($modifications[$j]['position'])) {
  382. trigger_error('<b>GraphNode:listModifiedNode()</b>: $modifications['.$j.'] is no valid modification array');
  383. return false;
  384. }
  385. if($modifications[$j]['position'] > $lastPosition)
  386. {
  387. $lastPosition = $modifications[$j]['position'];
  388. $newIds[] = $modifications[$j]['id'];
  389. }
  390. }
  391. }
  392. }
  393. $list = array();
  394. for($i = 0; $i < count($newIds); $i++)
  395. {
  396. $currentNode = $this->_returnNode($newIds[$i]);
  397. $entry = array('id' => $newIds[$i], 'type' => $currentNode->getBlockType());
  398. $list[] = $entry;
  399. $list = array_merge($list, $currentNode->listModifiedNode($modifications));
  400. }
  401. return $list;
  402. }
  403. /**
  404. * prepares everything to delete this block itself
  405. * @return boolean
  406. */
  407. function cleanUp()
  408. {
  409. $children = $this->getChildren();
  410. for($i = 0; $i < count($children); $i++) {
  411. $this->deleteChild($children[$i]->getId());
  412. }
  413. return parent::cleanUp();
  414. }
  415. /**
  416. * Returns a copy of the current node
  417. * @param target of target node
  418. * @param 2-dimensional array of changed ids. First dimension containing node type as name and seconde dimension assigns old id to new id.
  419. ß * @return Node
  420. */
  421. function copyNode($parentId, &$changedIds)
  422. {
  423. $newNode = parent::copyNode($parentId, $changedIds);
  424. $children = $this->getChildren();
  425. for($i = 0; $i < count($children); $i++)
  426. {
  427. $children[$i]->copyNode($newNode->getId(), $changedIds);
  428. }
  429. return $newNode;
  430. }
  431. }
  432. ?>