2 namespace wcf\system\package\plugin
;
4 use wcf\data\box\BoxEditor
;
5 use wcf\data\box\BoxList
;
6 use wcf\data\
object\type\ObjectTypeCache
;
7 use wcf\data\page\PageNode
;
8 use wcf\data\page\PageNodeTree
;
9 use wcf\system\box\AbstractDatabaseObjectListBoxController
;
10 use wcf\system\database\util\PreparedStatementConditionBuilder
;
11 use wcf\system\devtools\pip\IDevtoolsPipEntryList
;
12 use wcf\system\devtools\pip\IGuiPackageInstallationPlugin
;
13 use wcf\system\devtools\pip\TXmlGuiPackageInstallationPlugin
;
14 use wcf\system\exception\SystemException
;
15 use wcf\system\form\builder\container\FormContainer
;
16 use wcf\system\form\builder\container\TabFormContainer
;
17 use wcf\system\form\builder\container\TabMenuFormContainer
;
18 use wcf\system\form\builder\field\BooleanFormField
;
19 use wcf\system\form\builder\field\dependency\ValueFormFieldDependency
;
20 use wcf\system\form\builder\field\ItemListFormField
;
21 use wcf\system\form\builder\field\MultilineTextFormField
;
22 use wcf\system\form\builder\field\MultipleSelectionFormField
;
23 use wcf\system\form\builder\field\RadioButtonFormField
;
24 use wcf\system\form\builder\field\SingleSelectionFormField
;
25 use wcf\system\form\builder\field\TextFormField
;
26 use wcf\system\form\builder\field\TitleFormField
;
27 use wcf\system\form\builder\field\validation\FormFieldValidationError
;
28 use wcf\system\form\builder\field\validation\FormFieldValidator
;
29 use wcf\system\form\builder\field\validation\FormFieldValidatorUtil
;
30 use wcf\system\form\builder\IFormDocument
;
31 use wcf\system\language\LanguageFactory
;
33 use wcf\util\StringUtil
;
36 * Installs, updates and deletes boxes.
38 * @author Alexander Ebert, Matthias Schmidt
39 * @copyright 2001-2018 WoltLab GmbH
40 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
41 * @package WoltLabSuite\Core\Acp\Package\Plugin
44 class BoxPackageInstallationPlugin
extends AbstractXMLPackageInstallationPlugin
implements IGuiPackageInstallationPlugin
{
45 use TXmlGuiPackageInstallationPlugin
;
50 public $className = BoxEditor
::class;
53 * list of created or updated boxes by id
56 protected $boxes = [];
62 protected $content = [];
65 * list of element names which are not considered as additional data
68 public static $reservedTags = ['boxType', 'content', 'cssClassName', 'name', 'objectType', 'position', 'showHeader', 'visibilityExceptions', 'visibleEverywhere'];
73 public $tagName = 'box';
76 * visibility exceptions per box
79 public $visibilityExceptions = [];
84 protected function handleDelete(array $items) {
85 $sql = "DELETE FROM wcf".WCF_N
."_box
88 $statement = WCF
::getDB()->prepareStatement($sql);
90 WCF
::getDB()->beginTransaction();
91 foreach ($items as $item) {
93 $item['attributes']['identifier'],
94 $this->installation
->getPackageID()
97 WCF
::getDB()->commitTransaction();
102 * @throws SystemException
104 protected function getElement(\DOMXPath
$xpath, array &$elements, \DOMElement
$element) {
105 $nodeValue = $element->nodeValue
;
107 if ($element->tagName
=== 'name') {
108 if (empty($element->getAttribute('language'))) {
109 throw new SystemException("Missing required attribute 'language' for '" . $element->tagName
. "' element (box '" . $element->parentNode
->getAttribute('identifier') . "')");
112 // element can occur multiple times using the `language` attribute
113 if (!isset($elements[$element->tagName
])) $elements[$element->tagName
] = [];
115 $elements[$element->tagName
][$element->getAttribute('language')] = $element->nodeValue
;
117 else if ($element->tagName
=== 'content') {
118 // content can occur multiple times using the `language` attribute
119 if (!isset($elements['content'])) $elements['content'] = [];
122 /** @var \DOMElement $child */
123 foreach ($xpath->query('child::*', $element) as $child) {
124 $children[$child->tagName
] = $child->nodeValue
;
127 if (empty($children['title'])) {
128 throw new SystemException("Expected non-empty child element 'title' for 'content' element (box '" . $element->parentNode
->getAttribute('identifier') . "')");
131 $elements['content'][$element->getAttribute('language')] = [
132 'content' => isset($children['content']) ?
$children['content'] : '',
133 'title' => $children['title']
136 else if ($element->tagName
=== 'visibilityExceptions') {
137 $elements['visibilityExceptions'] = [];
138 /** @var \DOMElement $child */
139 foreach ($xpath->query('child::*', $element) as $child) {
140 $elements['visibilityExceptions'][] = $child->nodeValue
;
144 $elements[$element->tagName
] = $nodeValue;
150 * @throws SystemException
152 protected function prepareImport(array $data) {
154 $boxType = $data['elements']['boxType'];
155 $objectTypeID = null;
156 $identifier = $data['attributes']['identifier'];
157 $isMultilingual = false;
158 $position = $data['elements']['position'];
160 if (!in_array($position, ['bottom', 'contentBottom', 'contentTop', 'footer', 'footerBoxes', 'headerBoxes', 'hero', 'sidebarLeft', 'sidebarRight', 'top'])) {
161 throw new SystemException("Unknown box position '{$position}' for box '{$identifier}'");
164 // pick the display name by choosing the default language, or 'en' or '' (empty string)
165 $defaultLanguageCode = LanguageFactory
::getInstance()->getDefaultLanguage()->getFixedLanguageCode();
166 if (isset($data['elements']['name'][$defaultLanguageCode])) {
167 // use the default language
168 $name = $data['elements']['name'][$defaultLanguageCode];
170 else if (isset($data['elements']['name']['en'])) {
171 // use the value for English
172 $name = $data['elements']['name']['en'];
175 // fallback to the display name without/empty language attribute
176 $name = $data['elements']['name'][''];
180 /** @noinspection PhpMissingBreakStatementInspection */
182 if (empty($data['elements']['objectType'])) {
183 throw new SystemException("Missing required element 'objectType' for 'system'-type box '{$identifier}'");
186 $sql = "SELECT objectTypeID
187 FROM wcf".WCF_N
."_object_type object_type
188 LEFT JOIN wcf".WCF_N
."_object_type_definition object_type_definition
189 ON (object_type_definition.definitionID = object_type.definitionID)
191 AND definitionName = ?";
192 $statement = WCF
::getDB()->prepareStatement($sql);
193 $statement->execute([$data['elements']['objectType'], 'com.woltlab.wcf.boxController']);
194 $objectTypeID = $statement->fetchSingleColumn();
195 if (!$objectTypeID) {
196 throw new SystemException("Unknown object type '{$data['elements']['objectType']}' for 'system'-type box '{$identifier}'");
199 $isMultilingual = true;
206 if (empty($data['elements']['content'])) {
207 if ($boxType === 'system') {
211 throw new SystemException("Missing required 'content' element(s) for box '{$identifier}'");
214 if (count($data['elements']['content']) === 1) {
215 if (!isset($data['elements']['content'][''])) {
216 throw new SystemException("Expected one 'content' element without a 'language' attribute for box '{$identifier}'");
220 $isMultilingual = true;
222 if (isset($data['elements']['content'][''])) {
223 throw new SystemException("Cannot mix 'content' elements with and without 'language' attribute for box '{$identifier}'");
227 $content = $data['elements']['content'];
232 throw new SystemException("Unknown type '{$boxType}' for box '{$identifier}");
236 if (!empty($data['elements']['visibilityExceptions'])) {
237 $this->visibilityExceptions
[$identifier] = $data['elements']['visibilityExceptions'];
240 $additionalData = [];
241 foreach ($data['elements'] as $tagName => $nodeValue) {
242 if (!in_array($tagName, self
::$reservedTags)) {
243 $additionalData[$tagName] = $nodeValue;
248 'identifier' => $identifier,
249 'content' => $content,
251 'boxType' => $boxType,
252 'position' => $position,
253 'showOrder' => $this->getItemOrder($position),
254 'visibleEverywhere' => (!empty($data['elements']['visibleEverywhere'])) ?
1 : 0,
255 'isMultilingual' => $isMultilingual ?
'1' : '0',
256 'cssClassName' => (!empty($data['elements']['cssClassName'])) ?
$data['elements']['cssClassName'] : '',
257 'showHeader' => (!empty($data['elements']['showHeader'])) ?
1 : 0,
258 'originIsSystem' => 1,
259 'objectTypeID' => $objectTypeID,
260 'additionalData' => serialize($additionalData)
267 protected function findExistingItem(array $data) {
269 FROM wcf".WCF_N
."_box
274 $this->installation
->getPackageID()
279 'parameters' => $parameters
284 * Returns the show order for a new item that will append it to the current
285 * menu or parent item.
287 * @param string $position box position
290 protected function getItemOrder($position) {
291 $sql = "SELECT MAX(showOrder) AS showOrder
292 FROM wcf".WCF_N
."_box
294 $statement = WCF
::getDB()->prepareStatement($sql, 1);
295 $statement->execute([$position]);
297 $row = $statement->fetchSingleRow();
299 return (!$row['showOrder']) ?
1 : $row['showOrder'] +
1;
305 protected function import(array $row, array $data) {
307 $content = $data['content'];
308 unset($data['content']);
310 // updating boxes is only supported for 'system' type boxes, all other
311 // types would potentially overwrite changes made by the user if updated
312 if (!empty($row) && $row['boxType'] !== 'system') {
313 $box = new Box(null, $row);
316 $box = parent
::import($row, $data);
319 // store content for later import
320 $this->content
[$box->boxID
] = $content;
321 $this->boxes
[$box->boxID
] = ($box instanceof Box
) ?
new BoxEditor($box) : $box;
329 protected function postImport() {
330 if (!empty($this->content
)) {
331 $sql = "SELECT COUNT(*) AS count
332 FROM wcf".WCF_N
."_box_content
334 AND languageID IS NULL";
335 $statement = WCF
::getDB()->prepareStatement($sql);
337 $sql = "INSERT IGNORE INTO wcf".WCF_N
."_box_content
338 (boxID, languageID, title, content)
339 VALUES (?, ?, ?, ?)";
340 $insertStatement = WCF
::getDB()->prepareStatement($sql);
342 WCF
::getDB()->beginTransaction();
343 foreach ($this->content
as $boxID => $contentData) {
344 $boxEditor = $this->boxes
[$boxID];
346 // expand non-i18n value
347 if ($boxEditor->boxType
=== 'system' && count($contentData) === 1 && isset($contentData[''])) {
348 foreach (LanguageFactory
::getInstance()->getLanguages() as $language) {
349 $insertStatement->execute([
351 $language->languageID
,
352 $contentData['']['title'],
360 foreach ($contentData as $languageCode => $content) {
362 if ($languageCode != '') {
363 $language = LanguageFactory
::getInstance()->getLanguageByCode($languageCode);
364 if ($language === null) continue;
366 $languageID = $language->languageID
;
369 if ($languageID === null) {
370 $statement->execute([$boxID]);
371 if ($statement->fetchSingleColumn()) continue;
374 $boxContent = isset($content['content']) ?
$content['content'] : '';
375 $insertStatement->execute([
382 if ($boxEditor->getDecoratedObject()->boxType
=== 'tpl') {
383 $boxEditor->writeTemplate($languageID, $boxContent);
387 WCF
::getDB()->commitTransaction();
390 if (empty($this->visibilityExceptions
)) return;
392 // get all boxes belonging to the identifiers
393 $conditions = new PreparedStatementConditionBuilder();
394 $conditions->add("identifier IN (?)", [array_keys($this->visibilityExceptions
)]);
395 $conditions->add("packageID = ?", [$this->installation
->getPackageID()]);
398 FROM wcf".WCF_N
."_box
400 $statement = WCF
::getDB()->prepareStatement($sql);
401 $statement->execute($conditions->getParameters());
403 /** @var Box[] $boxes */
404 $boxes = $statement->fetchObjects(Box
::class, 'identifier');
406 // save visibility exceptions
407 $sql = "DELETE FROM wcf".WCF_N
."_box_to_page
409 $deleteStatement = WCF
::getDB()->prepareStatement($sql);
410 $sql = "INSERT IGNORE wcf".WCF_N
."_box_to_page
411 (boxID, pageID, visible)
413 $insertStatement = WCF
::getDB()->prepareStatement($sql);
414 foreach ($this->visibilityExceptions
as $boxIdentifier => $pages) {
415 // delete old visibility exceptions
416 $deleteStatement->execute([$boxes[$boxIdentifier]->boxID
]);
419 $conditionBuilder = new PreparedStatementConditionBuilder();
420 $conditionBuilder->add('identifier IN (?)', [$pages]);
421 $sql = "SELECT pageID
422 FROM wcf".WCF_N
."_page
424 $statement = WCF
::getDB()->prepareStatement($sql);
425 $statement->execute($conditionBuilder->getParameters());
426 $pageIDs = $statement->fetchAll(\PDO
::FETCH_COLUMN
);
429 foreach ($pageIDs as $pageID) {
430 $insertStatement->execute([$boxes[$boxIdentifier]->boxID
, $pageID, $boxes[$boxIdentifier]->visibleEverywhere ?
0 : 1]);
439 public static function getSyncDependencies() {
440 return ['language', 'objectType'];
447 public function getAdditionalTemplateCode() {
448 return WCF
::getTPL()->fetch('__boxPipGui');
455 protected function addFormFields(IFormDocument
$form) {
456 $tabContainter = TabMenuFormContainer
::create('tabMenu');
457 $form->appendChild($tabContainter);
459 $dataTab = TabFormContainer
::create('dataTab')
460 ->label('wcf.global.form.data');
461 $tabContainter->appendChild($dataTab);
462 $dataContainer = FormContainer
::create('dataTabData');
463 $dataTab->appendChild($dataContainer);
465 $contentTab = TabFormContainer
::create('contentTab')
466 ->label('wcf.acp.pip.box.content');
467 $tabContainter->appendChild($contentTab);
468 $contentContainer = FormContainer
::create('contentTabContent');
469 $contentTab->appendChild($contentContainer);
471 $dataContainer->appendChildren([
472 TextFormField
::create('identifier')
473 ->label('wcf.acp.pip.box.identifier')
474 ->description('wcf.acp.pip.box.identifier.description')
476 ->addValidator(FormFieldValidatorUtil
::getDotSeparatedStringValidator(
477 'wcf.acp.pip.box.identifier',
480 ->addValidator(new FormFieldValidator('uniqueness', function(TextFormField
$formField) {
482 $formField->getDocument()->getFormMode() === IFormDocument
::FORM_MODE_CREATE ||
483 $this->editedEntry
->getAttribute('identifier') !== $formField->getValue()
485 $pageList = new BoxList();
486 $pageList->getConditionBuilder()->add('identifier = ?', [$formField->getValue()]);
488 if ($pageList->countObjects() > 0) {
489 $formField->addValidationError(
490 new FormFieldValidationError(
492 'wcf.acp.pip.box.identifier.error.notUnique'
499 TextFormField
::create('name')
500 ->label('wcf.acp.pip.box.name')
501 ->description('wcf.acp.pip.box.name.description')
505 ->languageItemPattern('__NONE__'),
507 RadioButtonFormField
::create('boxType')
508 ->label('wcf.acp.pip.box.boxType')
509 ->description('wcf.acp.pip.box.boxType.description')
510 ->options(array_combine(Box
::$availableBoxTypes, Box
::$availableBoxTypes))
513 SingleSelectionFormField
::create('objectType')
514 ->label('wcf.acp.pip.box.objectType')
515 ->description('wcf.acp.pip.box.objectType.description')
517 ->options(function() {
518 $objectTypes = ObjectTypeCache
::getInstance()->getObjectTypes('com.woltlab.wcf.boxController');
521 foreach ($objectTypes as $objectType) {
522 $options[$objectType->objectType
] = $objectType->objectType
;
530 SingleSelectionFormField
::create('position')
531 ->label('wcf.acp.pip.box.position')
532 ->options(array_combine(Box
::$availablePositions, Box
::$availablePositions)),
534 BooleanFormField
::create('showHeader')
535 ->label('wcf.acp.pip.box.showHeader')
538 BooleanFormField
::create('visibleEverywhere')
539 ->label('wcf.acp.pip.box.visibleEverywhere')
542 MultipleSelectionFormField
::create('visibilityExceptions')
543 ->label('wcf.acp.pip.box.visibilityExceptions.hiddenEverywhere')
545 ->options(function() {
546 $pageNodeList = (new PageNodeTree())->getNodeList();
549 /** @var PageNode $pageNode */
550 foreach ($pageNodeList as $pageNode) {
552 'depth' => $pageNode->getDepth() - 1,
553 'label' => $pageNode->name
,
554 'value' => $pageNode->identifier
558 return $nestedOptions;
561 ItemListFormField
::create('cssClassName')
562 ->label('wcf.acp.pip.box.cssClassName')
563 ->description('wcf.acp.pip.box.cssClassName.description')
564 ->saveValueType(ItemListFormField
::SAVE_VALUE_TYPE_SSV
)
567 $contentContainer->appendChildren([
568 TitleFormField
::create('title')
569 ->label('wcf.acp.pip.box.content.title')
573 ->languageItemPattern('__NONE__'),
575 MultilineTextFormField
::create('contentContent')
576 ->objectProperty('content')
577 ->label('wcf.acp.pip.box.content.content')
581 ->languageItemPattern('__NONE__')
584 // add box controller-specific form fields
585 foreach (ObjectTypeCache
::getInstance()->getObjectTypes('com.woltlab.wcf.boxController') as $objectType) {
586 if (is_subclass_of($objectType->className
, AbstractDatabaseObjectListBoxController
::class)) {
587 /** @var AbstractDatabaseObjectListBoxController $boxController */
588 $boxController = new $objectType->className
;
590 $boxController->addPipGuiFormFields($form, $objectType->objectType
);
596 /** @var SingleSelectionFormField $boxType */
597 $boxType = $dataContainer->getNodeById('boxType');
599 $dataContainer->getNodeById('objectType')->addDependency(
600 ValueFormFieldDependency
::create('boxType')
605 $contentContainer->getNodeById('contentContent')->addDependency(
606 ValueFormFieldDependency
::create('pageType')
617 protected function doGetElementData(\DOMElement
$element, $saveData) {
619 'boxType' => $element->getElementsByTagName('boxType')->item(0)->nodeValue
,
621 'identifier' => $element->getAttribute('identifier'),
623 'originIsSystem' => 1,
624 'packageID' => $this->installation
->getPackageID(),
625 'position' => $element->getElementsByTagName('position')->item(0)->nodeValue
,
629 /** @var \DOMElement $name */
630 foreach ($element->getElementsByTagName('name') as $name) {
631 $data['name'][LanguageFactory
::getInstance()->getLanguageByCode($name->getAttribute('language'))->languageID
] = $name->nodeValue
;
634 /** @var \DOMElement $content */
635 foreach ($element->getElementsByTagName('content') as $content) {
636 if ($content->parentNode
=== $element) {
637 $languageID = LanguageFactory
::getInstance()->getLanguageByCode($content->getAttribute('language'))->languageID
;
639 $contentContent = $content->getElementsByTagName('content')->item(0);
640 if ($contentContent !== null) {
641 $data['content'][$languageID] = $contentContent->nodeValue
;
644 $title = $content->getElementsByTagName('title')->item(0);
645 if ($title !== null) {
646 $data['title'][$languageID] = $title->nodeValue
;
651 foreach (['objectType', 'cssClassName', 'showHeader', 'visibleEverywhere'] as $optionalElementName) {
652 $optionalElement = $element->getElementsByTagName($optionalElementName)->item(0);
653 if ($optionalElement !== null) {
654 $data[$optionalElementName] = $optionalElement->nodeValue
;
658 $visibilityExceptions = $element->getElementsByTagName('visibilityExceptions')->item(0);
659 if ($visibilityExceptions !== null) {
661 /** @var \DOMElement $page */
662 foreach ($visibilityExceptions->getElementsByTagName('page') as $page) {
663 $exceptions[] = $page->nodeValue
;
666 if (!empty($exceptions)) {
667 $data['visibilityExceptions'] = $exceptions;
671 $objectTypeData = null;
672 if (isset($data['objectType'])) {
673 $objectType = ObjectTypeCache
::getInstance()->getObjectTypeByName('com.woltlab.wcf.boxController', $data['objectType']);
674 if ($objectType !== null && is_subclass_of($objectType->className
, AbstractDatabaseObjectListBoxController
::class)) {
675 /** @var AbstractDatabaseObjectListBoxController $boxController */
676 $boxController = new $objectType->className
;
678 $objectTypeData = $boxController->getPipGuiElementData($element, $saveData);
682 if ($objectTypeData !== null) {
684 $data['additionalData'] = serialize($objectTypeData);
687 $data = array_merge($objectTypeData, $data);
692 $defaultLanguageID = LanguageFactory
::getInstance()->getDefaultLanguage()->languageID
;
693 $englishLanguage = LanguageFactory
::getInstance()->getLanguageByCode('en');
695 if (isset($data['name'][$defaultLanguageID])) {
696 // use the default language
697 $name = $data['name'][$defaultLanguageID];
699 else if ($englishLanguage !== null && isset($data['name'][$englishLanguage->languageID
])) {
700 // use the value for English
701 $name = $data['name'][$englishLanguage->languageID
];
704 // fallback to first element
705 $name = reset($data['name']);
708 $data['name'] = $name;
710 if (isset($data['objectType'])) {
711 $objectType = $data['objectType'];
712 unset($data['objectType']);
714 $data['objectTypeID'] = ObjectTypeCache
::getInstance()->getObjectTypeByName('com.woltlab.wcf.boxController', $objectType)->objectTypeID
;
717 if (isset($data['visibilityExceptions'])) {
718 $this->visibilityExceptions
[$data['identifier']] = $data['visibilityExceptions'];
719 unset($data['visibilityExceptions']);
723 if (isset($data['content'])) {
724 $content['content'] = $data['content'];
725 unset($data['content']);
727 if (isset($data['title'])) {
728 $content['title'] = $data['title'];
729 unset($data['title']);
732 if (!empty($content)) {
733 $data['content'] = $content;
744 public function getElementIdentifier(\DOMElement
$element) {
745 return $element->getAttribute('identifier');
752 protected function setEntryListKeys(IDevtoolsPipEntryList
$entryList) {
753 $entryList->setKeys([
754 'identifier' => 'wcf.acp.pip.box.identifier',
755 'boxType' => 'wcf.acp.pip.box.boxType'
763 protected function doCreateXmlElement(\DOMDocument
$document, IFormDocument
$form) {
764 $formData = $form->getData();
765 $data = $formData['data'];
767 if ($data['identifier'] === 'com.woltlab.wcf.MainMenu') {
768 $data['boxPosition'] = 'mainMenu';
771 $box = $document->createElement($this->tagName
);
772 $box->setAttribute('identifier', $data['identifier']);
774 foreach ($formData['name_i18n'] as $languageID => $name) {
775 $nameElement = $document->createElement('name', $this->getAutoCdataValue($name));
776 $nameElement->setAttribute('language', LanguageFactory
::getInstance()->getLanguage($languageID)->languageCode
);
778 $box->appendChild($nameElement);
781 $box->appendChild($document->createElement('boxType', $data['boxType']));
782 $box->appendChild($document->createElement('position', $data['position']));
784 $this->appendElementChildren(
788 'cssClassName' => '',
794 if (!empty($data['visibilityExceptions'])) {
795 $box->appendChild($document->createElement('visibleEverywhere', (string)($data['visibleEverywhere'] ??
0)));
797 $visibilityExceptions = $document->createElement('visibilityExceptions');
799 sort($data['visibilityExceptions']);
800 foreach ($data['visibilityExceptions'] as $page) {
801 $visibilityExceptions->appendChild($document->createElement('page', $page));
804 $box->appendChild($visibilityExceptions);
806 else if (!empty($data['visibleEverywhere'])) {
807 $box->appendChild($document->createElement('visibleEverywhere', (string)$data['visibleEverywhere']));
810 foreach (LanguageFactory
::getInstance()->getLanguages() as $language) {
813 foreach (['title', 'content'] as $property) {
814 if (!empty($formData[$property . '_i18n'][$language->languageID
])) {
815 if ($content === null) {
816 $content = $document->createElement('content');
817 $content->setAttribute('language', $language->languageCode
);
819 $box->appendChild($content);
822 if ($property === 'content') {
823 $contentContent = $document->createElement('content');
824 $contentContent->appendChild(
825 $document->createCDATASection(
826 StringUtil
::escapeCDATA(StringUtil
::unifyNewlines(
827 $formData[$property . '_i18n'][$language->languageID
]
832 $content->appendChild($contentContent);
835 $content->appendChild(
836 $document->createElement(
838 $formData[$property . '_i18n'][$language->languageID
]
846 if (isset($data['objectType'])) {
847 $objectType = ObjectTypeCache
::getInstance()->getObjectTypeByName('com.woltlab.wcf.boxController', $data['objectType']);
848 if ($objectType !== null && is_subclass_of($objectType->className
, AbstractDatabaseObjectListBoxController
::class)) {
849 /** @var AbstractDatabaseObjectListBoxController $boxController */
850 $boxController = new $objectType->className
;
852 $boxController->writePipGuiEntry($box, $form);