Merge remote-tracking branch 'refs/remotes/origin/master' into next
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / package / plugin / MenuItemPackageInstallationPlugin.class.php
1 <?php
2 namespace wcf\system\package\plugin;
3 use wcf\data\menu\item\MenuItem;
4 use wcf\data\menu\item\MenuItemEditor;
5 use wcf\system\exception\SystemException;
6 use wcf\system\WCF;
7
8 /**
9 * Installs, updates and deletes menu items.
10 *
11 * @author Alexander Ebert
12 * @copyright 2001-2016 WoltLab GmbH
13 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
14 * @package WoltLabSuite\Core\Acp\Package\Plugin
15 * @since 3.0
16 */
17 class MenuItemPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin {
18 /**
19 * @inheritDoc
20 */
21 public $className = MenuItemEditor::class;
22
23 /**
24 * @inheritDoc
25 */
26 public $tagName = 'item';
27
28 /**
29 * @inheritDoc
30 */
31 protected function handleDelete(array $items) {
32 $sql = "DELETE FROM wcf".WCF_N."_menu_item
33 WHERE identifier = ?
34 AND packageID = ?";
35 $statement = WCF::getDB()->prepareStatement($sql);
36
37 WCF::getDB()->beginTransaction();
38 foreach ($items as $item) {
39 $statement->execute([
40 $item['attributes']['identifier'],
41 $this->installation->getPackageID()
42 ]);
43 }
44 WCF::getDB()->commitTransaction();
45 }
46
47 /**
48 * @inheritDoc
49 * @throws SystemException
50 */
51 protected function getElement(\DOMXPath $xpath, array &$elements, \DOMElement $element) {
52 $nodeValue = $element->nodeValue;
53
54 if ($element->tagName === 'title') {
55 if (empty($element->getAttribute('language'))) {
56 throw new SystemException("Missing required attribute 'language' for menu item '" . $element->parentNode->getAttribute('identifier') . "'");
57 }
58
59 // <title> can occur multiple times using the `language` attribute
60 if (!isset($elements['title'])) $elements['title'] = [];
61
62 $elements['title'][$element->getAttribute('language')] = $element->nodeValue;
63 }
64 else {
65 $elements[$element->tagName] = $nodeValue;
66 }
67 }
68
69 /**
70 * @inheritDoc
71 * @throws SystemException
72 */
73 protected function prepareImport(array $data) {
74 $menuID = null;
75 if (!empty($data['elements']['menu'])) {
76 $sql = "SELECT menuID
77 FROM wcf".WCF_N."_menu
78 WHERE identifier = ?";
79 $statement = WCF::getDB()->prepareStatement($sql, 1);
80 $statement->execute([$data['elements']['menu']]);
81 $row = $statement->fetchSingleRow();
82 if ($row === false) {
83 throw new SystemException("Unable to find menu '" . $data['elements']['menu'] . "' for menu item '" . $data['attributes']['identifier'] . "'");
84 }
85
86 $menuID = $row['menuID'];
87 }
88
89 $parentItemID = null;
90 if (!empty($data['elements']['parent'])) {
91 if ($menuID !== null) {
92 throw new SystemException("The menu item '" . $data['attributes']['identifier'] . "' can either have an associated menu or a parent menu item, but not both.");
93 }
94
95 $sql = "SELECT *
96 FROM wcf".WCF_N."_menu_item
97 WHERE identifier = ?";
98 $statement = WCF::getDB()->prepareStatement($sql, 1);
99 $statement->execute([$data['elements']['parent']]);
100
101 /** @var MenuItem|null $parent */
102 $parent = $statement->fetchObject(MenuItem::class);
103 if ($parent === null) {
104 throw new SystemException("Unable to find parent menu item '" . $data['elements']['parent'] . "' for menu item '" . $data['attributes']['identifier'] . "'");
105 }
106
107 $parentItemID = $parent->itemID;
108 $menuID = $parent->menuID;
109 }
110
111 if ($menuID === null && $parentItemID === null) {
112 throw new SystemException("The menu item '" . $data['attributes']['identifier'] . "' must either have an associated menu or a parent menu item.");
113 }
114
115 $pageID = null;
116 if (!empty($data['elements']['page'])) {
117 $sql = "SELECT pageID
118 FROM wcf".WCF_N."_page
119 WHERE identifier = ?";
120 $statement = WCF::getDB()->prepareStatement($sql, 1);
121 $statement->execute([$data['elements']['page']]);
122 $row = $statement->fetchSingleRow();
123 if ($row === false) {
124 throw new SystemException("Unable to find page '" . $data['elements']['page'] . "' for menu item '" . $data['attributes']['identifier'] . "'");
125 }
126
127 $pageID = $row['pageID'];
128 }
129
130 $externalURL = (!empty($data['elements']['externalURL'])) ? $data['elements']['externalURL'] : '';
131
132 if ($pageID === null && empty($externalURL)) {
133 throw new SystemException("The menu item '" . $data['attributes']['identifier'] . "' must either have an associated page or an external url set.");
134 }
135 else if ($pageID !== null && !empty($externalURL)) {
136 throw new SystemException("The menu item '" . $data['attributes']['identifier'] . "' can either have an associated page or an external url, but not both.");
137 }
138
139 return [
140 'externalURL' => $externalURL,
141 'identifier' => $data['attributes']['identifier'],
142 'menuID' => $menuID,
143 'originIsSystem' => 1,
144 'pageID' => $pageID,
145 'parentItemID' => $parentItemID,
146 'showOrder' => $this->getItemOrder($menuID, $parentItemID),
147 'title' => $this->getI18nValues($data['elements']['title'])
148 ];
149 }
150
151 /**
152 * @inheritDoc
153 */
154 protected function findExistingItem(array $data) {
155 $sql = "SELECT *
156 FROM wcf".WCF_N."_menu_item
157 WHERE identifier = ?
158 AND packageID = ?";
159 $parameters = [
160 $data['identifier'],
161 $this->installation->getPackageID()
162 ];
163
164 return [
165 'sql' => $sql,
166 'parameters' => $parameters
167 ];
168 }
169
170 /**
171 * @inheritDoc
172 */
173 protected function import(array $row, array $data) {
174 // updating menu items is not supported because all fields that could be modified
175 // would potentially overwrite changes made by the user
176 if (!empty($row)) {
177 return new MenuItem(null, $row);
178 }
179
180 return parent::import($row, $data);
181 }
182
183 /**
184 * Returns the show order for a new item that will append it to the current
185 * menu or parent item.
186 *
187 * @param integer $menuID
188 * @param integer $parentItemID
189 * @return integer
190 * @throws \wcf\system\database\DatabaseException
191 */
192 protected function getItemOrder($menuID, $parentItemID = null) {
193 $sql = "SELECT MAX(showOrder) AS showOrder
194 FROM wcf".WCF_N."_menu_item
195 WHERE " . ($parentItemID === null ? 'menuID' : 'parentItemID') . " = ?";
196 $statement = WCF::getDB()->prepareStatement($sql, 1);
197 $statement->execute([
198 $parentItemID === null ? $menuID : $parentItemID
199 ]);
200
201 $row = $statement->fetchSingleRow();
202
203 return (!$row['showOrder']) ? 1 : $row['showOrder'] + 1;
204 }
205 }