Example #1
0
 /**
  * Sets the parent of the given node
  *
  * The force parameter is used to override the "don't change the parent to the current parent" logic in the event
  * of recovering a corrupted table, or creating new nodes. Otherwise it should always be false. In reality this
  * method could be private, since calling save with parent_id set also calls setParent
  *
  * @param AppModel $Model Model instance
  * @param mixed $parentId
  * @return boolean true on success, false on failure
  * @access protected
  */
 function _setParent(&$Model, $parentId = null, $created = false)
 {
     extract($this->settings[$Model->alias]);
     list($node) = array_values($Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $Model->id), 'fields' => array($Model->primaryKey, $parent, $left, $right), 'recursive' => $recursive)));
     $edge = $this->__getMax($Model, $scope, $right, $recursive, $created);
     if (empty($parentId)) {
         $this->__sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created);
         $this->__sync($Model, $node[$right] - $node[$left] + 1, '-', '> ' . $node[$left], $created);
     } else {
         $values = $Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $parentId), 'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive));
         if ($values === false) {
             return false;
         }
         $parentNode = array_values($values);
         if (empty($parentNode) || empty($parentNode[0])) {
             return false;
         }
         $parentNode = $parentNode[0];
         if ($Model->id == $parentId) {
             return false;
         } elseif ($node[$left] < $parentNode[$left] && $parentNode[$right] < $node[$right]) {
             return false;
         }
         if (empty($node[$left]) && empty($node[$right])) {
             $this->__sync($Model, 2, '+', '>= ' . $parentNode[$right], $created);
             $result = $Model->save(array($left => $parentNode[$right], $right => $parentNode[$right] + 1, $parent => $parentId), array('validate' => false, 'callbacks' => false));
             $Model->data = $result;
         } else {
             $this->__sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created);
             $diff = $node[$right] - $node[$left] + 1;
             if ($node[$left] > $parentNode[$left]) {
                 if ($node[$right] < $parentNode[$right]) {
                     $this->__sync($Model, $diff, '-', 'BETWEEN ' . $node[$right] . ' AND ' . ($parentNode[$right] - 1), $created);
                     $this->__sync($Model, $edge - $parentNode[$right] + $diff + 1, '-', '> ' . $edge, $created);
                 } else {
                     $this->__sync($Model, $diff, '+', 'BETWEEN ' . $parentNode[$right] . ' AND ' . $node[$right], $created);
                     $this->__sync($Model, $edge - $parentNode[$right] + 1, '-', '> ' . $edge, $created);
                 }
             } else {
                 $this->__sync($Model, $diff, '-', 'BETWEEN ' . $node[$right] . ' AND ' . ($parentNode[$right] - 1), $created);
                 $this->__sync($Model, $edge - $parentNode[$right] + $diff + 1, '-', '> ' . $edge, $created);
             }
         }
     }
     return true;
 }
Example #2
0
 /**
  * Sets the parent of the given node
  *
  * The force parameter is used to override the "don't change the parent to the current parent" logic in the event
  * of recovering a corrupted table, or creating new nodes. Otherwise it should always be false. In reality this
  * method could be private, since calling save with parent_id set also calls setParent
  *
  * @param AppModel $Model Model instance
  * @param mixed $parentId
  * @return boolean true on success, false on failure
  */
 function _setParent(&$Model, $parentId = null, $created = false)
 {
     extract($this->settings[$Model->alias]);
     $cachequeries = $Model->cacheQueries;
     $Model->cacheQueries = false;
     list($node) = array_values($Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $Model->id), 'fields' => array($Model->primaryKey, $parent, $left, $right), 'recursive' => $recursive)));
     if (empty($parentId)) {
         $edge = $this->__getMax($Model, $scope, $right, $recursive, $created);
         $this->__sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created);
         $this->__sync($Model, $node[$right] - $node[$left] + 1, '-', '> ' . $node[$left], $created);
     } else {
         $parentNode = array_values($Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $parentId), 'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive)));
         if (empty($parentNode) || empty($parentNode[0])) {
             return false;
         }
         $parentNode = $parentNode[0];
         $edge = $this->__getMax($Model, "{$Model->alias}.{$left} <= " . $parentNode[$left], $right, $recursive, $created);
         if ($Model->id == $parentId) {
             return false;
         } elseif ($node[$left] < $parentNode[$left] && $parentNode[$right] < $node[$right]) {
             return false;
         }
         if (empty($node[$left]) && empty($node[$right])) {
             $result = $Model->save(array($left => $parentNode[$right], $right => $parentNode[$right] + 1, $parent => $parentId), array('validate' => false, 'callbacks' => false));
             $this->__sync($Model, 2, '+', '>= ' . $parentNode[$right], $created);
             $Model->data = $result;
         } else {
             $diff = $node[$right] - $node[$left];
             // increment position values of new family of retargeted subtree
             $this->__sync($Model, $diff + 1, '+', '>= ' . $parentNode[$right], $created);
             // relocate target node under parent
             $this->__sync($Model, $parentNode[$right] - $node[$left], '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created);
             // reduce values in former position
             $nodeTreeMax = $this->__getMax($Model, "{$Model->alias}.{$left} <= " . $node[$left], $right, $recursive, $created);
             $this->__sync($Model, $diff + 1, '-', 'BETWEEN ' . $node[$right] . ' AND ' . $nodeTreeMax, $created);
         }
     }
     $Model->cacheQueries = $cachequeries;
     return true;
 }
