Dotclear

source: inc/core/class.dc.categories.php @ 2566:9bf417837888

Revision 2566:9bf417837888, 16.7 KB checked in by franck <carnet.franck.paul@…>, 12 years ago (diff)

Add some people in CREDITS, remove trailing spaces and tabs.

Line 
1<?php
2# -- BEGIN LICENSE BLOCK ---------------------------------------
3#
4# This file is part of Dotclear 2.
5#
6# Copyright (c) 2003-2013 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 updatePosition($id,$left,$right)
191        {
192                $sql = 'UPDATE '.$this->table.' SET '
193                        .$this->f_left.' = '.$left.', '
194                        .$this->f_right.' = '.$right
195                        .' WHERE '.$this->f_id .' = '.(integer) $id
196                        .$this->getCondition();
197
198                $this->con->begin();
199                try {
200                        $this->con->execute($sql);
201                        $this->con->commit();
202                } catch (Exception $e) {
203                        $this->con->rollback();
204                        throw $e;
205                }
206        }
207
208     public function deleteNode($node,$keep_children=true)
209     {
210          $node = (integer) $node;
211
212          $rs = $this->getChildren(0,$node);
213          if ($rs->isEmpty()) {
214               throw new Exception('Node does not exist.');
215          }
216          $node_left = (integer) $rs->{$this->f_left};
217          $node_right = (integer) $rs->{$this->f_right};
218
219          try
220          {
221               $this->con->begin();
222
223               if ($keep_children)
224               {
225                    $this->con->execute('DELETE FROM '.$this->table.' WHERE '.$this->f_id.' = '.$node);
226
227                    $sql = 'UPDATE '.$this->table.' SET '
228                    . $this->f_right.' = CASE '
229                    .    'WHEN '.$this->f_right.' BETWEEN '.$node_left.' AND '.$node_right.' '
230                    .         'THEN '.$this->f_right.' - 1 '
231                    .    'WHEN '.$this->f_right.' > '.$node_right.' '
232                    .         'THEN '.$this->f_right.' - 2 '
233                    .    'ELSE '.$this->f_right.' '
234                    .    'END, '
235                    . $this->f_left.' = CASE '
236                    .    'WHEN '.$this->f_left.' BETWEEN '.$node_left.' AND '.$node_right.' '
237                    .         'THEN '.$this->f_left.' - 1 '
238                    .    'WHEN '.$this->f_left.' > '.$node_right.' '
239                    .         'THEN '.$this->f_left.' - 2 '
240                    .    'ELSE '.$this->f_left.' '
241                    .    'END '
242                    . 'WHERE '.$this->f_right.' > '.$node_left
243                    . $this->getCondition();
244
245                    $this->con->execute($sql);
246               }
247               else
248               {
249                    $this->con->execute('DELETE FROM '.$this->table.' WHERE '.$this->f_left.' BETWEEN '.$node_left.' AND '.$node_right);
250
251                    $node_delta = $node_right - $node_left + 1;
252                    $sql = 'UPDATE '.$this->table.' SET '
253                    . $this->f_left.' = CASE '
254                    .    'WHEN '.$this->f_left.' > '.$node_left.' '
255                    .         'THEN '.$this->f_left.' - ('.$node_delta.') '
256                    .    'ELSE '.$this->f_left.' '
257                    .    'END, '
258                    . $this->f_right.' = CASE '
259                    .    'WHEN '.$this->f_right.' > '.$node_left.' '
260                    .         'THEN '.$this->f_right.' - ('.$node_delta.') '
261                    .    'ELSE '.$this->f_right.' '
262                    .    'END '
263                    . 'WHERE '.$this->f_right.' > '.$node_right
264                    . $this->getCondition();
265               }
266
267               $this->con->commit();
268          }
269          catch (Exception $e)
270          {
271               $this->con->rollback();
272               throw $e;
273          }
274     }
275
276     public function resetOrder()
277     {
278          $rs = $this->con->select(
279               'SELECT '.$this->f_id.' '
280               .'FROM '.$this->table.' '
281               .$this->getCondition('WHERE')
282               .'ORDER BY '.$this->f_left.' ASC '
283          );
284
285          $lft = 2;
286          $this->con->begin();
287          try
288          {
289               while ($rs->fetch()) {
290                    $this->con->execute(
291                         'UPDATE '.$this->table.' SET '
292                         .$this->f_left.' = '.($lft++).', '
293                         .$this->f_right.' = '.($lft++).' '
294                         .'WHERE '.$this->f_id .' = '.(integer) $rs->{$this->f_id}.' '
295                         .$this->getCondition()
296                    );
297               }
298               $this->con->commit();
299          }
300          catch (Exception $e)
301          {
302               $this->con->rollback();
303               throw $e;
304          }
305     }
306
307     public function setNodeParent($node,$target=0)
308     {
309          if ($node == $target) {
310               return;
311          }
312          $node = (integer) $node;
313          $target = (integer) $target;
314
315          $rs = $this->getChildren(0,$node);
316          if ($rs->isEmpty()) {
317               throw new Exception('Node does not exist.');
318          }
319          $node_left = (integer) $rs->{$this->f_left};
320          $node_right = (integer) $rs->{$this->f_right};
321          $node_level = (integer) $rs->level;
322
323          if ($target > 0)
324          {
325               $rs = $this->getChildren(0,$target);
326          }
327          else
328          {
329               $rs = $this->con->select(
330                    'SELECT MIN('.$this->f_left.')-1 AS '.$this->f_left.', MAX('.$this->f_right.')+1 AS '.$this->f_right.', 0 AS level '.
331                    'FROM '.$this->table.' '.
332                    $this->getCondition('WHERE')
333               );
334          }
335          $target_left = (integer) $rs->{$this->f_left};
336          $target_right = (integer) $rs->{$this->f_right};
337          $target_level = (integer) $rs->level;
338
339          if ($node_left == $target_left
340               || ($target_left >= $node_left && $target_left <= $node_right)
341               || ($node_level == $target_level+1 && $node_left > $target_left && $node_right < $target_right)
342          )
343          {
344               throw new Exception('Cannot move tree');
345          }
346
347          if ($target_left < $node_left && $target_right > $node_right && $target_level < $node_level -1)
348          {
349               $sql = 'UPDATE '.$this->table.' SET '
350               . $this->f_right.' = CASE '
351               .    'WHEN '.$this->f_right.' BETWEEN '.($node_right+1).' AND '.($target_right-1).' '
352               .         'THEN '.$this->f_right.'-('.($node_right-$node_left+1).') '
353               .    'WHEN '.$this->f_left.' BETWEEN '.$node_left.' AND '.$node_right.' '
354               .         'THEN '.$this->f_right.'+'.((($target_right-$node_right-$node_level+$target_level)/2)*2+$node_level-$target_level-1).' '
355               .    'ELSE '
356               .         $this->f_right.' '
357               .    'END, '
358               . $this->f_left.' = CASE '
359               .    'WHEN '.$this->f_left.' BETWEEN '.($node_right+1).' AND '.($target_right-1).' '
360               .         'THEN '.$this->f_left.'-('.($node_right-$node_left+1).') '
361               .    'WHEN '.$this->f_left.' BETWEEN '.$node_left.' AND '.$node_right.' '
362               .         'THEN '.$this->f_left.'+'.((($target_right-$node_right-$node_level+$target_level)/2)*2+$node_level-$target_level-1).' '
363               .    'ELSE '.$this->f_left.' '
364               .    'END '
365               . 'WHERE '.$this->f_left.' BETWEEN '.($target_left+1).' AND '.($target_right-1).'';
366          }
367          elseif ($target_left < $node_left)
368          {
369               $sql = 'UPDATE '.$this->table.' SET '
370               . $this->f_left.' = CASE '
371               .    'WHEN '.$this->f_left.' BETWEEN '.$target_right.' AND '.($node_left-1).' '
372               .         'THEN '.$this->f_left.'+'.($node_right-$node_left+1).' '
373               .    'WHEN '.$this->f_left.' BETWEEN '.$node_left.' AND '.$node_right.' '
374               .         'THEN '.$this->f_left.'-('.($node_left-$target_right).') '
375               .    'ELSE '.$this->f_left .' '
376               .    'END, '
377               . $this->f_right.' = CASE '
378               .    'WHEN '.$this->f_right.' BETWEEN '.$target_right.' AND '.$node_left.' '
379               .         'THEN '.$this->f_right.'+'.($node_right-$node_left+1).' '
380               .    'WHEN '.$this->f_right.' BETWEEN '.$node_left.' AND '.$node_right.' '
381               .         'THEN '.$this->f_right.'-('.($node_left-$target_right).') '
382               .    'ELSE '.$this->f_right.' '
383               .    'END '
384               . 'WHERE ('.$this->f_left.' BETWEEN '.$target_left.' AND '.$node_right. ' '
385               .    'OR '.$this->f_right.' BETWEEN '.$target_left.' AND '.$node_right.')';
386          }
387          else
388          {
389               $sql = 'UPDATE '.$this->table.' SET '
390               . $this->f_left.' = CASE '
391               .    'WHEN '.$this->f_left.' BETWEEN '.$node_right.' AND '.$target_right.' '
392               .         'THEN '.$this->f_left.'-'.($node_right-$node_left+1).' '
393               .    'WHEN '.$this->f_left.' BETWEEN '.$node_left.' AND '.$node_right.' '
394               .         'THEN '.$this->f_left.'+'.($target_right-1-$node_right).' '
395               .    'ELSE '.$this->f_left.' '
396               .    'END, '
397               . $this->f_right.' = CASE '
398               .    'WHEN '.$this->f_right.' BETWEEN '.($node_right+1).' AND '.($target_right-1).' '
399               .         'THEN '.$this->f_right.'-'.($node_right-$node_left+1).' '
400               .    'WHEN '.$this->f_right.' BETWEEN '.$node_left.' AND '.$node_right.' '
401               .         'THEN '.$this->f_right.'+'.($target_right-1-$node_right).' '
402               .    'ELSE '.$this->f_right.' '
403               .    'END '
404               . 'WHERE ('.$this->f_left.' BETWEEN '.$node_left.' AND '.$target_right.' '
405               .    'OR '.$this->f_right.' BETWEEN '.$node_left.' AND '.$target_right.')';
406          }
407
408          $sql .= ' '.$this->getCondition();
409
410          $this->con->execute($sql);
411     }
412
413     public function setNodePosition($nodeA,$nodeB,$position='after')
414     {
415          $nodeA = (integer) $nodeA;
416          $nodeB = (integer) $nodeB;
417
418          $rs = $this->getChildren(0,$nodeA);
419          if ($rs->isEmpty()) {
420               throw new Exception('Node does not exist.');
421          }
422          $A_left = $rs->{$this->f_left};
423          $A_right = $rs->{$this->f_right};
424          $A_level = $rs->level;
425
426          $rs = $this->getChildren(0,$nodeB);
427          if ($rs->isEmpty()) {
428               throw new Exception('Node does not exist.');
429          }
430          $B_left = $rs->{$this->f_left};
431          $B_right = $rs->{$this->f_right};
432          $B_level = $rs->level;
433
434          if ($A_level != $B_level) {
435               throw new Exception('Cannot change position');
436          }
437
438          $rs = $this->getParents($nodeA);
439          $parentA = $rs->isEmpty() ? 0 : $rs->{$this->f_id};
440          $rs = $this->getParents($nodeB);
441          $parentB = $rs->isEmpty() ? 0 : $rs->{$this->f_id};
442
443          if ($parentA != $parentB) {
444               throw new Exception('Cannot change position');
445          }
446
447          if ($position == 'before')
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).') '
452                    . '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, '
453                    . $this->f_left.' = CASE WHEN '.$this->f_left.' BETWEEN '.$A_left.' AND '.$A_right.' THEN '.$this->f_left.' - ('.($A_left - $B_left).') '
454                    . '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 '
455                    . 'WHERE '.$this->f_left.' BETWEEN '.$B_left.' 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_left - $A_left) - ($A_right - $A_left + 1)).' '
459                    . '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, '
460                    . $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)).' '
461                    . '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 '
462                    . 'WHERE '.$this->f_left.' BETWEEN '.$A_left.' AND '.($B_left - 1);
463               }
464          }
465          else
466          {
467               if ($A_left > $B_left) {
468                    $sql = 'UPDATE '.$this->table.' SET '
469                    . $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)).') '
470                    . '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, '
471                    . $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)).') '
472                    . '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 '
473                    . 'WHERE '.$this->f_left.' BETWEEN '.($B_right + 1).' AND '.$A_right;
474               } else {
475                    $sql = 'UPDATE '.$this->table.' SET '
476                    . $this->f_right.' = CASE WHEN '.$this->f_left.' BETWEEN '.$A_left.' AND '.$A_right.' THEN '.$this->f_right.' + '.($B_right - $A_right).' '
477                    . '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, '
478                    . $this->f_left.' = CASE WHEN '.$this->f_left.' BETWEEN '.$A_left.' AND '.$A_right.' THEN '.$this->f_left.' + '.($B_right - $A_right).' '
479                    . '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 '
480                    . 'WHERE '.$this->f_left.' BETWEEN '.$A_left.' AND '.$B_right;
481               }
482          }
483
484          $sql .= $this->getCondition();
485          $this->con->execute($sql);
486     }
487
488     protected function getCondition($start='AND',$prefix='')
489     {
490          if (empty($this->add_condition)) {
491               return '';
492          }
493
494          $w = array();
495          foreach ($this->add_condition as $c => $n) {
496               $w[] = $prefix.$c.' = '.$n;
497          }
498          return ' '.$start.' '.implode(' AND ',$w).' ';
499     }
500}
Note: See TracBrowser for help on using the repository browser.

Sites map