Dotclear

source: inc/core/class.dc.categories.php @ 270:48858be15bda

Revision 270:48858be15bda, 16.1 KB checked in by Franck <carnet.franck.paul@…>, 13 years ago (diff)

Changement d'année

Line 
1<?php
2# -- BEGIN LICENSE BLOCK ---------------------------------------
3#
4# This file is part of Dotclear 2.
5#
6# Copyright (c) 2003-2011 Olivier Meunier & Association Dotclear
7# Licensed under the GPL version 2.0 license.
8# See LICENSE file or
9# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10#
11# -- END LICENSE BLOCK -----------------------------------------
12if (!defined('DC_RC_PATH')) { return; }
13
14# nestedTree class is based on excellent work of Kuzma Feskov
15# (http://php.russofile.ru/ru/authors/sql/nestedsets01/)
16#
17# One day we'll move nestedTree to Clearbricks.
18
19class dcCategories extends nestedTree
20{
21     protected $f_left = 'cat_lft';
22     protected $f_right = 'cat_rgt';
23     protected $f_id = 'cat_id';
24     
25     protected $core;
26     protected $blog_id;
27     
28     public function __construct($core)
29     {
30          $this->core =& $core;
31          $this->con =& $core->con;
32          $this->blog_id = $core->blog->id;
33          $this->table = $core->prefix.'category';
34          $this->add_condition = array('blog_id' => "'".$this->con->escape($this->blog_id)."'");
35     }
36     
37     public function getChildren($start=0,$id=null,$sort='asc',$fields=array())
38     {
39          $fields = array_merge(array('cat_title','cat_url','cat_desc'),$fields);
40          return parent::getChildren($start,$id,$sort,$fields);
41     }
42     
43     public function getParents($id,$fields=array())
44     {
45          $fields = array_merge(array('cat_title','cat_url','cat_desc'),$fields);
46          return parent::getParents($id,$fields);
47     }
48     
49     public function getParent($id,$fields=array())
50     {
51          $fields = array_merge(array('cat_title','cat_url','cat_desc'),$fields);
52          return parent::getParent($id,$fields);
53     }
54}
55
56abstract class nestedTree
57{
58     protected $con;
59     
60     protected $table;
61     protected $f_left;
62     protected $f_right;
63     protected $f_id;
64     
65     protected $add_condition = array();
66     
67     protected $parents;
68     
69     public function __construct($con)
70     {
71          $this->con =& $con;
72     }
73     
74     public function getChildren($start=0,$id=null,$sort='asc',$fields=array())
75     {
76          $fields = count($fields) > 0 ? ', C2.'.implode(', C2.',$fields) : '';
77         
78          $sql = 'SELECT C2.'.$this->f_id.', C2.'.$this->f_left.', C2.'.$this->f_right.', COUNT(C1.'.$this->f_id.') AS level '
79          . $fields.' ' 
80          . 'FROM '.$this->table.' AS C1, '.$this->table.' AS C2 %s '
81          . 'WHERE C2.'.$this->f_left.' BETWEEN C1.'.$this->f_left.' AND C1.'.$this->f_right.' '
82          . ' %s '
83          . $this->getCondition('AND','C2.')
84          . $this->getCondition('AND','C1.')
85          . 'GROUP BY C2.'.$this->f_id.', C2.'.$this->f_left.', C2.'.$this->f_right.' '.$fields.' '
86          . ' %s '
87          . 'ORDER BY C2.'.$this->f_left.' '.($sort == 'asc' ? 'ASC' : 'DESC').' ';
88         
89          $from = $where = '';
90          if ($start > 0) {
91               $from = ', '.$this->table.' AS C3';
92               $where = 'AND C3.'.$this->f_id.' = '.(integer) $start.' AND C1.'.$this->f_left.' >= C3.'.$this->f_left.' AND C1.'.$this->f_right.' <= C3.'.$this->f_right;
93               $where .= $this->getCondition('AND','C3.');
94          }
95         
96          $having = '';
97          if ($id !== null) {
98               $having = ' HAVING C2.'.$this->f_id.' = '.(integer) $id;
99          }
100         
101          $sql = sprintf($sql,$from,$where,$having);
102         
103          return $this->con->select($sql);
104     }
105     
106     public function getParents($id,$fields=array())
107     {
108          $fields = count($fields) > 0 ? ', C1.'.implode(', C1.',$fields) : '';
109         
110          return $this->con->select(
111               'SELECT C1.'.$this->f_id.' '.$fields.' '
112               . 'FROM '.$this->table.' C1, '.$this->table.' C2 '
113               . 'WHERE C2.'.$this->f_id.' = '.(integer) $id.' '
114               . 'AND C1.'.$this->f_left.' < C2.'.$this->f_left.' '
115               . 'AND C1.'.$this->f_right.' > C2.'.$this->f_right.' '
116               . $this->getCondition('AND','C2.')
117               . $this->getCondition('AND','C1.')
118               . 'ORDER BY C1.'.$this->f_left.' ASC '
119          );
120     }
121     
122     public function getParent($id,$fields=array())
123     {
124          $fields = count($fields) > 0 ? ', C1.'.implode(', C1.',$fields) : '';
125         
126          return $this->con->select(
127               'SELECT C1.'.$this->f_id.' '.$fields.' '
128               . 'FROM '.$this->table.' C1, '.$this->table.' C2 '
129               . 'WHERE C2.'.$this->f_id.' = '.(integer) $id.' '
130               . 'AND C1.'.$this->f_left.' < C2.'.$this->f_left.' '
131               . 'AND C1.'.$this->f_right.' > C2.'.$this->f_right.' '
132               . $this->getCondition('AND','C2.')
133               . $this->getCondition('AND','C1.')
134               . 'ORDER BY C1.'.$this->f_left.' DESC '
135               . $this->con->limit(1)
136          );
137     }
138     
139     /* ------------------------------------------------
140      * Tree manipulations
141      * ---------------------------------------------- */
142     public function addNode($data,$target=0)
143     {
144          if (!is_array($data) && !($data instanceof cursor)) {
145               throw new Exception('Invalid data block');
146          }
147         
148          if (is_array($data))
149          {
150               $D = $data;
151               $data = $this->con->openCursor($this->table);
152               foreach ($D as $k => $v) {
153                    $data->{$k} = $v;
154               }
155               unset($D);
156          }
157         
158          # We want to put it at the end
159          $this->con->writeLock($this->table);
160          try
161          {
162               $rs = $this->con->select('SELECT MAX('.$this->f_id.') as n_id FROM '.$this->table);
163               $id = $rs->n_id;
164               
165               $rs = $this->con->select(
166                    'SELECT MAX('.$this->f_right.') as n_r '.
167                    'FROM '.$this->table.
168                    $this->getCondition('WHERE')
169               );
170               $last = $rs->n_r == 0 ? 1 : $rs->n_r;
171               
172               $data->{$this->f_id} = $id+1;
173               $data->{$this->f_left} = $last+1;
174               $data->{$this->f_right} = $last+2;
175               
176               $data->insert();
177               $this->con->unlock();
178               try {
179                    $this->setNodeParent($id+1,$target);
180                    return $data->{$this->f_id};
181               } catch (Exception $e) {} # We don't mind error in this case
182          }
183          catch (Exception $e)
184          {
185               $this->con->unlock();
186               throw $e;
187          }
188     }
189     
190     public function deleteNode($node,$keep_children=true)
191     {
192          $node = (integer) $node;
193         
194          $rs = $this->getChildren(0,$node);
195          if ($rs->isEmpty()) {
196               throw new Exception('Node does not exist.');
197          }
198          $node_left = (integer) $rs->{$this->f_left};
199          $node_right = (integer) $rs->{$this->f_right};
200         
201          try
202          {
203               $this->con->begin();
204               
205               if ($keep_children)
206               {
207                    $this->con->execute('DELETE FROM '.$this->table.' WHERE '.$this->f_id.' = '.$node);
208                   
209                    $sql = 'UPDATE '.$this->table.' SET '
210                    . $this->f_right.' = CASE '
211                    .    'WHEN '.$this->f_right.' BETWEEN '.$node_left.' AND '.$node_right.' '
212                    .         'THEN '.$this->f_right.' - 1 '
213                    .    'WHEN '.$this->f_right.' > '.$node_right.' '
214                    .         'THEN '.$this->f_right.' - 2 '
215                    .    'ELSE '.$this->f_right.' '
216                    .    'END, '
217                    . $this->f_left.' = CASE '
218                    .    'WHEN '.$this->f_left.' BETWEEN '.$node_left.' AND '.$node_right.' '
219                    .         'THEN '.$this->f_left.' - 1 '
220                    .    'WHEN '.$this->f_left.' > '.$node_right.' '
221                    .         'THEN '.$this->f_left.' - 2 '
222                    .    'ELSE '.$this->f_left.' '
223                    .    'END '
224                    . 'WHERE '.$this->f_right.' > '.$node_left
225                    . $this->getCondition();
226                   
227                    $this->con->execute($sql);
228               }
229               else
230               {
231                    $this->con->execute('DELETE FROM '.$this->table.' WHERE '.$this->f_left.' BETWEEN '.$node_left.' AND '.$node_right);
232                   
233                    $node_delta = $node_right - $node_left + 1;
234                    $sql = 'UPDATE '.$this->table.' SET '
235                    . $this->f_left.' = CASE '
236                    .    'WHEN '.$this->f_left.' > '.$node_left.' '
237                    .         'THEN '.$this->f_left.' - ('.$node_delta.') '
238                    .    'ELSE '.$this->f_left.' '
239                    .    'END, '
240                    . $this->f_right.' = CASE '
241                    .    'WHEN '.$this->f_right.' > '.$node_left.' '
242                    .         'THEN '.$this->f_right.' - ('.$node_delta.') '
243                    .    'ELSE '.$this->f_right.' '
244                    .    'END '
245                    . 'WHERE '.$this->f_right.' > '.$node_right
246                    . $this->getCondition();
247               }
248               
249               $this->con->commit();
250          }
251          catch (Exception $e)
252          {
253               $this->con->rollback();
254               throw $e;
255          }
256     }
257     
258     public function resetOrder()
259     {
260          $rs = $this->con->select(
261               'SELECT '.$this->f_id.' '
262               .'FROM '.$this->table.' '
263               .$this->getCondition('WHERE')
264               .'ORDER BY '.$this->f_left.' ASC '
265          );
266         
267          $lft = 2;
268          $this->con->begin();
269          try
270          {
271               while ($rs->fetch()) {
272                    $this->con->execute(
273                         'UPDATE '.$this->table.' SET '
274                         .$this->f_left.' = '.($lft++).', '
275                         .$this->f_right.' = '.($lft++).' '
276                         .'WHERE '.$this->f_id .' = '.(integer) $rs->{$this->f_id}.' '
277                         .$this->getCondition()
278                    );
279               }
280               $this->con->commit();
281          }
282          catch (Exception $e)
283          {
284               $this->con->rollback();
285               throw $e;
286          }
287     }
288     
289     public function setNodeParent($node,$target=0)
290     {
291          if ($node == $target) {
292               return;
293          }
294          $node = (integer) $node;
295          $target = (integer) $target;
296         
297          $rs = $this->getChildren(0,$node);
298          if ($rs->isEmpty()) {
299               throw new Exception('Node does not exist.');
300          }
301          $node_left = (integer) $rs->{$this->f_left};
302          $node_right = (integer) $rs->{$this->f_right};
303          $node_level = (integer) $rs->level;
304         
305          if ($target > 0)
306          {
307               $rs = $this->getChildren(0,$target);
308          }
309          else
310          {
311               $rs = $this->con->select(
312                    'SELECT MIN('.$this->f_left.')-1 AS '.$this->f_left.', MAX('.$this->f_right.')+1 AS '.$this->f_right.', 0 AS level '.
313                    'FROM '.$this->table.' '.
314                    $this->getCondition('WHERE')
315               );
316          }
317          $target_left = (integer) $rs->{$this->f_left};
318          $target_right = (integer) $rs->{$this->f_right};
319          $target_level = (integer) $rs->level;
320         
321          if ($node_left == $target_left
322               || ($target_left >= $node_left && $target_left <= $node_right)
323               || ($node_level == $target_level+1 && $node_left > $target_left && $node_right < $target_right)
324          )
325          {
326               throw new Exception('Cannot move tree');
327          }
328         
329          if ($target_left < $node_left && $target_right > $node_right && $target_level < $node_level -1)
330          {
331               $sql = 'UPDATE '.$this->table.' SET '
332               . $this->f_right.' = CASE '
333               .    'WHEN '.$this->f_right.' BETWEEN '.($node_right+1).' AND '.($target_right-1).' '
334               .         'THEN '.$this->f_right.'-('.($node_right-$node_left+1).') '
335               .    'WHEN '.$this->f_left.' BETWEEN '.$node_left.' AND '.$node_right.' '
336               .         'THEN '.$this->f_right.'+'.((($target_right-$node_right-$node_level+$target_level)/2)*2+$node_level-$target_level-1).' '
337               .    'ELSE '
338               .         $this->f_right.' '
339               .    'END, '
340               . $this->f_left.' = CASE '
341               .    'WHEN '.$this->f_left.' BETWEEN '.($node_right+1).' AND '.($target_right-1).' '
342               .         'THEN '.$this->f_left.'-('.($node_right-$node_left+1).') '
343               .    'WHEN '.$this->f_left.' BETWEEN '.$node_left.' AND '.$node_right.' '
344               .         'THEN '.$this->f_left.'+'.((($target_right-$node_right-$node_level+$target_level)/2)*2+$node_level-$target_level-1).' '
345               .    'ELSE '.$this->f_left.' '
346               .    'END '
347               . 'WHERE '.$this->f_left.' BETWEEN '.($target_left+1).' AND '.($target_right-1).'';
348          }
349          elseif ($target_left < $node_left)
350          {
351               $sql = 'UPDATE '.$this->table.' SET '
352               . $this->f_left.' = CASE '
353               .    'WHEN '.$this->f_left.' BETWEEN '.$target_right.' AND '.($node_left-1).' '
354               .         'THEN '.$this->f_left.'+'.($node_right-$node_left+1).' '
355               .    'WHEN '.$this->f_left.' BETWEEN '.$node_left.' AND '.$node_right.' '
356               .         'THEN '.$this->f_left.'-('.($node_left-$target_right).') '
357               .    'ELSE '.$this->f_left .' '
358               .    'END, '
359               . $this->f_right.' = CASE '
360               .    'WHEN '.$this->f_right.' BETWEEN '.$target_right.' AND '.$node_left.' '
361               .         'THEN '.$this->f_right.'+'.($node_right-$node_left+1).' '
362               .    'WHEN '.$this->f_right.' BETWEEN '.$node_left.' AND '.$node_right.' '
363               .         'THEN '.$this->f_right.'-('.($node_left-$target_right).') '
364               .    'ELSE '.$this->f_right.' '
365               .    'END '
366               . 'WHERE ('.$this->f_left.' BETWEEN '.$target_left.' AND '.$node_right. ' '
367               .    'OR '.$this->f_right.' BETWEEN '.$target_left.' AND '.$node_right.')';
368          }
369          else
370          {
371               $sql = 'UPDATE '.$this->table.' SET '
372               . $this->f_left.' = CASE '
373               .    'WHEN '.$this->f_left.' BETWEEN '.$node_right.' AND '.$target_right.' '
374               .         'THEN '.$this->f_left.'-'.($node_right-$node_left+1).' '
375               .    'WHEN '.$this->f_left.' BETWEEN '.$node_left.' AND '.$node_right.' '
376               .         'THEN '.$this->f_left.'+'.($target_right-1-$node_right).' '
377               .    'ELSE '.$this->f_left.' '
378               .    'END, '
379               . $this->f_right.' = CASE '
380               .    'WHEN '.$this->f_right.' BETWEEN '.($node_right+1).' AND '.($target_right-1).' '
381               .         'THEN '.$this->f_right.'-'.($node_right-$node_left+1).' '
382               .    'WHEN '.$this->f_right.' BETWEEN '.$node_left.' AND '.$node_right.' '
383               .         'THEN '.$this->f_right.'+'.($target_right-1-$node_right).' '
384               .    'ELSE '.$this->f_right.' '
385               .    'END '
386               . 'WHERE ('.$this->f_left.' BETWEEN '.$node_left.' AND '.$target_right.' '
387               .    'OR '.$this->f_right.' BETWEEN '.$node_left.' AND '.$target_right.')';
388          }
389         
390          $sql .= ' '.$this->getCondition();
391         
392          $this->con->execute($sql);
393     }
394     
395     public function setNodePosition($nodeA,$nodeB,$position='after')
396     {
397          $nodeA = (integer) $nodeA;
398          $nodeB = (integer) $nodeB;
399         
400          $rs = $this->getChildren(0,$nodeA);
401          if ($rs->isEmpty()) {
402               throw new Exception('Node does not exist.');
403          }
404          $A_left = $rs->{$this->f_left};
405          $A_right = $rs->{$this->f_right};
406          $A_level = $rs->level;
407         
408          $rs = $this->getChildren(0,$nodeB);
409          if ($rs->isEmpty()) {
410               throw new Exception('Node does not exist.');
411          }
412          $B_left = $rs->{$this->f_left};
413          $B_right = $rs->{$this->f_right};
414          $B_level = $rs->level;
415         
416          if ($A_level != $B_level) {
417               throw new Exception('Cannot change position');
418          }
419         
420          $rs = $this->getParents($nodeA);
421          $parentA = $rs->isEmpty() ? 0 : $rs->{$this->f_id};
422          $rs = $this->getParents($nodeB);
423          $parentB = $rs->isEmpty() ? 0 : $rs->{$this->f_id};
424         
425          if ($parentA != $parentB) {
426               throw new Exception('Cannot change position');
427          }
428         
429          if ($position == 'before')
430          {
431               if ($A_left > $B_left) {
432                    $sql = 'UPDATE '.$this->table.' SET '
433                    . $this->f_right.' = CASE WHEN '.$this->f_left.' BETWEEN '.$A_left.' AND '.$A_right.' THEN '.$this->f_right.' - ('.($A_left - $B_left).') '
434                    . 'WHEN '.$this->f_left.' BETWEEN '.$B_left.' AND '.($A_left - 1).' THEN '.$this->f_right.' +  '.($A_right - $A_left + 1).' ELSE '.$this->f_right.' END, '
435                    . $this->f_left.' = CASE WHEN '.$this->f_left.' BETWEEN '.$A_left.' AND '.$A_right.' THEN '.$this->f_left.' - ('.($A_left - $B_left).') '
436                    . 'WHEN '.$this->f_left.' BETWEEN '.$B_left.' AND '.($A_left - 1).' THEN '.$this->f_left.' + '.($A_right - $A_left + 1).' ELSE '.$this->f_left.' END '
437                    . 'WHERE '.$this->f_left.' BETWEEN '.$B_left.' AND '.$A_right;
438               } else {
439                    $sql = 'UPDATE '.$this->table.' SET '
440                    . $this->f_right.' = CASE WHEN '.$this->f_left.' BETWEEN '.$A_left.' AND '.$A_right.' THEN '.$this->f_right.' + '.(($B_left - $A_left) - ($A_right - $A_left + 1)).' '
441                    . 'WHEN '.$this->f_left.' BETWEEN '.($A_right + 1).' AND '.($B_left - 1).' THEN '.$this->f_right.' - ('.(($A_right - $A_left + 1)).') ELSE '.$this->f_right.' END, '
442                    . $this->f_left.' = CASE WHEN '.$this->f_left.' BETWEEN '.$A_left.' AND '.$A_right.' THEN '.$this->f_left.' + '.(($B_left - $A_left) - ($A_right - $A_left + 1)).' '
443                    . 'WHEN '.$this->f_left.' BETWEEN '.($A_right + 1).' AND '.($B_left - 1).' THEN '.$this->f_left.' - ('.($A_right - $A_left + 1).') ELSE '.$this->f_left.' END '
444                    . 'WHERE '.$this->f_left.' BETWEEN '.$A_left.' AND '.($B_left - 1);
445               }
446          }
447          else
448          {
449               if ($A_left > $B_left) {
450                    $sql = 'UPDATE '.$this->table.' SET '
451                    . $this->f_right.' = CASE WHEN '.$this->f_left.' BETWEEN '.$A_left.' AND '.$A_right.' THEN '.$this->f_right.' - ('.($A_left - $B_left - ($B_right - $B_left + 1)).') '
452                    . 'WHEN '.$this->f_left.' BETWEEN '.($B_right + 1).' AND '.($A_left - 1).' THEN '.$this->f_right.' +  '.($A_right - $A_left + 1).' ELSE '.$this->f_right.' END, '
453                    . $this->f_left.' = CASE WHEN '.$this->f_left.' BETWEEN '.$A_left.' AND '.$A_right.' THEN '.$this->f_left.' - ('.($A_left - $B_left - ($B_right - $B_left + 1)).') '
454                    . 'WHEN '.$this->f_left.' BETWEEN '.($B_right + 1).' AND '.($A_left - 1).' THEN '.$this->f_left.' + '.($A_right - $A_left + 1).' ELSE '.$this->f_left.' END '
455                    . 'WHERE '.$this->f_left.' BETWEEN '.($B_right + 1).' AND '.$A_right;
456               } else {
457                    $sql = 'UPDATE '.$this->table.' SET '
458                    . $this->f_right.' = CASE WHEN '.$this->f_left.' BETWEEN '.$A_left.' AND '.$A_right.' THEN '.$this->f_right.' + '.($B_right - $A_right).' '
459                    . 'WHEN '.$this->f_left.' BETWEEN '.($A_right + 1).' AND '.$B_right.' THEN '.$this->f_right.' - ('.(($A_right - $A_left + 1)).') ELSE '.$this->f_right.' END, '
460                    . $this->f_left.' = CASE WHEN '.$this->f_left.' BETWEEN '.$A_left.' AND '.$A_right.' THEN '.$this->f_left.' + '.($B_right - $A_right).' '
461                    . 'WHEN '.$this->f_left.' BETWEEN '.($A_right + 1).' AND '.$B_right.' THEN '.$this->f_left.' - ('.($A_right - $A_left + 1).') ELSE '.$this->f_left.' END '
462                    . 'WHERE '.$this->f_left.' BETWEEN '.$A_left.' AND '.$B_right;
463               }
464          }
465         
466          $sql .= $this->getCondition();
467          $this->con->execute($sql);
468     }
469     
470     protected function getCondition($start='AND',$prefix='')
471     {
472          if (empty($this->add_condition)) {
473               return '';
474          }
475         
476          $w = array();
477          foreach ($this->add_condition as $c => $n) {
478               $w[] = $prefix.$c.' = '.$n;
479          }
480          return ' '.$start.' '.implode(' AND ',$w).' ';
481     }
482}
483?>
Note: See TracBrowser for help on using the repository browser.

Sites map