Commit | Line | Data |
---|---|---|
13d8b49b | 1 | <?php |
a9229942 | 2 | |
13d8b49b | 3 | namespace wcf\data\category; |
a9229942 | 4 | |
13d8b49b | 5 | use wcf\data\AbstractDatabaseObjectAction; |
cea5b918 | 6 | use wcf\data\ISortableAction; |
a427a8c8 | 7 | use wcf\data\IToggleAction; |
a6418d60 | 8 | use wcf\data\IToggleContainerAction; |
a9229942 | 9 | use wcf\data\language\item\LanguageItemAction; |
03ba10fa | 10 | use wcf\data\TDatabaseObjectToggle; |
13d8b49b | 11 | use wcf\system\category\CategoryHandler; |
9c3f3eb2 | 12 | use wcf\system\database\util\PreparedStatementConditionBuilder; |
13d8b49b | 13 | use wcf\system\exception\PermissionDeniedException; |
fc3d134b | 14 | use wcf\system\exception\SystemException; |
3631f7bd | 15 | use wcf\system\exception\UserInputException; |
13d8b49b MS |
16 | use wcf\system\user\collapsible\content\UserCollapsibleContentHandler; |
17 | use wcf\system\WCF; | |
18 | ||
19 | /** | |
20 | * Executes category-related actions. | |
a9229942 TD |
21 | * |
22 | * @author Matthias Schmidt | |
23 | * @copyright 2001-2019 WoltLab GmbH | |
24 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> | |
25 | * @package WoltLabSuite\Core\Data\Category | |
26 | * | |
27 | * @method Category create() | |
28 | * @method CategoryEditor[] getObjects() | |
29 | * @method CategoryEditor getSingleObject() | |
13d8b49b | 30 | */ |
a9229942 TD |
31 | class CategoryAction extends AbstractDatabaseObjectAction implements |
32 | ISortableAction, | |
33 | IToggleAction, | |
34 | IToggleContainerAction | |
35 | { | |
36 | use TDatabaseObjectToggle; | |
37 | ||
38 | /** | |
39 | * categorized object type | |
40 | * @var \wcf\data\object\type\ObjectType | |
41 | */ | |
42 | protected $objectType; | |
43 | ||
44 | /** | |
45 | * @inheritDoc | |
46 | */ | |
47 | protected $requireACP = ['create', 'delete', 'toggle', 'update', 'updatePosition']; | |
48 | ||
49 | /** | |
50 | * @inheritDoc | |
51 | */ | |
52 | public function delete() | |
53 | { | |
54 | // call category types | |
55 | foreach ($this->getObjects() as $categoryEditor) { | |
56 | $categoryEditor->getProcessor()->beforeDeletion($categoryEditor); | |
57 | } | |
58 | ||
59 | $returnValue = parent::delete(); | |
60 | ||
61 | // delete language items | |
62 | if (!empty($this->objects)) { | |
63 | // identify i18n labels | |
64 | $languageVariables = []; | |
65 | foreach ($this->getObjects() as $category) { | |
66 | if ($category->title === $category->getProcessor()->getI18nLangVarPrefix() . '.title.category' . $category->categoryID) { | |
67 | $languageVariables[] = $category->title; | |
68 | } | |
69 | if ($category->description === $category->getProcessor()->getI18nLangVarPrefix() . '.description.category' . $category->categoryID) { | |
70 | $languageVariables[] = $category->description; | |
71 | } | |
72 | } | |
73 | ||
74 | // remove language variables | |
75 | if (!empty($languageVariables)) { | |
76 | $conditions = new PreparedStatementConditionBuilder(); | |
77 | $conditions->add('languageItem IN (?)', [$languageVariables]); | |
78 | ||
79 | $sql = "SELECT languageItemID | |
80 | FROM wcf" . WCF_N . "_language_item | |
81 | " . $conditions; | |
82 | $statement = WCF::getDB()->prepareStatement($sql); | |
83 | $statement->execute($conditions->getParameters()); | |
84 | $languageItemIDs = $statement->fetchAll(\PDO::FETCH_COLUMN); | |
85 | ||
86 | $objectAction = new LanguageItemAction($languageItemIDs, 'delete'); | |
87 | $objectAction->executeAction(); | |
88 | } | |
89 | } | |
90 | ||
91 | // call category types | |
92 | foreach ($this->getObjects() as $categoryEditor) { | |
93 | $categoryEditor->getProcessor()->afterDeletion($categoryEditor); | |
94 | } | |
95 | ||
96 | return $returnValue; | |
97 | } | |
98 | ||
99 | /** | |
100 | * @inheritDoc | |
101 | */ | |
102 | public function toggleContainer() | |
103 | { | |
104 | $collapsibleObjectTypeName = $this->getObjects()[0]->getProcessor()->getObjectTypeName('com.woltlab.wcf.collapsibleContent'); | |
105 | if ($collapsibleObjectTypeName === null) { | |
106 | throw new SystemException("Categories of this type don't support collapsing"); | |
107 | } | |
108 | ||
109 | $objectTypeID = UserCollapsibleContentHandler::getInstance()->getObjectTypeID($collapsibleObjectTypeName); | |
110 | $collapsedCategories = UserCollapsibleContentHandler::getInstance()->getCollapsedContent($objectTypeID); | |
111 | ||
112 | $categoryID = $this->objects[0]->categoryID; | |
113 | if (\array_search($categoryID, $collapsedCategories) !== false) { | |
114 | UserCollapsibleContentHandler::getInstance()->markAsOpened($objectTypeID, $categoryID); | |
115 | } else { | |
116 | UserCollapsibleContentHandler::getInstance()->markAsCollapsed($objectTypeID, $categoryID); | |
117 | } | |
118 | } | |
119 | ||
120 | /** | |
121 | * @inheritDoc | |
122 | */ | |
123 | public function update() | |
124 | { | |
125 | // check if showOrder needs to be recalculated | |
126 | if (\count($this->objects) == 1 && isset($this->parameters['data']['parentCategoryID']) && isset($this->parameters['data']['showOrder'])) { | |
127 | $categoryEditor = $this->getObjects()[0]; | |
128 | if ($categoryEditor->parentCategoryID != $this->parameters['data']['parentCategoryID'] || $categoryEditor->showOrder != $this->parameters['data']['showOrder']) { | |
129 | $this->parameters['data']['showOrder'] = $categoryEditor->updateShowOrder( | |
130 | $this->parameters['data']['parentCategoryID'], | |
131 | $this->parameters['data']['showOrder'] | |
132 | ); | |
133 | } | |
134 | } | |
135 | ||
136 | parent::update(); | |
137 | ||
138 | if (isset($this->parameters['data']['parentCategoryID'])) { | |
139 | $objectType = null; | |
140 | $parentUpdates = []; | |
141 | ||
142 | foreach ($this->getObjects() as $category) { | |
143 | if ($objectType === null) { | |
144 | $objectType = $category->getObjectType(); | |
145 | } | |
146 | ||
147 | if ($category->parentCategoryID != $this->parameters['data']['parentCategoryID']) { | |
148 | $parentUpdates[$category->categoryID] = [ | |
149 | 'oldParentCategoryID' => $category->parentCategoryID, | |
150 | 'newParentCategoryID' => $this->parameters['data']['parentCategoryID'], | |
151 | ]; | |
152 | } | |
153 | } | |
154 | ||
155 | if (!empty($parentUpdates)) { | |
156 | $objectType->getProcessor()->changedParentCategories($parentUpdates); | |
157 | } | |
158 | } | |
159 | } | |
160 | ||
161 | /** | |
162 | * @inheritDoc | |
163 | */ | |
164 | public function updatePosition() | |
165 | { | |
166 | $objectType = null; | |
167 | $parentUpdates = []; | |
168 | ||
169 | WCF::getDB()->beginTransaction(); | |
170 | foreach ($this->parameters['data']['structure'] as $parentCategoryID => $categoryIDs) { | |
171 | $showOrder = 1; | |
172 | foreach ($categoryIDs as $categoryID) { | |
173 | $category = CategoryHandler::getInstance()->getCategory($categoryID); | |
174 | if ($objectType === null) { | |
175 | $objectType = $category->getObjectType(); | |
176 | } | |
177 | ||
178 | if ($category->parentCategoryID != $parentCategoryID) { | |
179 | $parentUpdates[$categoryID] = [ | |
180 | 'oldParentCategoryID' => $category->parentCategoryID, | |
181 | 'newParentCategoryID' => $parentCategoryID, | |
182 | ]; | |
183 | } | |
184 | ||
185 | $this->objects[$categoryID]->update([ | |
186 | 'parentCategoryID' => $parentCategoryID ? $this->objects[$parentCategoryID]->categoryID : 0, | |
187 | 'showOrder' => $showOrder++, | |
188 | ]); | |
189 | } | |
190 | } | |
191 | WCF::getDB()->commitTransaction(); | |
192 | ||
193 | if (!empty($parentUpdates)) { | |
194 | $objectType->getProcessor()->changedParentCategories($parentUpdates); | |
195 | } | |
196 | } | |
197 | ||
198 | /** | |
199 | * @inheritDoc | |
200 | */ | |
201 | public function validateCreate() | |
202 | { | |
203 | $this->readInteger('objectTypeID', false, 'data'); | |
204 | ||
205 | $objectType = CategoryHandler::getInstance()->getObjectType($this->parameters['data']['objectTypeID']); | |
206 | if ($objectType === null) { | |
207 | throw new UserInputException('objectTypeID', 'invalid'); | |
208 | } | |
209 | if (!$objectType->getProcessor()->canAddCategory()) { | |
210 | throw new PermissionDeniedException(); | |
211 | } | |
212 | } | |
213 | ||
214 | /** | |
215 | * @inheritDoc | |
216 | */ | |
217 | public function validateDelete() | |
218 | { | |
219 | // read objects | |
220 | if (empty($this->objects)) { | |
221 | $this->readObjects(); | |
222 | ||
223 | if (empty($this->objects)) { | |
224 | throw new UserInputException('objectIDs'); | |
225 | } | |
226 | } | |
227 | ||
228 | foreach ($this->getObjects() as $categoryEditor) { | |
229 | if (!$categoryEditor->getProcessor()->canDeleteCategory()) { | |
230 | throw new PermissionDeniedException(); | |
231 | } | |
232 | } | |
233 | } | |
234 | ||
235 | /** | |
236 | * @inheritDoc | |
237 | */ | |
238 | public function validateToggleContainer() | |
239 | { | |
240 | $this->validateUpdate(); | |
241 | } | |
242 | ||
243 | /** | |
244 | * @inheritDoc | |
245 | */ | |
246 | public function validateUpdate() | |
247 | { | |
248 | // read objects | |
249 | if (empty($this->objects)) { | |
250 | $this->readObjects(); | |
251 | ||
252 | if (empty($this->objects)) { | |
253 | throw new UserInputException('objectIDs'); | |
254 | } | |
255 | } | |
256 | ||
257 | foreach ($this->getObjects() as $categoryEditor) { | |
258 | if (!$categoryEditor->getProcessor()->canEditCategory()) { | |
259 | throw new PermissionDeniedException(); | |
260 | } | |
261 | } | |
262 | } | |
263 | ||
264 | /** | |
265 | * @inheritDoc | |
266 | */ | |
267 | public function validateUpdatePosition() | |
268 | { | |
269 | // validate 'structure' parameter | |
270 | if (!isset($this->parameters['data']['structure']) || !\is_array($this->parameters['data']['structure'])) { | |
271 | throw new UserInputException('structure'); | |
272 | } | |
273 | ||
274 | // validate given category ids | |
275 | foreach ($this->parameters['data']['structure'] as $parentCategoryID => $categoryIDs) { | |
276 | if ($parentCategoryID) { | |
277 | // validate category | |
278 | $category = CategoryHandler::getInstance()->getCategory($parentCategoryID); | |
279 | if ($category === null) { | |
280 | throw new UserInputException('structure'); | |
281 | } | |
282 | ||
283 | // validate permissions | |
284 | if (!$category->getProcessor()->canEditCategory()) { | |
285 | throw new PermissionDeniedException(); | |
286 | } | |
287 | ||
288 | $this->objects[$category->categoryID] = new $this->className($category); | |
289 | } | |
290 | ||
291 | foreach ($categoryIDs as $categoryID) { | |
292 | // validate category | |
293 | $category = CategoryHandler::getInstance()->getCategory($categoryID); | |
294 | if ($category === null) { | |
295 | throw new UserInputException('structure'); | |
296 | } | |
297 | ||
298 | // validate permissions | |
299 | if (!$category->getProcessor()->canEditCategory()) { | |
300 | throw new PermissionDeniedException(); | |
301 | } | |
302 | ||
303 | $this->objects[$category->categoryID] = new $this->className($category); | |
304 | } | |
305 | } | |
306 | } | |
13d8b49b | 307 | } |