Joomla Platform  13.1
Documentation des API du framework Joomla Platform
 Tout Classes Espaces de nommage Fichiers Fonctions Variables Pages
categories.php
Aller à la documentation de ce fichier.
1 <?php
2 /**
3  * @package Joomla.Legacy
4  * @subpackage Categories
5  *
6  * @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
7  * @license GNU General Public License version 2 or later; see LICENSE
8  */
9 
10 defined('JPATH_PLATFORM') or die;
11 
12 /**
13  * JCategories Class.
14  *
15  * @package Joomla.Legacy
16  * @subpackage Categories
17  * @since 11.1
18  */
20 {
21  /**
22  * Array to hold the object instances
23  *
24  * @var array
25  * @since 11.1
26  */
27  public static $instances = array();
28 
29  /**
30  * Array of category nodes
31  *
32  * @var mixed
33  * @since 11.1
34  */
35  protected $_nodes;
36 
37  /**
38  * Array of checked categories -- used to save values when _nodes are null
39  *
40  * @var array
41  * @since 11.1
42  */
44 
45  /**
46  * Name of the extension the categories belong to
47  *
48  * @var string
49  * @since 11.1
50  */
51  protected $_extension = null;
52 
53  /**
54  * Name of the linked content table to get category content count
55  *
56  * @var string
57  * @since 11.1
58  */
59  protected $_table = null;
60 
61  /**
62  * Name of the category field
63  *
64  * @var string
65  * @since 11.1
66  */
67  protected $_field = null;
68 
69  /**
70  * Name of the key field
71  *
72  * @var string
73  * @since 11.1
74  */
75  protected $_key = null;
76 
77  /**
78  * Name of the items state field
79  *
80  * @var string
81  * @since 11.1
82  */
83  protected $_statefield = null;
84 
85  /**
86  * Array of options
87  *
88  * @var array
89  * @since 11.1
90  */
91  protected $_options = null;
92 
93  /**
94  * Class constructor
95  *
96  * @param array $options Array of options
97  *
98  * @since 11.1
99  */
100  public function __construct($options)
101  {
102  $this->_extension = $options['extension'];
103  $this->_table = $options['table'];
104  $this->_field = (isset($options['field']) && $options['field']) ? $options['field'] : 'catid';
105  $this->_key = (isset($options['key']) && $options['key']) ? $options['key'] : 'id';
106  $this->_statefield = (isset($options['statefield'])) ? $options['statefield'] : 'state';
107  $options['access'] = (isset($options['access'])) ? $options['access'] : 'true';
108  $options['published'] = (isset($options['published'])) ? $options['published'] : 1;
109  $this->_options = $options;
110 
111  return true;
112  }
113 
114  /**
115  * Returns a reference to a JCategories object
116  *
117  * @param string $extension Name of the categories extension
118  * @param array $options An array of options
119  *
120  * @return JCategories JCategories object
121  *
122  * @since 11.1
123  */
124  public static function getInstance($extension, $options = array())
125  {
126  $hash = md5($extension . serialize($options));
127 
128  if (isset(self::$instances[$hash]))
129  {
130  return self::$instances[$hash];
131  }
132 
133  $parts = explode('.', $extension);
134  $component = 'com_' . strtolower($parts[0]);
135  $section = count($parts) > 1 ? $parts[1] : '';
136  $classname = ucfirst(substr($component, 4)) . ucfirst($section) . 'Categories';
137 
138  if (!class_exists($classname))
139  {
140  $path = JPATH_SITE . '/components/' . $component . '/helpers/category.php';
141  if (is_file($path))
142  {
143  include_once $path;
144  }
145  else
146  {
147  return false;
148  }
149  }
150 
151  self::$instances[$hash] = new $classname($options);
152 
153  return self::$instances[$hash];
154  }
155 
156  /**
157  * Loads a specific category and all its children in a JCategoryNode object
158  *
159  * @param mixed $id an optional id integer or equal to 'root'
160  * @param boolean $forceload True to force the _load method to execute
161  *
162  * @return mixed JCategoryNode object or null if $id is not valid
163  *
164  * @since 11.1
165  */
166  public function get($id = 'root', $forceload = false)
167  {
168  if ($id !== 'root')
169  {
170  $id = (int) $id;
171 
172  if ($id == 0)
173  {
174  $id = 'root';
175  }
176  }
177 
178  // If this $id has not been processed yet, execute the _load method
179  if ((!isset($this->_nodes[$id]) && !isset($this->_checkedCategories[$id])) || $forceload)
180  {
181  $this->_load($id);
182  }
183 
184  // If we already have a value in _nodes for this $id, then use it.
185  if (isset($this->_nodes[$id]))
186  {
187  return $this->_nodes[$id];
188  }
189  // If we processed this $id already and it was not valid, then return null.
190  elseif (isset($this->_checkedCategories[$id]))
191  {
192  return null;
193  }
194 
195  return false;
196  }
197 
198  /**
199  * Load method
200  *
201  * @param integer $id Id of category to load
202  *
203  * @return void
204  *
205  * @since 11.1
206  */
207  protected function _load($id)
208  {
209  $db = JFactory::getDbo();
210  $user = JFactory::getUser();
211  $extension = $this->_extension;
212 
213  // Record that has this $id has been checked
214  $this->_checkedCategories[$id] = true;
215 
216  $query = $db->getQuery(true);
217 
218  // Right join with c for category
219  $query->select('c.id, c.asset_id, c.access, c.alias, c.checked_out, c.checked_out_time,
220  c.created_time, c.created_user_id, c.description, c.extension, c.hits, c.language, c.level,
221  c.lft, c.metadata, c.metadesc, c.metakey, c.modified_time, c.note, c.params, c.parent_id,
222  c.path, c.published, c.rgt, c.title, c.modified_user_id, c.version');
223  $case_when = ' CASE WHEN ';
224  $case_when .= $query->charLength('c.alias', '!=', '0');
225  $case_when .= ' THEN ';
226  $c_id = $query->castAsChar('c.id');
227  $case_when .= $query->concatenate(array($c_id, 'c.alias'), ':');
228  $case_when .= ' ELSE ';
229  $case_when .= $c_id . ' END as slug';
230  $query->select($case_when)
231  ->from('#__categories as c')
232  ->where('(c.extension=' . $db->quote($extension) . ' OR c.extension=' . $db->quote('system') . ')');
233 
234  if ($this->_options['access'])
235  {
236  $query->where('c.access IN (' . implode(',', $user->getAuthorisedViewLevels()) . ')');
237  }
238 
239  if ($this->_options['published'] == 1)
240  {
241  $query->where('c.published = 1');
242  }
243 
244  $query->order('c.lft');
245 
246  // Note: s for selected id
247  if ($id != 'root')
248  {
249  // Get the selected category
250  $query->join('LEFT', '#__categories AS s ON (s.lft <= c.lft AND s.rgt >= c.rgt) OR (s.lft > c.lft AND s.rgt < c.rgt)')
251  ->where('s.id=' . (int) $id);
252  }
253 
254  $subQuery = ' (SELECT cat.id as id FROM #__categories AS cat JOIN #__categories AS parent ' .
255  'ON cat.lft BETWEEN parent.lft AND parent.rgt WHERE parent.extension = ' . $db->quote($extension) .
256  ' AND parent.published != 1 GROUP BY cat.id) ';
257  $query->join('LEFT', $subQuery . 'AS badcats ON badcats.id = c.id')
258  ->where('badcats.id is null');
259 
260  // Note: i for item
261  if (isset($this->_options['countItems']) && $this->_options['countItems'] == 1)
262  {
263  if ($this->_options['published'] == 1)
264  {
265  $query->join(
266  'LEFT',
267  $db->quoteName($this->_table) . ' AS i ON i.' . $db->quoteName($this->_field) . ' = c.id AND i.' . $this->_statefield . ' = 1'
268  );
269  }
270  else
271  {
272  $query->join('LEFT', $db->quoteName($this->_table) . ' AS i ON i.' . $db->quoteName($this->_field) . ' = c.id');
273  }
274 
275  $query->select('COUNT(i.' . $db->quoteName($this->_key) . ') AS numitems');
276  }
277 
278  // Group by
279  $query->group(
280  'c.id, c.asset_id, c.access, c.alias, c.checked_out, c.checked_out_time,
281  c.created_time, c.created_user_id, c.description, c.extension, c.hits, c.language, c.level,
282  c.lft, c.metadata, c.metadesc, c.metakey, c.modified_time, c.note, c.params, c.parent_id,
283  c.path, c.published, c.rgt, c.title, c.modified_user_id, c.version'
284  );
285 
286  // Get the results
287  $db->setQuery($query);
288  $results = $db->loadObjectList('id');
289  $childrenLoaded = false;
290 
291  if (count($results))
292  {
293  // Foreach categories
294  foreach ($results as $result)
295  {
296  // Deal with root category
297  if ($result->id == 1)
298  {
299  $result->id = 'root';
300  }
301 
302  // Deal with parent_id
303  if ($result->parent_id == 1)
304  {
305  $result->parent_id = 'root';
306  }
307 
308  // Create the node
309  if (!isset($this->_nodes[$result->id]))
310  {
311  // Create the JCategoryNode and add to _nodes
312  $this->_nodes[$result->id] = new JCategoryNode($result, $this);
313 
314  // If this is not root and if the current node's parent is in the list or the current node parent is 0
315  if ($result->id != 'root' && (isset($this->_nodes[$result->parent_id]) || $result->parent_id == 1))
316  {
317  // Compute relationship between node and its parent - set the parent in the _nodes field
318  $this->_nodes[$result->id]->setParent($this->_nodes[$result->parent_id]);
319  }
320 
321  // If the node's parent id is not in the _nodes list and the node is not root (doesn't have parent_id == 0),
322  // then remove the node from the list
323  if (!(isset($this->_nodes[$result->parent_id]) || $result->parent_id == 0))
324  {
325  unset($this->_nodes[$result->id]);
326  continue;
327  }
328 
329  if ($result->id == $id || $childrenLoaded)
330  {
331  $this->_nodes[$result->id]->setAllLoaded();
332  $childrenLoaded = true;
333  }
334  }
335  elseif ($result->id == $id || $childrenLoaded)
336  {
337  // Create the JCategoryNode
338  $this->_nodes[$result->id] = new JCategoryNode($result, $this);
339 
340  if ($result->id != 'root' && (isset($this->_nodes[$result->parent_id]) || $result->parent_id))
341  {
342  // Compute relationship between node and its parent
343  $this->_nodes[$result->id]->setParent($this->_nodes[$result->parent_id]);
344  }
345 
346  if (!isset($this->_nodes[$result->parent_id]))
347  {
348  unset($this->_nodes[$result->id]);
349  continue;
350  }
351 
352  if ($result->id == $id || $childrenLoaded)
353  {
354  $this->_nodes[$result->id]->setAllLoaded();
355  $childrenLoaded = true;
356  }
357  }
358  }
359  }
360  else
361  {
362  $this->_nodes[$id] = null;
363  }
364  }
365 }
366 
367 /**
368  * Helper class to load Categorytree
369  *
370  * @package Joomla.Legacy
371  * @subpackage Categories
372  * @since 11.1
373  */
374 class JCategoryNode extends JObject
375 {
376  /**
377  * Primary key
378  *
379  * @var integer
380  * @since 11.1
381  */
382  public $id = null;
383 
384  /**
385  * The id of the category in the asset table
386  *
387  * @var integer
388  * @since 11.1
389  */
390  public $asset_id = null;
391 
392  /**
393  * The id of the parent of category in the asset table, 0 for category root
394  *
395  * @var integer
396  * @since 11.1
397  */
398  public $parent_id = null;
399 
400  /**
401  * The lft value for this category in the category tree
402  *
403  * @var integer
404  * @since 11.1
405  */
406  public $lft = null;
407 
408  /**
409  * The rgt value for this category in the category tree
410  *
411  * @var integer
412  * @since 11.1
413  */
414  public $rgt = null;
415 
416  /**
417  * The depth of this category's position in the category tree
418  *
419  * @var integer
420  * @since 11.1
421  */
422  public $level = null;
423 
424  /**
425  * The extension this category is associated with
426  *
427  * @var integer
428  * @since 11.1
429  */
430  public $extension = null;
431 
432  /**
433  * The menu title for the category (a short name)
434  *
435  * @var string
436  * @since 11.1
437  */
438  public $title = null;
439 
440  /**
441  * The the alias for the category
442  *
443  * @var string
444  * @since 11.1
445  */
446  public $alias = null;
447 
448  /**
449  * Description of the category.
450  *
451  * @var string
452  * @since 11.1
453  */
454  public $description = null;
455 
456  /**
457  * The publication status of the category
458  *
459  * @var boolean
460  * @since 11.1
461  */
462  public $published = null;
463 
464  /**
465  * Whether the category is or is not checked out
466  *
467  * @var boolean
468  * @since 11.1
469  */
470  public $checked_out = 0;
471 
472  /**
473  * The time at which the category was checked out
474  *
475  * @var string
476  * @since 11.1
477  */
478  public $checked_out_time = 0;
479 
480  /**
481  * Access level for the category
482  *
483  * @var integer
484  * @since 11.1
485  */
486  public $access = null;
487 
488  /**
489  * JSON string of parameters
490  *
491  * @var string
492  * @since 11.1
493  */
494  public $params = null;
495 
496  /**
497  * Metadata description
498  *
499  * @var string
500  * @since 11.1
501  */
502  public $metadesc = null;
503 
504  /**
505  * Key words for meta data
506  *
507  * @var string
508  * @since 11.1
509  */
510  public $metakey = null;
511 
512  /**
513  * JSON string of other meta data
514  *
515  * @var string
516  * @since 11.1
517  */
518  public $metadata = null;
519 
520  /**
521  * The ID of the user who created the category
522  *
523  * @var integer
524  * @since 11.1
525  */
526  public $created_user_id = null;
527 
528  /**
529  * The time at which the category was created
530  *
531  * @var string
532  * @since 11.1
533  */
534  public $created_time = null;
535 
536  /**
537  * The ID of the user who last modified the category
538  *
539  * @var integer
540  * @since 11.1
541  */
542  public $modified_user_id = null;
543 
544  /**
545  * The time at which the category was modified
546  *
547  * @var string
548  * @since 11.1
549  */
550  public $modified_time = null;
551 
552  /**
553  * Nmber of times the category has been viewed
554  *
555  * @var integer
556  * @since 11.1
557  */
558  public $hits = null;
559 
560  /**
561  * The language for the category in xx-XX format
562  *
563  * @var string
564  * @since 11.1
565  */
566  public $language = null;
567 
568  /**
569  * Number of items in this category or descendants of this category
570  *
571  * @var integer
572  * @since 11.1
573  */
574  public $numitems = null;
575 
576  /**
577  * Number of children items
578  *
579  * @var integer
580  * @since 11.1
581  */
582  public $childrennumitems = null;
583 
584  /**
585  * Slug fo the category (used in URL)
586  *
587  * @var string
588  * @since 11.1
589  */
590  public $slug = null;
591 
592  /**
593  * Array of assets
594  *
595  * @var array
596  * @since 11.1
597  */
598  public $assets = null;
599 
600  /**
601  * Parent Category object
602  *
603  * @var object
604  * @since 11.1
605  */
606  protected $_parent = null;
607 
608  /**
609  * @var Array of Children
610  * @since 11.1
611  */
612  protected $_children = array();
613 
614  /**
615  * Path from root to this category
616  *
617  * @var array
618  * @since 11.1
619  */
620  protected $_path = array();
621 
622  /**
623  * Category left of this one
624  *
625  * @var integer
626  * @since 11.1
627  */
628  protected $_leftSibling = null;
629 
630  /**
631  * Category right of this one
632  *
633  * @var
634  * @since 11.1
635  */
636  protected $_rightSibling = null;
637 
638  /**
639  * true if all children have been loaded
640  *
641  * @var boolean
642  * @since 11.1
643  */
644  protected $_allChildrenloaded = false;
645 
646  /**
647  * Constructor of this tree
648  *
649  * @var
650  * @since 11.1
651  */
652  protected $_constructor = null;
653 
654  /**
655  * Class constructor
656  *
657  * @param array $category The category data.
658  * @param JCategoryNode $constructor The tree constructor.
659  *
660  * @since 11.1
661  */
662  public function __construct($category = null, $constructor = null)
663  {
664  if ($category)
665  {
666  $this->setProperties($category);
667  if ($constructor)
668  {
669  $this->_constructor = $constructor;
670  }
671 
672  return true;
673  }
674 
675  return false;
676  }
677 
678  /**
679  * Set the parent of this category
680  *
681  * If the category already has a parent, the link is unset
682  *
683  * @param mixed $parent JCategoryNode for the parent to be set or null
684  *
685  * @return void
686  *
687  * @since 11.1
688  */
689  public function setParent($parent)
690  {
691  if ($parent instanceof JCategoryNode || is_null($parent))
692  {
693  if (!is_null($this->_parent))
694  {
695  $key = array_search($this, $this->_parent->_children);
696  unset($this->_parent->_children[$key]);
697  }
698 
699  if (!is_null($parent))
700  {
701  $parent->_children[] = & $this;
702  }
703 
704  $this->_parent = $parent;
705 
706  if ($this->id != 'root')
707  {
708  if ($this->parent_id != 1)
709  {
710  $this->_path = $parent->getPath();
711  }
712  $this->_path[] = $this->id . ':' . $this->alias;
713  }
714 
715  if (count($parent->_children) > 1)
716  {
717  end($parent->_children);
718  $this->_leftSibling = prev($parent->_children);
719  $this->_leftSibling->_rightsibling = & $this;
720  }
721  }
722  }
723 
724  /**
725  * Add child to this node
726  *
727  * If the child already has a parent, the link is unset
728  *
729  * @param JCategoryNode $child The child to be added.
730  *
731  * @return void
732  *
733  * @since 11.1
734  */
735  public function addChild($child)
736  {
737  if ($child instanceof JCategoryNode)
738  {
739  $child->setParent($this);
740  }
741  }
742 
743  /**
744  * Remove a specific child
745  *
746  * @param integer $id ID of a category
747  *
748  * @return void
749  *
750  * @since 11.1
751  */
752  public function removeChild($id)
753  {
754  $key = array_search($this, $this->_parent->_children);
755  unset($this->_parent->_children[$key]);
756  }
757 
758  /**
759  * Get the children of this node
760  *
761  * @param boolean $recursive False by default
762  *
763  * @return array The children
764  *
765  * @since 11.1
766  */
767  public function &getChildren($recursive = false)
768  {
769  if (!$this->_allChildrenloaded)
770  {
771  $temp = $this->_constructor->get($this->id, true);
772  if ($temp)
773  {
774  $this->_children = $temp->getChildren();
775  $this->_leftSibling = $temp->getSibling(false);
776  $this->_rightSibling = $temp->getSibling(true);
777  $this->setAllLoaded();
778  }
779  }
780 
781  if ($recursive)
782  {
783  $items = array();
784  foreach ($this->_children as $child)
785  {
786  $items[] = $child;
787  $items = array_merge($items, $child->getChildren(true));
788  }
789  return $items;
790  }
791 
792  return $this->_children;
793  }
794 
795  /**
796  * Get the parent of this node
797  *
798  * @return mixed JCategoryNode or null
799  *
800  * @since 11.1
801  */
802  public function getParent()
803  {
804  return $this->_parent;
805  }
806 
807  /**
808  * Test if this node has children
809  *
810  * @return boolean True if there is a child
811  *
812  * @since 11.1
813  */
814  public function hasChildren()
815  {
816  return count($this->_children);
817  }
818 
819  /**
820  * Test if this node has a parent
821  *
822  * @return boolean True if there is a parent
823  *
824  * @since 11.1
825  */
826  public function hasParent()
827  {
828  return $this->getParent() != null;
829  }
830 
831  /**
832  * Function to set the left or right sibling of a category
833  *
834  * @param JCategoryNode $sibling JCategoryNode object for the sibling
835  * @param boolean $right If set to false, the sibling is the left one
836  *
837  * @return void
838  *
839  * @since 11.1
840  */
841  public function setSibling($sibling, $right = true)
842  {
843  if ($right)
844  {
845  $this->_rightSibling = $sibling;
846  }
847  else
848  {
849  $this->_leftSibling = $sibling;
850  }
851  }
852 
853  /**
854  * Returns the right or left sibling of a category
855  *
856  * @param boolean $right If set to false, returns the left sibling
857  *
858  * @return mixed JCategoryNode object with the sibling information or
859  * NULL if there is no sibling on that side.
860  *
861  * @since 11.1
862  */
863  public function getSibling($right = true)
864  {
865  if (!$this->_allChildrenloaded)
866  {
867  $temp = $this->_constructor->get($this->id, true);
868  $this->_children = $temp->getChildren();
869  $this->_leftSibling = $temp->getSibling(false);
870  $this->_rightSibling = $temp->getSibling(true);
871  $this->setAllLoaded();
872  }
873 
874  if ($right)
875  {
876  return $this->_rightSibling;
877  }
878  else
879  {
880  return $this->_leftSibling;
881  }
882  }
883 
884  /**
885  * Returns the category parameters
886  *
887  * @return JRegistry
888  *
889  * @since 11.1
890  */
891  public function getParams()
892  {
893  if (!($this->params instanceof JRegistry))
894  {
895  $temp = new JRegistry;
896  $temp->loadString($this->params);
897  $this->params = $temp;
898  }
899 
900  return $this->params;
901  }
902 
903  /**
904  * Returns the category metadata
905  *
906  * @return JRegistry A JRegistry object containing the metadata
907  *
908  * @since 11.1
909  */
910  public function getMetadata()
911  {
912  if (!($this->metadata instanceof JRegistry))
913  {
914  $temp = new JRegistry;
915  $temp->loadString($this->metadata);
916  $this->metadata = $temp;
917  }
918 
919  return $this->metadata;
920  }
921 
922  /**
923  * Returns the category path to the root category
924  *
925  * @return array
926  *
927  * @since 11.1
928  */
929  public function getPath()
930  {
931  return $this->_path;
932  }
933 
934  /**
935  * Returns the user that created the category
936  *
937  * @param boolean $modified_user Returns the modified_user when set to true
938  *
939  * @return JUser A JUser object containing a userid
940  *
941  * @since 11.1
942  */
943  public function getAuthor($modified_user = false)
944  {
945  if ($modified_user)
946  {
947  return JFactory::getUser($this->modified_user_id);
948  }
949 
950  return JFactory::getUser($this->created_user_id);
951  }
952 
953  /**
954  * Set to load all children
955  *
956  * @return void
957  *
958  * @since 11.1
959  */
960  public function setAllLoaded()
961  {
962  $this->_allChildrenloaded = true;
963  foreach ($this->_children as $child)
964  {
965  $child->setAllLoaded();
966  }
967  }
968 
969  /**
970  * Returns the number of items.
971  *
972  * @param boolean $recursive If false number of children, if true number of descendants
973  *
974  * @return integer Number of children or descendants
975  *
976  * @since 11.1
977  */
978  public function getNumItems($recursive = false)
979  {
980  if ($recursive)
981  {
982  $count = $this->numitems;
983 
984  foreach ($this->getChildren() as $child)
985  {
986  $count = $count + $child->getNumItems(true);
987  }
988 
989  return $count;
990  }
991 
992  return $this->numitems;
993  }
994 }