Add EmailLogListPage
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / data / category / CategoryAction.class.php
CommitLineData
13d8b49b 1<?php
a9229942 2
13d8b49b 3namespace wcf\data\category;
a9229942 4
13d8b49b 5use wcf\data\AbstractDatabaseObjectAction;
cea5b918 6use wcf\data\ISortableAction;
a427a8c8 7use wcf\data\IToggleAction;
a6418d60 8use wcf\data\IToggleContainerAction;
a9229942 9use wcf\data\language\item\LanguageItemAction;
03ba10fa 10use wcf\data\TDatabaseObjectToggle;
13d8b49b 11use wcf\system\category\CategoryHandler;
9c3f3eb2 12use wcf\system\database\util\PreparedStatementConditionBuilder;
13d8b49b 13use wcf\system\exception\PermissionDeniedException;
fc3d134b 14use wcf\system\exception\SystemException;
3631f7bd 15use wcf\system\exception\UserInputException;
13d8b49b
MS
16use wcf\system\user\collapsible\content\UserCollapsibleContentHandler;
17use 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
31class 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}