Add EmailLogListPage
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / data / category / CategoryNodeTree.class.php
1 <?php
2
3 namespace wcf\data\category;
4
5 use wcf\system\category\CategoryHandler;
6 use wcf\system\exception\SystemException;
7
8 /**
9 * Represents a tree of category nodes.
10 *
11 * @author Matthias Schmidt
12 * @copyright 2001-2019 WoltLab GmbH
13 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
14 * @package WoltLabSuite\Core\Data\Category
15 */
16 class CategoryNodeTree implements \IteratorAggregate
17 {
18 /**
19 * list of ids of categories which will not be included in the node tree
20 * @var int[]
21 */
22 protected $excludedCategoryIDs = [];
23
24 /**
25 * if true, disabled categories are also included in the node tree
26 * @var bool
27 */
28 protected $includeDisabledCategories = false;
29
30 /**
31 * maximum depth considered when building the node tree
32 * @var int
33 */
34 protected $maxDepth = -1;
35
36 /**
37 * name of the category node class
38 * @var string
39 */
40 protected $nodeClassName = CategoryNode::class;
41
42 /**
43 * id of the parent category
44 * @var int
45 */
46 protected $parentCategoryID = 0;
47
48 /**
49 * parent category node
50 * @var CategoryNode
51 */
52 protected $parentNode;
53
54 /**
55 * name of the category object type
56 * @var string
57 */
58 protected $objectType = '';
59
60 /**
61 * Creates a new instance of CategoryNodeTree.
62 *
63 * @param string $objectType
64 * @param int $parentCategoryID
65 * @param bool $includeDisabledCategories
66 * @param int[] $excludedCategoryIDs
67 * @throws SystemException
68 */
69 public function __construct(
70 $objectType,
71 $parentCategoryID = 0,
72 $includeDisabledCategories = false,
73 array $excludedCategoryIDs = []
74 ) {
75 $this->objectType = $objectType;
76 $this->parentCategoryID = $parentCategoryID;
77 $this->includeDisabledCategories = $includeDisabledCategories;
78 $this->excludedCategoryIDs = $excludedCategoryIDs;
79
80 // validate category object type
81 if (CategoryHandler::getInstance()->getObjectTypeByName($this->objectType) === null) {
82 throw new SystemException("Unknown category object type '" . $this->objectType . "'");
83 }
84 }
85
86 /**
87 * Sets the maximum depth considered when building the node tree, defaults
88 * to -1 which equals infinite.
89 *
90 * @param int $maxDepth
91 */
92 public function setMaxDepth($maxDepth)
93 {
94 $this->maxDepth = $maxDepth;
95 }
96
97 /**
98 * Builds the category node tree.
99 */
100 protected function buildTree()
101 {
102 $this->parentNode = $this->getNode($this->parentCategoryID);
103 $this->buildTreeLevel($this->parentNode, $this->maxDepth);
104 }
105
106 /**
107 * Builds a certain level of the tree.
108 *
109 * @param CategoryNode $parentNode
110 * @param int $depth
111 */
112 protected function buildTreeLevel(CategoryNode $parentNode, $depth = 0)
113 {
114 if ($this->maxDepth != -1 && $depth < 0) {
115 return;
116 }
117
118 foreach ($this->getChildCategories($parentNode) as $childCategory) {
119 $childNode = $this->getNode($childCategory->categoryID);
120
121 if ($this->isIncluded($childNode)) {
122 $parentNode->addChild($childNode);
123
124 // build next level
125 $this->buildTreeLevel($childNode, $depth - 1);
126 }
127 }
128 }
129
130 /**
131 * Returns the category with the given id.
132 *
133 * @param int $categoryID
134 * @return Category
135 */
136 protected function getCategory($categoryID)
137 {
138 return CategoryHandler::getInstance()->getCategory($categoryID);
139 }
140
141 /**
142 * Returns the child categories of the given category node.
143 *
144 * @param CategoryNode $parentNode
145 * @return Category[]
146 */
147 protected function getChildCategories(CategoryNode $parentNode)
148 {
149 return CategoryHandler::getInstance()->getChildCategories($parentNode->categoryID, $parentNode->objectTypeID);
150 }
151
152 /**
153 * @inheritDoc
154 */
155 public function getIterator()
156 {
157 if ($this->parentNode === null) {
158 $this->buildTree();
159 }
160
161 return new \RecursiveIteratorIterator($this->parentNode, \RecursiveIteratorIterator::SELF_FIRST);
162 }
163
164 /**
165 * Returns the category node for the category with the given id.
166 *
167 * @param int $categoryID
168 * @return CategoryNode
169 */
170 protected function getNode($categoryID)
171 {
172 if (!$categoryID) {
173 $category = new Category(null, [
174 'categoryID' => 0,
175 'objectTypeID' => CategoryHandler::getInstance()->getObjectTypeByName($this->objectType)->objectTypeID,
176 ]);
177 } else {
178 $category = $this->getCategory($categoryID);
179 }
180
181 // decorate category if necessary
182 $decoratorClassName = \call_user_func([$this->nodeClassName, 'getBaseClass']);
183 if ($decoratorClassName != Category::class) {
184 $category = new $decoratorClassName($category);
185 }
186
187 return new $this->nodeClassName($category);
188 }
189
190 /**
191 * Returns true if the given category node fulfils all relevant conditions
192 * to be included in this tree.
193 *
194 * @param CategoryNode $categoryNode
195 * @return bool
196 */
197 protected function isIncluded(CategoryNode $categoryNode)
198 {
199 return (!$categoryNode->isDisabled || $this->includeDisabledCategories) && !\in_array(
200 $categoryNode->categoryID,
201 $this->excludedCategoryIDs
202 );
203 }
204 }