Commit | Line | Data |
---|---|---|
effc9389 AE |
1 | <?php |
2 | namespace wcf\system\package\plugin; | |
3 | use wcf\data\menu\item\MenuItem; | |
4 | use wcf\data\menu\item\MenuItemEditor; | |
2c3107d0 MS |
5 | use wcf\data\menu\item\MenuItemList; |
6 | use wcf\data\menu\item\MenuItemNode; | |
7 | use wcf\data\menu\Menu; | |
8 | use wcf\data\menu\MenuList; | |
9 | use wcf\data\page\PageNode; | |
10 | use wcf\data\page\PageNodeTree; | |
11 | use wcf\system\devtools\pip\IDevtoolsPipEntryList; | |
12 | use wcf\system\devtools\pip\IGuiPackageInstallationPlugin; | |
13 | use wcf\system\devtools\pip\TXmlGuiPackageInstallationPlugin; | |
effc9389 | 14 | use wcf\system\exception\SystemException; |
2c3107d0 MS |
15 | use wcf\system\form\builder\container\FormContainer; |
16 | use wcf\system\form\builder\field\dependency\ValueFormFieldDependency; | |
17 | use wcf\system\form\builder\field\IntegerFormField; | |
2c3107d0 MS |
18 | use wcf\system\form\builder\field\RadioButtonFormField; |
19 | use wcf\system\form\builder\field\SingleSelectionFormField; | |
20 | use wcf\system\form\builder\field\TextFormField; | |
21 | use wcf\system\form\builder\field\TitleFormField; | |
22 | use wcf\system\form\builder\field\validation\FormFieldValidationError; | |
23 | use wcf\system\form\builder\field\validation\FormFieldValidator; | |
ab116957 | 24 | use wcf\system\form\builder\field\validation\FormFieldValidatorUtil; |
2c3107d0 MS |
25 | use wcf\system\form\builder\IFormDocument; |
26 | use wcf\system\language\LanguageFactory; | |
effc9389 AE |
27 | use wcf\system\WCF; |
28 | ||
29 | /** | |
30 | * Installs, updates and deletes menu items. | |
31 | * | |
2c3107d0 | 32 | * @author Alexander Ebert, Matthias Schmidt |
c839bd49 | 33 | * @copyright 2001-2018 WoltLab GmbH |
effc9389 | 34 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
e71525e4 MW |
35 | * @package WoltLabSuite\Core\Acp\Package\Plugin |
36 | * @since 3.0 | |
effc9389 | 37 | */ |
2c3107d0 MS |
38 | class MenuItemPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements IGuiPackageInstallationPlugin { |
39 | use TXmlGuiPackageInstallationPlugin; | |
40 | ||
effc9389 AE |
41 | /** |
42 | * @inheritDoc | |
43 | */ | |
44 | public $className = MenuItemEditor::class; | |
45 | ||
46 | /** | |
47 | * @inheritDoc | |
48 | */ | |
49 | public $tagName = 'item'; | |
50 | ||
51 | /** | |
52 | * @inheritDoc | |
53 | */ | |
54 | protected function handleDelete(array $items) { | |
893aace3 MS |
55 | $sql = "DELETE FROM wcf".WCF_N."_menu_item |
56 | WHERE identifier = ? | |
effc9389 AE |
57 | AND packageID = ?"; |
58 | $statement = WCF::getDB()->prepareStatement($sql); | |
59 | ||
60 | WCF::getDB()->beginTransaction(); | |
61 | foreach ($items as $item) { | |
62 | $statement->execute([ | |
63 | $item['attributes']['identifier'], | |
64 | $this->installation->getPackageID() | |
65 | ]); | |
66 | } | |
67 | WCF::getDB()->commitTransaction(); | |
68 | } | |
69 | ||
70 | /** | |
71 | * @inheritDoc | |
893aace3 | 72 | * @throws SystemException |
effc9389 AE |
73 | */ |
74 | protected function getElement(\DOMXPath $xpath, array &$elements, \DOMElement $element) { | |
75 | $nodeValue = $element->nodeValue; | |
76 | ||
77 | if ($element->tagName === 'title') { | |
78 | if (empty($element->getAttribute('language'))) { | |
79 | throw new SystemException("Missing required attribute 'language' for menu item '" . $element->parentNode->getAttribute('identifier') . "'"); | |
80 | } | |
81 | ||
82 | // <title> can occur multiple times using the `language` attribute | |
83 | if (!isset($elements['title'])) $elements['title'] = []; | |
84 | ||
85 | $elements['title'][$element->getAttribute('language')] = $element->nodeValue; | |
86 | } | |
87 | else { | |
88 | $elements[$element->tagName] = $nodeValue; | |
89 | } | |
90 | } | |
91 | ||
92 | /** | |
93 | * @inheritDoc | |
893aace3 | 94 | * @throws SystemException |
effc9389 AE |
95 | */ |
96 | protected function prepareImport(array $data) { | |
97 | $menuID = null; | |
98 | if (!empty($data['elements']['menu'])) { | |
2c3107d0 MS |
99 | $menuID = $this->getMenuID($data['elements']['menu']); |
100 | ||
101 | if ($menuID === null) { | |
effc9389 AE |
102 | throw new SystemException("Unable to find menu '" . $data['elements']['menu'] . "' for menu item '" . $data['attributes']['identifier'] . "'"); |
103 | } | |
effc9389 AE |
104 | } |
105 | ||
106 | $parentItemID = null; | |
107 | if (!empty($data['elements']['parent'])) { | |
108 | if ($menuID !== null) { | |
109 | throw new SystemException("The menu item '" . $data['attributes']['identifier'] . "' can either have an associated menu or a parent menu item, but not both."); | |
110 | } | |
111 | ||
893aace3 MS |
112 | $sql = "SELECT * |
113 | FROM wcf".WCF_N."_menu_item | |
114 | WHERE identifier = ?"; | |
effc9389 AE |
115 | $statement = WCF::getDB()->prepareStatement($sql, 1); |
116 | $statement->execute([$data['elements']['parent']]); | |
a13849b8 MS |
117 | |
118 | /** @var MenuItem|null $parent */ | |
effc9389 AE |
119 | $parent = $statement->fetchObject(MenuItem::class); |
120 | if ($parent === null) { | |
121 | throw new SystemException("Unable to find parent menu item '" . $data['elements']['parent'] . "' for menu item '" . $data['attributes']['identifier'] . "'"); | |
122 | } | |
123 | ||
124 | $parentItemID = $parent->itemID; | |
125 | $menuID = $parent->menuID; | |
126 | } | |
127 | ||
128 | if ($menuID === null && $parentItemID === null) { | |
129 | throw new SystemException("The menu item '" . $data['attributes']['identifier'] . "' must either have an associated menu or a parent menu item."); | |
130 | } | |
131 | ||
132 | $pageID = null; | |
133 | if (!empty($data['elements']['page'])) { | |
2c3107d0 MS |
134 | $pageID = $this->getPageID($data['elements']['page']); |
135 | ||
136 | if ($pageID === null) { | |
effc9389 AE |
137 | throw new SystemException("Unable to find page '" . $data['elements']['page'] . "' for menu item '" . $data['attributes']['identifier'] . "'"); |
138 | } | |
effc9389 AE |
139 | } |
140 | ||
86dbae33 | 141 | $externalURL = (!empty($data['elements']['externalURL'])) ? $data['elements']['externalURL'] : ''; |
effc9389 AE |
142 | |
143 | if ($pageID === null && empty($externalURL)) { | |
144 | throw new SystemException("The menu item '" . $data['attributes']['identifier'] . "' must either have an associated page or an external url set."); | |
145 | } | |
c1e54023 | 146 | else if ($pageID !== null && !empty($externalURL)) { |
effc9389 AE |
147 | throw new SystemException("The menu item '" . $data['attributes']['identifier'] . "' can either have an associated page or an external url, but not both."); |
148 | } | |
149 | ||
150 | return [ | |
151 | 'externalURL' => $externalURL, | |
152 | 'identifier' => $data['attributes']['identifier'], | |
153 | 'menuID' => $menuID, | |
154 | 'originIsSystem' => 1, | |
155 | 'pageID' => $pageID, | |
156 | 'parentItemID' => $parentItemID, | |
157 | 'showOrder' => $this->getItemOrder($menuID, $parentItemID), | |
158 | 'title' => $this->getI18nValues($data['elements']['title']) | |
159 | ]; | |
160 | } | |
161 | ||
2c3107d0 MS |
162 | /** |
163 | * Returns the id of the menu with the given identifier. If no such menu | |
164 | * exists, `null` is returned. | |
165 | * | |
166 | * @param string $identifier | |
167 | * @return null|int | |
168 | */ | |
ef6d8b4f | 169 | protected function getMenuID($identifier) { |
2c3107d0 MS |
170 | $sql = "SELECT menuID |
171 | FROM wcf" . WCF_N . "_menu | |
172 | WHERE identifier = ?"; | |
173 | $statement = WCF::getDB()->prepareStatement($sql, 1); | |
174 | $statement->execute([$identifier]); | |
175 | ||
176 | return $statement->fetchSingleColumn(); | |
177 | } | |
178 | ||
179 | /** | |
180 | * Returns the id of the page with the given identifier. If no such page | |
181 | * exists, `null` is returned. | |
182 | * | |
183 | * @param string $identifier | |
184 | * @return null|int | |
185 | */ | |
ef6d8b4f | 186 | protected function getPageID($identifier) { |
2c3107d0 MS |
187 | $sql = "SELECT pageID |
188 | FROM wcf" . WCF_N . "_page | |
189 | WHERE identifier = ?"; | |
190 | $statement = WCF::getDB()->prepareStatement($sql, 1); | |
191 | $statement->execute([$identifier]); | |
192 | ||
193 | return $statement->fetchSingleColumn(); | |
194 | } | |
195 | ||
effc9389 AE |
196 | /** |
197 | * @inheritDoc | |
198 | */ | |
199 | protected function findExistingItem(array $data) { | |
200 | $sql = "SELECT * | |
201 | FROM wcf".WCF_N."_menu_item | |
202 | WHERE identifier = ? | |
203 | AND packageID = ?"; | |
058cbd6a | 204 | $parameters = [ |
effc9389 AE |
205 | $data['identifier'], |
206 | $this->installation->getPackageID() | |
058cbd6a | 207 | ]; |
effc9389 | 208 | |
058cbd6a | 209 | return [ |
effc9389 AE |
210 | 'sql' => $sql, |
211 | 'parameters' => $parameters | |
058cbd6a | 212 | ]; |
effc9389 AE |
213 | } |
214 | ||
215 | /** | |
216 | * @inheritDoc | |
217 | */ | |
218 | protected function import(array $row, array $data) { | |
219 | // updating menu items is not supported because all fields that could be modified | |
220 | // would potentially overwrite changes made by the user | |
221 | if (!empty($row)) { | |
222 | return new MenuItem(null, $row); | |
223 | } | |
224 | ||
225 | return parent::import($row, $data); | |
226 | } | |
227 | ||
228 | /** | |
229 | * Returns the show order for a new item that will append it to the current | |
230 | * menu or parent item. | |
231 | * | |
893aace3 MS |
232 | * @param integer $menuID |
233 | * @param integer $parentItemID | |
234 | * @return integer | |
effc9389 AE |
235 | */ |
236 | protected function getItemOrder($menuID, $parentItemID = null) { | |
893aace3 MS |
237 | $sql = "SELECT MAX(showOrder) AS showOrder |
238 | FROM wcf".WCF_N."_menu_item | |
239 | WHERE " . ($parentItemID === null ? 'menuID' : 'parentItemID') . " = ?"; | |
effc9389 AE |
240 | $statement = WCF::getDB()->prepareStatement($sql, 1); |
241 | $statement->execute([ | |
63b9817b | 242 | $parentItemID === null ? $menuID : $parentItemID |
effc9389 AE |
243 | ]); |
244 | ||
245 | $row = $statement->fetchSingleRow(); | |
246 | ||
247 | return (!$row['showOrder']) ? 1 : $row['showOrder'] + 1; | |
248 | } | |
d4955651 AE |
249 | |
250 | /** | |
251 | * @inheritDoc | |
2c3107d0 | 252 | * @since 3.1 |
d4955651 AE |
253 | */ |
254 | public static function getSyncDependencies() { | |
0e86b76b | 255 | return ['language', 'menu', 'page']; |
d4955651 | 256 | } |
2c3107d0 MS |
257 | |
258 | /** | |
259 | * @inheritDoc | |
260 | * @since 3.2 | |
261 | */ | |
add83c7c | 262 | protected function addFormFields(IFormDocument $form) { |
2c3107d0 MS |
263 | $menuList = new MenuList(); |
264 | $menuList->readObjects(); | |
265 | ||
266 | /** @var FormContainer $dataContainer */ | |
267 | $dataContainer = $form->getNodeById('data'); | |
268 | ||
269 | $dataContainer->appendChildren([ | |
270 | TextFormField::create('identifier') | |
271 | ->label('wcf.acp.pip.menuItem.identifier') | |
272 | ->description('wcf.acp.pip.menuItem.identifier.description') | |
273 | ->required() | |
ab116957 | 274 | ->addValidator(FormFieldValidatorUtil::getDotSeparatedStringValidator( |
2c3107d0 MS |
275 | 'wcf.acp.pip.menuItem.identifier', |
276 | 4 | |
277 | )) | |
278 | ->addValidator(new FormFieldValidator('uniqueness', function(TextFormField $formField) { | |
279 | if ( | |
280 | $formField->getDocument()->getFormMode() === IFormDocument::FORM_MODE_CREATE || | |
281 | $this->editedEntry->getAttribute('identifier') !== $formField->getValue() | |
282 | ) { | |
283 | $menuItemList = new MenuItemList(); | |
284 | $menuItemList->getConditionBuilder()->add('identifier = ?', [$formField->getValue()]); | |
285 | ||
286 | if ($menuItemList->countObjects() > 0) { | |
287 | $formField->addValidationError( | |
288 | new FormFieldValidationError( | |
289 | 'notUnique', | |
290 | 'wcf.acp.pip.menuItem.identifier.error.notUnique' | |
291 | ) | |
292 | ); | |
293 | } | |
294 | } | |
295 | })), | |
296 | ||
297 | TitleFormField::create() | |
298 | ->required() | |
299 | ->i18n() | |
300 | ->i18nRequired() | |
301 | ->languageItemPattern('__NONE__'), | |
302 | ||
303 | SingleSelectionFormField::create('menu') | |
304 | ->label('wcf.acp.pip.menuItem.menu') | |
305 | ->description('wcf.acp.pip.menuItem.menu.description') | |
306 | ->required() | |
bf42c0ad | 307 | ->options(function() use ($menuList) { |
2c3107d0 MS |
308 | $options = []; |
309 | foreach ($menuList as $menu) { | |
310 | $options[$menu->identifier] = $menu->identifier; | |
311 | } | |
312 | ||
313 | asort($options); | |
314 | ||
315 | return $options; | |
316 | }), | |
317 | ||
318 | RadioButtonFormField::create('linkType') | |
319 | ->label('wcf.acp.pip.menuItem.linkType') | |
320 | ->required() | |
321 | ->options([ | |
322 | 'internal' => 'wcf.acp.pip.menuItem.linkType.internal', | |
323 | 'external' => 'wcf.acp.pip.menuItem.linkType.external' | |
324 | ]) | |
325 | ->value('internal'), | |
326 | ||
327 | SingleSelectionFormField::create('menuItemPage') | |
328 | ->objectProperty('page') | |
329 | ->label('wcf.acp.pip.menuItem.page') | |
330 | ->description('wcf.acp.pip.menuItem.page.description') | |
331 | ->required() | |
332 | ->filterable() | |
bf42c0ad | 333 | ->options(function() { |
2c3107d0 MS |
334 | $pageNodeList = (new PageNodeTree())->getNodeList(); |
335 | ||
2c3107d0 MS |
336 | $packageIDs = array_merge( |
337 | [$this->installation->getPackage()->packageID], | |
338 | array_keys($this->installation->getPackage()->getAllRequiredPackages()) | |
339 | ); | |
340 | ||
18fa3745 MS |
341 | $nestedOptions = []; |
342 | ||
2c3107d0 MS |
343 | /** @var PageNode $pageNode */ |
344 | foreach ($pageNodeList as $pageNode) { | |
345 | if (in_array($pageNode->packageID, $packageIDs)) { | |
346 | $nestedOptions[] = [ | |
347 | 'depth' => $pageNode->getDepth() - 1, | |
348 | 'label' => $pageNode->name, | |
349 | 'value' => $pageNode->identifier | |
350 | ]; | |
351 | } | |
352 | } | |
353 | ||
354 | return $nestedOptions; | |
355 | }, true), | |
356 | ||
357 | TextFormField::create('externalURL') | |
358 | ->label('wcf.acp.pip.menuItem.externalURL') | |
359 | ->description('wcf.acp.pip.menuItem.externalURL.description') | |
360 | ->required() | |
361 | ->i18n(), | |
362 | ||
363 | IntegerFormField::create('showOrder') | |
364 | ->objectProperty('showorder') | |
365 | ->label('wcf.acp.pip.menuItem.showOrder') | |
366 | ->description('wcf.acp.pip.menuItem.showOrder.description') | |
367 | ->objectProperty('showorder') | |
368 | ->minimum(1) | |
c5b55690 | 369 | ->nullable() |
2c3107d0 MS |
370 | ]); |
371 | ||
372 | /** @var SingleSelectionFormField $menuField */ | |
373 | $menuField = $form->getNodeById('menu'); | |
374 | ||
375 | foreach ($menuList as $menu) { | |
376 | $dataContainer->insertBefore( | |
377 | SingleSelectionFormField::create('parentMenuItem' . $menu->menuID) | |
378 | ->objectProperty('parent') | |
379 | ->label('wcf.acp.pip.menuItem.parentMenuItem') | |
bf42c0ad | 380 | ->options(function() use($menu) { |
2c3107d0 MS |
381 | $options = [[ |
382 | 'depth' => 0, | |
383 | 'label' => 'wcf.global.noSelection', | |
384 | 'value' => '' | |
385 | ]]; | |
386 | ||
387 | $packageIDs = array_merge( | |
388 | [$this->installation->getPackage()->packageID], | |
389 | array_keys($this->installation->getPackage()->getAllRequiredPackages()) | |
390 | ); | |
391 | ||
392 | /** @var MenuItemNode $menuItem */ | |
393 | foreach ($menu->getMenuItemNodeList() as $menuItem) { | |
394 | if (in_array($menuItem->packageID, $packageIDs)) { | |
395 | $options[] = [ | |
396 | 'depth' => $menuItem->getDepth() - 1, | |
397 | 'label' => $menuItem->identifier, | |
398 | 'value' => $menuItem->identifier | |
399 | ]; | |
400 | } | |
401 | } | |
402 | ||
403 | if (count($options) === 1) { | |
404 | return []; | |
405 | } | |
406 | ||
407 | return $options; | |
408 | }, true) | |
409 | ->addDependency( | |
410 | ValueFormFieldDependency::create('menu') | |
411 | ->field($menuField) | |
412 | ->values([$menu->identifier]) | |
413 | ), | |
414 | 'linkType' | |
415 | ); | |
416 | } | |
417 | ||
418 | // dependencies | |
419 | ||
420 | /** @var RadioButtonFormField $linkType */ | |
421 | $linkType = $form->getNodeById('linkType'); | |
422 | $form->getNodeById('menuItemPage')->addDependency( | |
423 | ValueFormFieldDependency::create('linkType') | |
424 | ->field($linkType) | |
425 | ->values(['internal']) | |
426 | ); | |
427 | ||
428 | $form->getNodeById('externalURL')->addDependency( | |
429 | ValueFormFieldDependency::create('linkType') | |
430 | ->field($linkType) | |
431 | ->values(['external']) | |
432 | ); | |
433 | } | |
434 | ||
435 | /** | |
436 | * @inheritDoc | |
437 | * @since 3.2 | |
438 | */ | |
add83c7c | 439 | protected function doGetElementData(\DOMElement $element, $saveData) { |
2c3107d0 MS |
440 | $data = [ |
441 | 'identifier' => $element->getAttribute('identifier'), | |
442 | 'packageID' => $this->installation->getPackageID(), | |
443 | 'originIsSystem' => 1, | |
444 | 'title' => [] | |
445 | ]; | |
446 | ||
447 | /** @var \DOMElement $title */ | |
448 | foreach ($element->getElementsByTagName('title') as $title) { | |
449 | $data['title'][LanguageFactory::getInstance()->getLanguageByCode($title->getAttribute('language'))->languageID] = $title->nodeValue; | |
450 | } | |
451 | ||
452 | foreach (['externalURL', 'menu', 'page', 'parent', 'showOrder'] as $optionalElementName) { | |
453 | $optionalElement = $element->getElementsByTagName($optionalElementName)->item(0); | |
454 | if ($optionalElement !== null) { | |
455 | $data[$optionalElementName] = $optionalElement->nodeValue; | |
456 | } | |
457 | } | |
458 | ||
459 | if (isset($data['parent'])) { | |
460 | $menuItemList = new MenuItemList(); | |
461 | $menuItemList->getConditionBuilder()->add('identifier = ?', [$data['parent']]); | |
462 | $menuItemList->getConditionBuilder()->add('packageID IN (?)', [ | |
463 | array_merge( | |
464 | [$this->installation->getPackage()->packageID], | |
465 | array_keys($this->installation->getPackage()->getAllRequiredPackages()) | |
466 | ) | |
467 | ]); | |
468 | $menuItemList->readObjects(); | |
469 | ||
470 | if (count($menuItemList) === 1) { | |
471 | if ($saveData) { | |
472 | $data['menuID'] = $menuItemList->current()->menuID; | |
473 | } | |
474 | else { | |
475 | $data['menu'] = (new Menu($menuItemList->current()->menuID))->identifier; | |
476 | } | |
477 | } | |
478 | } | |
479 | else if (isset($data['menu']) && $saveData) { | |
480 | $data['menuID'] = $this->getMenuID($data['menu']); | |
481 | unset($data['menu']); | |
482 | } | |
483 | ||
484 | if ($saveData) { | |
485 | // updating menu items is not supported thus handling the title | |
486 | // array causes issues | |
487 | if ($this->editedEntry !== null) { | |
488 | unset($data['title']); | |
489 | } | |
490 | else { | |
491 | $titles = []; | |
492 | foreach ($data['title'] as $languageID => $title) { | |
493 | $titles[LanguageFactory::getInstance()->getLanguage($languageID)->languageCode] = $title; | |
494 | } | |
495 | ||
496 | $data['title'] = $titles; | |
497 | } | |
498 | ||
499 | if (isset($data['page'])) { | |
500 | $data['pageID'] = $this->getPageID($data['page']); | |
501 | unset($data['page']); | |
502 | } | |
503 | } | |
504 | ||
505 | return $data; | |
506 | } | |
507 | ||
508 | /** | |
509 | * @inheritDoc | |
510 | * @since 3.2 | |
511 | */ | |
bf42c0ad | 512 | public function getElementIdentifier(\DOMElement $element) { |
2c3107d0 MS |
513 | return $element->getAttribute('identifier'); |
514 | } | |
515 | ||
516 | /** | |
517 | * @inheritDoc | |
518 | * @since 3.2 | |
519 | */ | |
520 | protected function setEntryListKeys(IDevtoolsPipEntryList $entryList) { | |
521 | $entryList->setKeys([ | |
522 | 'identifier' => 'wcf.acp.pip.menuItem.identifier', | |
523 | 'menu' => 'wcf.acp.pip.menuItem.menu' | |
524 | ]); | |
525 | } | |
526 | ||
527 | /** | |
528 | * @inheritDoc | |
529 | * @since 3.2 | |
530 | */ | |
add83c7c | 531 | protected function doCreateXmlElement(\DOMDocument $document, IFormDocument $form) { |
2c3107d0 MS |
532 | $formData = $form->getData(); |
533 | $data = $formData['data']; | |
534 | ||
92bcab67 | 535 | $menuItem = $document->createElement($this->tagName); |
c5b55690 | 536 | |
2c3107d0 MS |
537 | $menuItem->setAttribute('identifier', $data['identifier']); |
538 | ||
38d8fe8f | 539 | if (!empty($data['parent'])) { |
2c3107d0 MS |
540 | $menuItem->appendChild($document->createElement('parent', $data['parent'])); |
541 | } | |
38d8fe8f MS |
542 | else if (!empty($data['menu'])) { |
543 | $menuItem->appendChild($document->createElement('menu', $data['menu'])); | |
544 | } | |
2c3107d0 MS |
545 | |
546 | foreach ($formData['title_i18n'] as $languageID => $title) { | |
547 | $title = $document->createElement('title', $this->getAutoCdataValue($title)); | |
548 | $title->setAttribute('language', LanguageFactory::getInstance()->getLanguage($languageID)->languageCode); | |
549 | ||
550 | $menuItem->appendChild($title); | |
551 | } | |
552 | ||
c5b55690 MS |
553 | $this->appendElementChildren( |
554 | $menuItem, | |
555 | [ | |
556 | 'page' => '', | |
557 | 'externalURL' => '', | |
558 | 'showOrder' => null | |
559 | ], | |
560 | $form | |
561 | ); | |
2c3107d0 | 562 | |
2c3107d0 MS |
563 | return $menuItem; |
564 | } | |
effc9389 | 565 | } |