Example #3
0
 /**
  * Get the number of child nodes
  *
  * If the direct parameter is set to true, only the direct children are counted (based upon the parent_id field)
  * If false is passed for the id parameter, all top level nodes are counted, or all nodes are counted.
  *
  * @param AppModel $Model Model instance
  * @param integer $id The ID of the record to read
  * @param boolean $direct whether to count direct, or all, children
  * @return integer number of child nodes
  * @access public
  */
 function getChildCount(&$Model, $id = null, $direct = false)
 {
     if (!$id && $Model->id) {
         $id = $Model->id;
     }
     extract($this->settings[$Model->alias]);
     if ($direct) {
         return $Model->find('count', array('conditions' => array($Model->escapeField($parent) => $id)));
     } else {
         // Use cached node if possible
         if (isset($Model->data[$Model->alias][$left]) && isset($Model->data[$Model->alias][$right])) {
             $node = $Model->data[$Model->alias];
         } else {
             // Get node
             if (($node = $this->_node($Model, $id)) === false) {
                 return false;
             }
         }
         return ($node[$right] - $node[$left] - 1) / 2;
     }
 }
Example #4
0
 /**
  * Sets the parent of the given node
  *
  * The force parameter is used to override the "don't change the parent to the current parent" logic in the event
  * of recovering a corrupted table, or creating new nodes. Otherwise it should always be false. In reality this
  * method could be private, since calling save with parent_id set also calls setParent
  *
  * @param AppModel $model
  * @param mixed $parentId
  * @return boolean true on success, false on failure
  * @access protected
  */
 function _setParent(&$model, $parentId = null)
 {
     extract($this->settings[$model->alias]);
     list($node) = array_values($model->find('first', array('conditions' => array($scope, $model->escapeField() => $model->id), 'fields' => array($model->primaryKey, $parent, $left, $right), 'recursive' => -1)));
     $edge = $this->__getMax($model, $scope, $right);
     if (empty($parentId)) {
         $this->__sync($model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right]);
         $this->__sync($model, $node[$right] - $node[$left] + 1, '-', '> ' . $node[$left]);
     } else {
         list($parentNode) = array_values($model->find('first', array('conditions' => array($scope, $model->escapeField() => $parentId), 'fields' => array($model->primaryKey, $left, $right), 'recursive' => -1)));
         if (empty($parentNode)) {
             return false;
         } elseif ($model->id == $parentId) {
             return false;
         } elseif ($node[$left] < $parentNode[$left] && $parentNode[$right] < $node[$right]) {
             return false;
         }
         if (empty($node[$left]) && empty($node[$right])) {
             $this->__sync($model, 2, '+', '>= ' . $parentNode[$right]);
             $model->save(array($left => $parentNode[$right], $right => $parentNode[$right] + 1, $parent => $parentId), false);
         } else {
             $this->__sync($model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right]);
             $diff = $node[$right] - $node[$left] + 1;
             if ($node[$left] > $parentNode[$left]) {
                 if ($node[$right] < $parentNode[$right]) {
                     $this->__sync($model, $diff, '-', 'BETWEEN ' . $node[$right] . ' AND ' . ($parentNode[$right] - 1));
                     $this->__sync($model, $edge - $parentNode[$right] + $diff + 1, '-', '> ' . $edge);
                 } else {
                     $this->__sync($model, $diff, '+', 'BETWEEN ' . $parentNode[$right] . ' AND ' . $node[$right]);
                     $this->__sync($model, $edge - $parentNode[$right] + 1, '-', '> ' . $edge);
                 }
             } else {
                 $this->__sync($model, $diff, '-', 'BETWEEN ' . $node[$right] . ' AND ' . ($parentNode[$right] - 1));
                 $this->__sync($model, $edge - $parentNode[$right] + $diff + 1, '-', '> ' . $edge);
             }
         }
     }
     return true;
 }
