PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/core/types/GraphNode.php

https://bitbucket.org/audax/testmaker-mod
PHP | 424 lines | 274 code | 51 blank | 99 comment | 56 complexity | 8bb7403268a5a02480a0ea07be411fdd 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__).'/Node.php');
  19. /**
  20. * This class is an abstract concept for nodes.
  21. *
  22. * @abstract
  23. * @package Core
  24. */
  25. class GraphNode extends Node
  26. {
  27. /**#@+
  28. * @access private
  29. */
  30. var $connectTable;
  31. var $connectId;
  32. var $connectParentId;
  33. /**#@-*/
  34. /**
  35. * This constructor has to be overwritten.
  36. * In the overwriting constructor the variable $this->table, $this->connectTable, $this->connectId and $this->connectParentId have to be set.
  37. * After initializing this variable the overwring constructor has to call this overwritten constructor.
  38. * @param DB The database object
  39. * @param integer ID of the node
  40. */
  41. function GraphNode($id)
  42. {
  43. $this->db = &$GLOBALS['dao']->getConnection();
  44. if(!isset($this->connectTable)) {
  45. trigger_error('<b>GraphNode</b>: $this->connectTable was not set');
  46. }
  47. if(!isset($this->connectId)) {
  48. trigger_error('<b>GraphNode</b>: $this->connectId was not set');
  49. }
  50. if(!isset($this->connectParentId)) {
  51. trigger_error('<b>GraphNode</b>: $this->connectParentId was not set');
  52. }
  53. $this->Node($id);
  54. }
  55. /**
  56. * returns a graph node with the correct type, automatically determined
  57. * @return String
  58. */
  59. function _returnNode($id)
  60. {
  61. trigger_error('<b>GraphNode:_returnNode</b>: function not overwritten by inherited class');
  62. }
  63. /**
  64. * compare function to sort modifications array
  65. * @access private
  66. * @param $a element 1
  67. * @param $b element 2
  68. * @return int
  69. */
  70. function _compareModifications($a, $b) {
  71. if($a['parent'] < $b['parent']){
  72. return -1;
  73. } elseif($a['parent'] > $b['parent']) {
  74. return 1;
  75. } else {
  76. if(!isset($a['position']) && isset($b['position'])) {
  77. return -1;
  78. } elseif(isset($a['position']) && !isset($b['position'])) {
  79. return 1;
  80. } elseif(!isset($a['position']) && !isset($b['position'])) {
  81. return 0;
  82. }
  83. if($a['position'] < $b['position']) {
  84. return -1;
  85. } elseif($a['position'] > $b['position']) {
  86. return 1;
  87. } else {
  88. return 0;
  89. }
  90. }
  91. }
  92. /**
  93. * Returns the parent nodes of the current node
  94. * @return GraphNode[]
  95. */
  96. function &getParents()
  97. {
  98. $nodes = array();
  99. $query = 'SELECT '.$this->connectParentId.' FROM '.$this->connectTable.' WHERE '.$this->connectId.' = ?';
  100. $ids = $this->db->getAll($query, array($this->id));
  101. if($this->db->isError($ids)) {
  102. return false;
  103. }
  104. for($i = 0; $i < count($ids); $i++)
  105. {
  106. $nodes[] = $this->_returnNode($ids[$i][$this->connectParentId]);
  107. }
  108. return $nodes;
  109. }
  110. /**
  111. * Get the unique parent of the block in respect to the working path (necessary for the uniqueness)
  112. */
  113. function getParent($workingPath)
  114. {
  115. $parent = NULL;
  116. if($workingPath == "") return NULL;
  117. $parentId = WorkingPath::getParentId($this->id, $workingPath);
  118. $parent = $GLOBALS['BLOCK_LIST']->getBlockById($parentId);
  119. return $parent;
  120. }
  121. /**
  122. * Returns the parent nodes of the current node
  123. * @return GraphNode[]
  124. */
  125. function getParentById($parent)
  126. {
  127. if(!$this->isParent($parent)) {
  128. trigger_error('<b>GraphNode:getParentById</b>: $parent is no parent of node');
  129. return false;
  130. }
  131. return $this->_returnNode($parent);
  132. }
  133. /**
  134. * Returns if the given node is a parent of current node
  135. * @param integer ID of the node to check if it is a parent
  136. * @return boolean
  137. */
  138. function isParent($parent)
  139. {
  140. $query = 'SELECT count(id) FROM '.$this->connectTable.' WHERE '.$this->connectParentId.' = ? AND '.$this->connectId.' = ?';
  141. $num = $this->db->getOne($query, array($parent, $this->id));
  142. if($this->db->isError($num)) {
  143. return false;
  144. }
  145. if($num == 1) {
  146. return true;
  147. } elseif ($num == 0) {
  148. return false;
  149. } else {
  150. trigger_error('<b>GraphNode:isParent</b>: database is inconsistent');
  151. return false;
  152. }
  153. }
  154. /**
  155. * Returns if current node or an upper node of current node has more than one parent
  156. * @return boolean
  157. */
  158. function hasMultipleParents()
  159. {
  160. $prefetch = retrieve('MultipleGraphParents', $this->id);
  161. if ($prefetch !== NULL) return $prefetch;
  162. $parents = $this->getParents();
  163. if(count($parents) > 1) {
  164. store('MultipleGraphParents', $this->id, true);
  165. return true;
  166. } else {
  167. foreach($parents as $parent) {
  168. if($parent->hasMultipleParents()) {
  169. store('MultipleGraphParents', $this->id, true);
  170. return true;
  171. }
  172. }
  173. store('MultipleGraphParents', $this->id, false);
  174. return false;
  175. }
  176. }
  177. /**
  178. * Returns the position of the current node
  179. * @param integer ID of the node in which our position is to be determined
  180. * @return integer
  181. */
  182. function getPosition($parent)
  183. {
  184. if(!$this->isParent($parent))
  185. {
  186. trigger_error('<b>GraphNode:getPosition</b>: $parent is no parent of node');
  187. return false;
  188. }
  189. $query = 'SELECT pos FROM '.$this->connectTable.' WHERE '.$this->connectParentId.' = ? AND '.$this->connectId.' = ?';
  190. $position = $this->db->getOne($query, array($parent, $this->id));
  191. if($this->db->isError($position)) {
  192. return false;
  193. }
  194. return (int) $position;
  195. }
  196. /**
  197. * Returns the position of the next node or if there is no next node return the position of the node itself
  198. * @param integer ID of the node in which our position is to be determined
  199. * @return mixed
  200. */
  201. function getNextUsedPosition($parent)
  202. {
  203. if (!$this->isParent($parent))
  204. {
  205. trigger_error('<b>GraphNode:getNextUsedPosition</b>: $parent is on parent of node');
  206. return false;
  207. }
  208. $query = 'SELECT pos FROM '.$this->connectTable.' WHERE '.$this->connectParentId.' = ? AND pos > ? ORDER BY pos ASC LIMIT 1';
  209. if (!($position = $this->db->getOne($query, array($parent, $this->getPosition($parent)))))
  210. {
  211. return false;
  212. }
  213. else
  214. {
  215. if($this->db->isError($position)) {
  216. return false;
  217. }
  218. return (int) $position;
  219. }
  220. }
  221. /**
  222. * Returns the position of the previous node or if there is no previous node return the position of the node itself
  223. * @param integer ID of the node in which our position is to be determined
  224. * @return integer
  225. */
  226. function getPreviousUsedPosition($parent)
  227. {
  228. if(!$this->isParent($parent))
  229. {
  230. trigger_error('<b>GraphNode:getNextUsedPosition</b>: $parent is on parent of node');
  231. return false;
  232. }
  233. $query = 'SELECT pos FROM '.$this->connectTable.' WHERE '.$this->connectParentId.' = ? AND pos < ? ORDER BY pos DESC LIMIT 1';
  234. if(!($position = $this->db->getOne($query, array($parent, $this->getPosition($parent))))) {
  235. return $this->getPosition($parent);
  236. } else {
  237. if($this->db->isError($position)) {
  238. return false;
  239. }
  240. return (int) $position;
  241. }
  242. }
  243. /**
  244. * Sets the position of the current node. If this position is already used by another node, it will relocate all following nodes as necessary.
  245. * @param integer New position
  246. * @param integer ID of the node in which our position is to be changed
  247. * @return boolean
  248. */
  249. function setPosition($position, $parent)
  250. {
  251. if(!$this->isParent($parent))
  252. {
  253. trigger_error('<b>GraphNode:setPosition</b>: $parent os no parent of node');
  254. return false;
  255. }
  256. if(!preg_match('/^[0-9]+$/', $position) && $position != NULL)
  257. {
  258. trigger_error('<b>GraphNode:setPosition</b>: $position is not valid');
  259. return false;
  260. }
  261. if($position == $this->getPosition($parent)) {
  262. return true;
  263. }
  264. $parentNode = $this->getParentById($parent);
  265. if($parentNode->existsChildAtPosition($position)) {
  266. $query = 'UPDATE '.$this->connectTable.' SET pos = pos + 1 WHERE '.$this->connectParentId.' = ? AND pos >= ?';
  267. $result = $this->db->query($query, array($parent, $position));
  268. }
  269. $query = 'UPDATE '.$this->connectTable.' SET pos = ? WHERE '.$this->connectParentId.' = ? AND '.$this->connectId.' = ?';
  270. $result = $this->db->query($query, array($position, $parent, $this->id));
  271. return (!$this->db->isError($result));
  272. }
  273. /**
  274. * Returns a copy of the current node
  275. * @param parent node id
  276. * @param 2-dimensional array of changed ids. First dimension containing node type as name and seconde dimension assigns old id to new id.
  277. * @param predefined new id
  278. * @return Node
  279. */
  280. function copyNode($parentId, &$changedIds, $newNodeId = NULL)
  281. {
  282. $query = 'SELECT * from '.$this->table.' WHERE id = ?';
  283. $result = $this->db->getRow($query, array($this->id));
  284. if($this->db->isError($result)) {
  285. return false;
  286. }
  287. if($newNodeId == NULL) {
  288. $newNodeId = $this->db->nextId($this->childrenSequence);
  289. }
  290. $rows = array();
  291. $values = array();
  292. for(reset($result); list($key, $value) = each($result);)
  293. {
  294. switch($key)
  295. {
  296. case 'id':
  297. $value = $newNodeId;
  298. break;
  299. case 't_created':
  300. case 't_modified':
  301. $value = time();
  302. break;
  303. case 'u_created':
  304. case 'u_modified':
  305. $value = $GLOBALS['PORTAL']->userId;
  306. break;
  307. case 'owner':
  308. $parent = $GLOBALS['BLOCK_LIST']->getBlockById($parentId);
  309. $pOwner = $parent->getOwner();
  310. $value = $pOwner->getId();
  311. }
  312. $rows[] = $key;
  313. $values[] = $value;
  314. }
  315. $query = 'INSERT INTO '.$this->table.' ('.implode(', ', $rows).') VALUES ('.ltrim(str_repeat(', ?', count($values)), ', ').')';
  316. $result = $this->db->query($query, $values);
  317. if($this->db->isError($result)) {
  318. return false;
  319. }
  320. $query = 'SELECT pos FROM '.$this->connectTable.' WHERE parent_id = ? ORDER BY pos DESC LIMIT 1';
  321. $num = $this->db->getOne($query, array($parentId));
  322. if($this->db->isError($num)) {
  323. return false;
  324. }
  325. if(!($position = ($this->db->getOne($query, array($parentId)) + 1))) {
  326. $position = 1;
  327. }
  328. $query = 'INSERT INTO '.$this->connectTable.' (id, pos, parent_id) VALUES (?, ?, ?)';
  329. $result = $this->db->query($query, array($newNodeId, $position, $parentId));
  330. if($this->db->isError($result)) {
  331. return false;
  332. }
  333. return $this->_returnNode($newNodeId);
  334. }
  335. /**
  336. * insert a new link of the current node in the given node
  337. * @return boolean
  338. */
  339. function linkNode($parentId)
  340. {
  341. $query = 'SELECT count(pos) FROM '.$this->connectTable.' WHERE parent_id = ? AND id = ?';
  342. $num = $this->db->getOne($query, array($parentId, $this->id));
  343. if($this->db->isError($num)) {
  344. return false;
  345. }
  346. if($num > 0) {
  347. return true;
  348. }
  349. $query = 'SELECT pos FROM '.$this->connectTable.' WHERE parent_id = ?';
  350. if(!($position = $this->db->getOne($query, array($parentId)))) {
  351. $position = 1;
  352. } else {
  353. if($this->db->isError($position)) {
  354. return false;
  355. }
  356. $position++;
  357. }
  358. $query = 'SELECT count(id) FROM '.$this->connectTable.' WHERE id = ? AND pos = ? AND parent_id = ?';
  359. if($this->db->getOne($query, array($this->id, $position, $parentId)) > 0) {
  360. return true;
  361. }
  362. $query = 'INSERT INTO '.$this->connectTable.' (id, pos, parent_id) VALUES (?, ?, ?)';
  363. $result = $this->db->query($query, array($this->id, $position, $parentId));
  364. if($this->db->isError($result)) {
  365. return false;
  366. }
  367. return true;
  368. }
  369. }
  370. ?>