<?php
/* This file is part of testMaker.
testMaker is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.
testMaker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/**
* @package Core
*/
/**
* Needs the class it inherites from
*/
require_once(dirname(__FILE__).'/Node.php');
/**
* This class is an abstract concept for nodes.
*
* @abstract
* @package Core
*/
class GraphNode extends Node
{
/**#@+
* @access private
*/
var $connectTable;
var $connectId;
var $connectParentId;
/**#@-*/
/**
* This constructor has to be overwritten.
* In the overwriting constructor the variable $this->table, $this->connectTable, $this->connectId and $this->connectParentId have to be set.
* After initializing this variable the overwring constructor has to call this overwritten constructor.
* @param DB The database object
* @param integer ID of the node
*/
function GraphNode($id)
{
$this->db = &$GLOBALS['dao']->getConnection();
if(!isset($this->connectTable)) {
trigger_error('<b>GraphNode</b>: $this->connectTable was not set');
}
if(!isset($this->connectId)) {
trigger_error('<b>GraphNode</b>: $this->connectId was not set');
}
if(!isset($this->connectParentId)) {
trigger_error('<b>GraphNode</b>: $this->connectParentId was not set');
}
$this->Node($id);
}
/**
* returns a graph node with the correct type, automatically determined
* @return String
*/
function _returnNode($id)
{
trigger_error('<b>GraphNode:_returnNode</b>: function not overwritten by inherited class');
}
/**
* compare function to sort modifications array
* @access private
* @param $a element 1
* @param $b element 2
* @return int
*/
function _compareModifications($a, $b) {
if($a['parent'] < $b['parent']){
return -1;
} elseif($a['parent'] > $b['parent']) {
return 1;
} else {
if(!isset($a['position']) && isset($b['position'])) {
return -1;
} elseif(isset($a['position']) && !isset($b['position'])) {
return 1;
} elseif(!isset($a['position']) && !isset($b['position'])) {
return 0;
}
if($a['position'] < $b['position']) {
return -1;
} elseif($a['position'] > $b['position']) {
return 1;
} else {
return 0;
}
}
}
/**
* Returns the parent nodes of the current node
* @return GraphNode[]
*/
function &getParents()
{
$nodes = array();
$query = 'SELECT '.$this->connectParentId.' FROM '.$this->connectTable.' WHERE '.$this->connectId.' = ?';
$ids = $this->db->getAll($query, array($this->id));
if($this->db->isError($ids)) {
return false;
}
for($i = 0; $i < count($ids); $i++)
{
$nodes[] = $this->_returnNode($ids[$i][$this->connectParentId]);
}
return $nodes;
}
/**
* Get the unique parent of the block in respect to the working path (necessary for the uniqueness)
*/
function getParent($workingPath)
{
$parent = NULL;
if($workingPath == "") return NULL;
$parentId = WorkingPath::getParentId($this->id, $workingPath);
$parent = $GLOBALS['BLOCK_LIST']->getBlockById($parentId);
return $parent;
}
/**
* Returns the parent nodes of the current node
* @return GraphNode[]
*/
function getParentById($parent)
{
if(!$this->isParent($parent)) {
trigger_error('<b>GraphNode:getParentById</b>: $parent is no parent of node');
return false;
}
return $this->_returnNode($parent);
}
/**
* Returns if the given node is a parent of current node
* @param integer ID of the node to check if it is a parent
* @return boolean
*/
function isParent($parent)
{
$query = 'SELECT count(id) FROM '.$this->connectTable.' WHERE '.$this->connectParentId.' = ? AND '.$this->connectId.' = ?';
$num = $this->db->getOne($query, array($parent, $this->id));
if($this->db->isError($num)) {
return false;
}
if($num == 1) {
return true;
} elseif ($num == 0) {
return false;
} else {
trigger_error('<b>GraphNode:isParent</b>: database is inconsistent');
return false;
}
}
/**
* Returns if current node or an upper node of current node has more than one parent
* @return boolean
*/
function hasMultipleParents()
{
$prefetch = retrieve('MultipleGraphParents', $this->id);
if ($prefetch !== NULL) return $prefetch;
$parents = $this->getParents();
if(count($parents) > 1) {
store('MultipleGraphParents', $this->id, true);
return true;
} else {
foreach($parents as $parent) {
if($parent->hasMultipleParents()) {
store('MultipleGraphParents', $this->id, true);
return true;
}
}
store('MultipleGraphParents', $this->id, false);
return false;
}
}
/**
* Returns the position of the current node
* @param integer ID of the node in which our position is to be determined
* @return integer
*/
function getPosition($parent)
{
if(!$this->isParent($parent))
{
trigger_error('<b>GraphNode:getPosition</b>: $parent is no parent of node');
return false;
}
$query = 'SELECT pos FROM '.$this->connectTable.' WHERE '.$this->connectParentId.' = ? AND '.$this->connectId.' = ?';
$position = $this->db->getOne($query, array($parent, $this->id));
if($this->db->isError($position)) {
return false;
}
return (int) $position;
}
/**
* Returns the position of the next node or if there is no next node return the position of the node itself
* @param integer ID of the node in which our position is to be determined
* @return mixed
*/
function getNextUsedPosition($parent)
{
if (!$this->isParent($parent))
{
trigger_error('<b>GraphNode:getNextUsedPosition</b>: $parent is on parent of node');
return false;
}
$query = 'SELECT pos FROM '.$this->connectTable.' WHERE '.$this->connectParentId.' = ? AND pos > ? ORDER BY pos ASC LIMIT 1';
if (!($position = $this->db->getOne($query, array($parent, $this->getPosition($parent)))))
{
return false;
}
else
{
if($this->db->isError($position)) {
return false;
}
return (int) $position;
}
}
/**
* Returns the position of the previous node or if there is no previous node return the position of the node itself
* @param integer ID of the node in which our position is to be determined
* @return integer
*/
function getPreviousUsedPosition($parent)
{
if(!$this->isParent($parent))
{
trigger_error('<b>GraphNode:getNextUsedPosition</b>: $parent is on parent of node');
return false;
}
$query = 'SELECT pos FROM '.$this->connectTable.' WHERE '.$this->connectParentId.' = ? AND pos < ? ORDER BY pos DESC LIMIT 1';
if(!($position = $this->db->getOne($query, array($parent, $this->getPosition($parent))))) {
return $this->getPosition($parent);
} else {
if($this->db->isError($position)) {
return false;
}
return (int) $position;
}
}
/**
* Sets the position of the current node. If this position is already used by another node, it will relocate all following nodes as necessary.
* @param integer New position
* @param integer ID of the node in which our position is to be changed
* @return boolean
*/
function setPosition($position, $parent)
{
if(!$this->isParent($parent))
{
trigger_error('<b>GraphNode:setPosition</b>: $parent os no parent of node');
return false;
}
if(!preg_match('/^[0-9]+$/', $position) && $position != NULL)
{
trigger_error('<b>GraphNode:setPosition</b>: $position is not valid');
return false;
}
if($position == $this->getPosition($parent)) {
return true;
}
$parentNode = $this->getParentById($parent);
if($parentNode->existsChildAtPosition($position)) {
$query = 'UPDATE '.$this->connectTable.' SET pos = pos + 1 WHERE '.$this->connectParentId.' = ? AND pos >= ?';
$result = $this->db->query($query, array($parent, $position));
}
$query = 'UPDATE '.$this->connectTable.' SET pos = ? WHERE '.$this->connectParentId.' = ? AND '.$this->connectId.' = ?';
$result = $this->db->query($query, array($position, $parent, $this->id));
return (!$this->db->isError($result));
}
/**
* Returns a copy of the current node
* @param parent node id
* @param 2-dimensional array of changed ids. First dimension containing node type as name and seconde dimension assigns old id to new id.
* @param predefined new id
* @return Node
*/
function copyNode($parentId, &$changedIds, $newNodeId = NULL)
{
$query = 'SELECT * from '.$this->table.' WHERE id = ?';
$result = $this->db->getRow($query, array($this->id));
if($this->db->isError($result)) {
return false;
}
if($newNodeId == NULL) {
$newNodeId = $this->db->nextId($this->childrenSequence);
}
$rows = array();
$values = array();
for(reset($result); list($key, $value) = each($result);)
{
switch($key)
{
case 'id':
$value = $newNodeId;
break;
case 't_created':
case 't_modified':
$value = time();
break;
case 'u_created':
case 'u_modified':
$value = $GLOBALS['PORTAL']->userId;
break;
case 'owner':
$parent = $GLOBALS['BLOCK_LIST']->getBlockById($parentId);
$pOwner = $parent->getOwner();
$value = $pOwner->getId();
}
$rows[] = $key;
$values[] = $value;
}
$query = 'INSERT INTO '.$this->table.' ('.implode(', ', $rows).') VALUES ('.ltrim(str_repeat(', ?', count($values)), ', ').')';
$result = $this->db->query($query, $values);
if($this->db->isError($result)) {
return false;
}
$query = 'SELECT pos FROM '.$this->connectTable.' WHERE parent_id = ? ORDER BY pos DESC LIMIT 1';
$num = $this->db->getOne($query, array($parentId));
if($this->db->isError($num)) {
return false;
}
if(!($position = ($this->db->getOne($query, array($parentId)) + 1))) {
$position = 1;
}
$query = 'INSERT INTO '.$this->connectTable.' (id, pos, parent_id) VALUES (?, ?, ?)';
$result = $this->db->query($query, array($newNodeId, $position, $parentId));
if($this->db->isError($result)) {
return false;
}
return $this->_returnNode($newNodeId);
}
/**
* insert a new link of the current node in the given node
* @return boolean
*/
function linkNode($parentId)
{
$query = 'SELECT count(pos) FROM '.$this->connectTable.' WHERE parent_id = ? AND id = ?';
$num = $this->db->getOne($query, array($parentId, $this->id));
if($this->db->isError($num)) {
return false;
}
if($num > 0) {
return true;
}
$query = 'SELECT pos FROM '.$this->connectTable.' WHERE parent_id = ?';
if(!($position = $this->db->getOne($query, array($parentId)))) {
$position = 1;
} else {
if($this->db->isError($position)) {
return false;
}
$position++;
}
$query = 'SELECT count(id) FROM '.$this->connectTable.' WHERE id = ? AND pos = ? AND parent_id = ?';
if($this->db->getOne($query, array($this->id, $position, $parentId)) > 0) {
return true;
}
$query = 'INSERT INTO '.$this->connectTable.' (id, pos, parent_id) VALUES (?, ?, ?)';
$result = $this->db->query($query, array($this->id, $position, $parentId));
if($this->db->isError($result)) {
return false;
}
return true;
}
}
?>