Example #5
0
 /**
  * Reorder the node without changing the parent.
  *
  * If the node is the last child, or is a top level node with no subsequent node this method will return false
  *
  * @since 1.2
  * @param AppModel $model
  * @param mixed $id The ID of the record to move
  * @param int $number how many places to move the node
  * @return boolean True on success, false on failure
  * @access public
  */
 function move_down(&$model, $id = null, $number = 1)
 {
     if (empty($id)) {
         $id = $model->id;
     }
     extract($this->settings[$model->name]);
     list($node) = array_values($model->find(array($scope, $model->escapeField() => $id), array($model->primaryKey, $left, $right, $parent), null, -1));
     if ($node[$parent]) {
         list($parentNode) = array_values($model->find(array($scope, $model->escapeField() => $node[$parent]), array($model->primaryKey, $left, $right), null, -1));
         if ($node[$right] + 1 == $parentNode[$right]) {
             return false;
         }
     }
     $nextNode = $model->find(array($scope, $left => $node[$right] + 1), array($model->primaryKey, $left, $right), null, -1);
     if ($nextNode) {
         list($nextNode) = array_values($nextNode);
     } else {
         return false;
     }
     $edge = $this->__get_max($model, $scope, $right);
     // First, move the node (and subnodes) to the end
     $this->__sync($model, $edge - $node[$left] + 1, '+', "BETWEEN {$node[$left]} AND {$node[$right]}", $scope);
     // Then move the 'next' node backwards
     $this->__sync($model, $nextNode[$left] - $node[$left], '-', "BETWEEN {$nextNode[$left]} AND {$nextNode[$right]}", $scope);
     // Then put in the hole
     $this->__sync($model, $edge - $node[$left] - ($nextNode[$right] - $nextNode[$left]), '-', "> {$edge}", $scope);
     if ($number > 1) {
         return $this->move_down($model, $number - 1);
     } else {
         return true;
     }
 }
Example #6
0
 /**
  * When doing any update all calls, you want to avoid updating the record
  * you've just modified, as the order will have been set already, so exclude
  * it with some conditions.
  *
  * @param AppModel $model
  * @return array Array Model.primary_key => $id
  */
 function conditionsNotCurrent(&$model)
 {
     if (!($id = $model->id)) {
         $id = $model->getInsertID();
     }
     return array($model->escapeField($model->primaryKey) . ' <>' => $id);
 }
Example #7
0
 /**
  * Callback...
  *
  * Allows automagic sorting and field retrival.
  * Add support for $query['localized'] that will filter out items not translated to the current language
  *
  * @param AppModel $model
  */
 public function beforeFind($model, $query)
 {
     //    	$this->_checkSchema($model);
     $fields = $this->settings[$model->alias];
     $currLocale = SlConfigure::read('I18n.locale');
     if (empty($currLocale)) {
         return $query;
     }
     if (!empty($query['localized'])) {
         if (!is_array($query['conditions'])) {
             $query['conditions'] = array($query['conditions']);
         }
         $query['conditions']["_{$currLocale}"] = true;
         unset($query['localized']);
     }
     // reorder based on current localization
     if (count($query['order']) > 0) {
         $orderArray =& $query['order'];
         foreach ($orderArray as &$order) {
             if (!is_array($order) && $order) {
                 $parts = explode(' ', $order);
                 $order = array($parts[0] => empty($parts[1]) ? 'ASC' : $parts[1]);
             }
             if (is_array($order)) {
                 $newOrder = array();
                 foreach ($order as $key => $direction) {
                     $field = $key;
                     if (is_int($field)) {
                         $field = $direction;
                         $direction = 'ASC';
                     }
                     $temp = explode('.', $field);
                     if (count($temp) === 1) {
                         $modelClass = $model->alias;
                         $fieldName = Inflector::slug($temp[0], '');
                     } else {
                         $modelClass = Inflector::slug($temp[0], '');
                         $fieldName = Inflector::slug($temp[1], '');
                     }
                     if (isset($this->settings[$modelClass]) && in_array($fieldName, $this->settings[$modelClass])) {
                         $newOrder[$modelClass . '.' . $fieldName . '_' . $currLocale] = $direction;
                     } else {
                         $newOrder[$key] = $direction;
                     }
                 }
                 $order = $newOrder;
             }
         }
     }
     if (is_string($query['fields'])) {
         $query['fields'] = array($query['fields']);
     }
     $locales = SlConfigure::read('I18n.locales');
     if (is_array($query['fields']) && count($query['fields']) > 0) {
         // Add {$field}_{$lang} to fields list (when needed)
         foreach ($fields as $field) {
             foreach ($query['fields'] as $fieldName) {
                 $temp = explode('.', $fieldName);
                 if (count($temp) === 1) {
                     $modelClass = $model->alias;
                     $fieldName = Inflector::slug($temp[0], '');
                 } else {
                     $modelClass = Inflector::slug($temp[0], '');
                     $fieldName = Inflector::slug($temp[1], '');
                 }
                 if (isset($this->settings[$modelClass]) && in_array($fieldName, $this->settings[$modelClass])) {
                     $query['fields'][] = $modelClass . '.' . $fieldName . '_' . $currLocale;
                 }
                 //                    foreach (array($field, $model->alias.'.'.$field, $model->escapeField($field)) as $_field) {
                 //						if ($_field === $fieldName) {
                 //							foreach ($locales as $locale) {
                 //								$query['fields'][] = $model->alias.'.'.$field.'_'.$locale;
                 //                            }
                 //                            //unset($query['fields'][$fieldName]);
                 //						}
                 //					}
             }
         }
     }
     // transcript conditions
     if ($query['conditions']) {
         $fields2 = array();
         $recursive = isset($query['recursive']) ? $query['recursive'] : $model->recursive;
         foreach ($fields as $field) {
             foreach (array($field, $model->alias . '.' . $field, $model->escapeField($field)) as $_field) {
                 $fields2["{$_field} LIKE"] = $model->alias . '.' . $field . '_' . $currLocale . ' LIKE';
                 //if ($recursive < 0 || $field != $_field) {
                 $fields2[$_field] = "{$_field}_{$currLocale}";
                 //}
             }
         }
         if ($fields2) {
             $query['conditions'] = $this->__parseConditions($query['conditions'], $fields2);
         }
     }
     return $query;
 }
Example #8
0
 /**
  * Sets the parent of the given node
  *
  * The force parameter is used to override the "don't change the parent to the current parent" logic in the event
  * of recovering a corrupted table, or creating new nodes. Otherwise it should always be false. In reality this
  * method could be private, since calling save with parent_id set also calls setParent
  *
  * @param AppModel $Model Model instance
  * @param mixed $parentId
  * @return boolean true on success, false on failure
  * @access protected
  */
 function _setParent(&$Model, $parentId = null, $created = false)
 {
     extract($this->settings[$Model->alias]);
     $cachequeries = $Model->cacheQueries;
     $Model->cacheQueries = false;
     list($node) = array_values($Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $Model->id), 'fields' => array($Model->primaryKey, $parent, $left, $right), 'recursive' => $recursive)));
     if (empty($parentId)) {
         $edge = $this->__getMax($Model, $scope, $right, $recursive, $created);
         $this->__sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created);
         $this->__sync($Model, $node[$right] - $node[$left] + 1, '-', '> ' . $node[$left], $created);
     } else {
         $parentNode = array_values($Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $parentId), 'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive)));
         if (empty($parentNode) || empty($parentNode[0])) {
             return false;
         }
         $parentNode = $parentNode[0];
         // find current rightmost edge of parent tree
         $edge = $this->__getMax($Model, "{$Model->alias}.{$left} <= " . $parentNode[$left], $right, $recursive, $created);
         if ($Model->id == $parentId) {
             return false;
         } elseif ($node[$left] < $parentNode[$left] && $parentNode[$right] < $node[$right]) {
             return false;
         }
         if (empty($node[$left]) && empty($node[$right])) {
             $result = $Model->save(array($left => $parentNode[$right], $right => $parentNode[$right] + 1, $parent => $parentId), array('validate' => false, 'callbacks' => false));
             $this->__sync($Model, 2, '+', '>= ' . $parentNode[$right], $created);
             $Model->data = $result;
         } else {
             $diff = $node[$right] - $node[$left];
             // swap subtree out into free memory and get updated positions
             $this->__swapout($Model, $node);
             $this->__updateNode($Model, $parentNode);
             // push new neighbours on our new right side to make room and get updated parent
             $this->__sync($Model, $diff + 1, '+', '>= ' . $parentNode[$right], $created);
             $this->__updateNode($Model, $parentNode);
             $this->__updateNode($Model, $node);
             // relocate target node under parent
             $this->__sync($Model, $parentNode[$left] - $node[$left] + $diff, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created);
         }
     }
     $Model->cacheQueries = $cachequeries;
     return true;
 }