--- /dev/null
+{capture assign='attributeTemplate'}
+ <section class="section">
+ <h2 class="sectionTitle"><span class="icon icon16 fa-times pointer jsDeleteButton jsTooltip" title="{lang}wcf.global.button.delete{/lang}"></span> <span>{lang __literal=true}wcf.acp.bbcode.numberedAttribute{/lang}</span></h2>
+
+ <dl>
+ <dt>
+ <label for="{@$field->getPrefixedId()}[{ldelim}@$attributeNumber}][attributeHtml]">{lang}wcf.acp.bbcode.attribute.attributeHtml{/lang}</label>
+ </dt>
+ <dd>
+ <input type="text" id="{@$field->getPrefixedId()}[{ldelim}@$attributeNumber}][attributeHtml]" name="{@$field->getPrefixedId()}[{ldelim}@$attributeNumber}][attributeHtml]" value="" class="long" maxlength="255">
+ </dd>
+ </dl>
+
+ <dl>
+ <dt>
+ <label for="{@$field->getPrefixedId()}[{ldelim}@$attributeNumber}][validationPattern]">{lang}wcf.acp.bbcode.attribute.validationPattern{/lang}</label>
+ </dt>
+ <dd>
+ <input type="text" id="{@$field->getPrefixedId()}[{ldelim}@$attributeNumber}][validationPattern]" name="{@$field->getPrefixedId()}[{ldelim}@$attributeNumber}][validationPattern]" value="" class="long" maxlength="255">
+ </dd>
+ </dl>
+
+ <dl>
+ <dt>
+ <label for="{@$field->getPrefixedId()}_required_{ldelim}@$attributeNumber}">{lang}wcf.acp.bbcode.attribute.required{/lang}</label>
+ </dt>
+ <dd>
+ <ol class="flexibleButtonGroup">
+ <li>
+ <input type="radio" {*
+ *}id="{@$field->getPrefixedId()}_required_{ldelim}@$attributeNumber}" {*
+ *}name="{@$field->getPrefixedId()}[{ldelim}@$attributeNumber}][required]" {*
+ *}value="1" {*
+ *}data-no-input-id="{@$field->getPrefixedId()}_required_{ldelim}@$attributeNumber}_no"{*
+ *}>
+ <label for="{@$field->getPrefixedId()}_required_{ldelim}@$attributeNumber}" class="green">
+ <span class="icon icon16 fa-check"></span> {lang}wcf.global.form.boolean.yes{/lang}
+ </label>
+ </li>
+ <li>
+ <input type="radio" {*
+ *}id="{@$field->getPrefixedId()}_required_{ldelim}@$attributeNumber}_no" {*
+ *}name="{@$field->getPrefixedId()}[{ldelim}@$attributeNumber}][required]" {*
+ *}value="0" {*
+ *}name="{@$field->getPrefixedId()}[{ldelim}@$attributeNumber}][required]" {*
+ *}checked{*
+ *}>
+ <label for="{@$field->getPrefixedId()}_required_{ldelim}@$attributeNumber}_no" class="red">
+ <span class="icon icon16 fa-times"></span> {lang}wcf.global.form.boolean.no{/lang}
+ </label>
+ </li>
+ </ol>
+ </dd>
+ </dl>
+
+ <dl>
+ <dt>
+ <label for="{@$field->getPrefixedId()}_useText_{ldelim}@$attributeNumber}">{lang}wcf.acp.bbcode.attribute.useText{/lang}</label>
+ </dt>
+ <dd>
+ <ol class="flexibleButtonGroup">
+ <li>
+ <input type="radio" {*
+ *}id="{@$field->getPrefixedId()}_useText_{ldelim}@$attributeNumber}" {*
+ *}name="{@$field->getPrefixedId()}[{ldelim}@$attributeNumber}][useText]" {*
+ *}value="1" {*
+ *}data-no-input-id="{@$field->getPrefixedId()}_useText_{ldelim}@$attributeNumber}_no"{*
+ *}>
+ <label for="{@$field->getPrefixedId()}_useText_{ldelim}@$attributeNumber}" class="green">
+ <span class="icon icon16 fa-check"></span> {lang}wcf.global.form.boolean.yes{/lang}
+ </label>
+ </li>
+ <li>
+ <input type="radio" {*
+ *}id="{@$field->getPrefixedId()}_useText_{ldelim}@$attributeNumber}_no" {*
+ *}name="{@$field->getPrefixedId()}[{ldelim}@$attributeNumber}][useText]" {*
+ *}value="0" {*
+ *}name="{@$field->getPrefixedId()}[{ldelim}@$attributeNumber}][useText]" {*
+ *}checked{*
+ *}>
+ <label for="{@$field->getPrefixedId()}_useText_{ldelim}@$attributeNumber}_no" class="red">
+ <span class="icon icon16 fa-times"></span> {lang}wcf.global.form.boolean.no{/lang}
+ </label>
+ </li>
+ </ol>
+ </dd>
+ </dl>
+
+ {event name='attributeFields'}
+ </section>
+{/capture}
+
+<script data-relocate="true">
+ require(['Dom/ChangeListener', 'Dom/Traverse', 'Dom/Util', 'WoltLabSuite/Core/Template'], function(DomChangeListener, DomTraverse, DomUtil, Template) {
+ var parentContainer = elById('{@$field->getParent()->getPrefixedId()}Container');
+
+ var parentTitle = DomTraverse.childBySel(parentContainer, 'h2.sectionTitle');
+ parentTitle.innerHTML = '<span class="icon icon16 fa-plus pointer jsTooltip" id="{@$field->getPrefixedId()}AddAttribute" title="{lang}wcf.global.button.add{/lang}"></span> ' + parentTitle.innerHTML;
+
+ DomChangeListener.trigger();
+
+ var addDeleteButtonListeners = function() {
+ var deleteButtonCallback = function(event) {
+ elRemove(event.currentTarget.closest('section'));
+ };
+
+ elBySelAll('.jsDeleteButton', parentContainer, function(deleteButton) {
+ deleteButton.classList.remove('jsDeleteButton');
+ deleteButton.addEventListener('click', deleteButtonCallback);
+ });
+ };
+ addDeleteButtonListeners();
+
+ var attributeNumber = {if $field->getValue()|empty}0{else}{$field->getValue()|count}{/if};
+ var attributeTemplate = new Template('{@$attributeTemplate|encodeJS}');
+
+ elById('{@$field->getPrefixedId()}AddAttribute').addEventListener('click', function(event) {
+ var html = attributeTemplate.fetch({ attributeNumber: attributeNumber++ });
+
+ DomUtil.insertHtml(html, parentContainer, 'append');
+
+ addDeleteButtonListeners();
+
+ DomChangeListener.trigger();
+ });
+ });
+</script>
+
+{foreach from=$field->getValue() key=attributeNumber item=attributeData name=bbcodeAttributes}
+ <section class="section">
+ <h2 class="sectionTitle"><span class="icon icon16 fa-times pointer jsDeleteButton jsTooltip" title="{lang}wcf.global.button.delete{/lang}"></span> <span>{lang}wcf.acp.bbcode.numberedAttribute{/lang}</span></h2>
+
+ <dl>
+ <dt>
+ <label for="{@$field->getPrefixedId()}[{@$attributeNumber}][attributeHtml]">{lang}wcf.acp.bbcode.attribute.attributeHtml{/lang}</label>
+ </dt>
+ <dd>
+ <input type="text" name="{@$field->getPrefixedId()}[{@$attributeNumber}][attributeHtml]" value="{if $attributeData[attributeHtml]|isset}{$attributeData[attributeHtml]}{/if}" class="long" maxlength="255">
+ </dd>
+ </dl>
+
+ {assign var='__attributeValidationError' value=null}
+ {foreach from=$field->getValidationErrors() item=validationError}
+ {if $validationError->getType() === $field->getPrefixedId()|concat:'_validationPattern_':$attributeNumber}
+ {assign var='__attributeValidationError' value=$validationError}
+ {/if}
+ {/foreach}
+ <dl{if $__attributeValidationError !== null} class="formError"{/if}>
+ <dt>
+ <label for="{@$field->getPrefixedId()}[{@$attributeNumber}][validationPattern]">{lang}wcf.acp.bbcode.attribute.validationPattern{/lang}</label>
+ </dt>
+ <dd>
+ <input type="text" name="{@$field->getPrefixedId()}[{@$attributeNumber}][validationPattern]" value="{if $attributeData[validationPattern]|isset}{$attributeData[validationPattern]}{/if}" class="long" maxlength="255">
+ {if $__attributeValidationError !== null}
+ <small class="innerError">{@$__attributeValidationError->getMessage()}</small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl>
+ <dt>
+ <label for="{@$field->getPrefixedId()}_required_{@$attributeNumber}">{lang}wcf.acp.bbcode.attribute.required{/lang}</label>
+ </dt>
+ <dd>
+ <ol class="flexibleButtonGroup">
+ <li>
+ <input type="radio" {*
+ *}id="{@$field->getPrefixedId()}_required_{@$attributeNumber}" {*
+ *}name="{@$field->getPrefixedId()}[{@$attributeNumber}][required]" {*
+ *}value="1" {*
+ *}data-no-input-id="{@$field->getPrefixedId()}_required_{@$attributeNumber}_no"{*
+ *}{if !$attributeData[required]|empty} checked{/if}{*
+ *}>
+ <label for="{@$field->getPrefixedId()}_required_{@$attributeNumber}" class="green">
+ <span class="icon icon16 fa-check"></span> {lang}wcf.global.form.boolean.yes{/lang}
+ </label>
+ </li>
+ <li>
+ <input type="radio" {*
+ *}id="{@$field->getPrefixedId()}_required_{@$attributeNumber}_no" {*
+ *}name="{@$field->getPrefixedId()}[{@$attributeNumber}][required]" {*
+ *}value="0" {*
+ *}name="{@$field->getPrefixedId()}[{@$attributeNumber}][required]"{*
+ *}{if $attributeData[required]|empty} checked{/if}{*
+ *}>
+ <label for="{@$field->getPrefixedId()}_required_{@$attributeNumber}_no" class="red">
+ <span class="icon icon16 fa-times"></span> {lang}wcf.global.form.boolean.no{/lang}
+ </label>
+ </li>
+ </ol>
+ </dd>
+ </dl>
+
+ <dl>
+ <dt>
+ <label for="{@$field->getPrefixedId()}_useText_{@$attributeNumber}">{lang}wcf.acp.bbcode.attribute.useText{/lang}</label>
+ </dt>
+ <dd>
+ <ol class="flexibleButtonGroup">
+ <li>
+ <input type="radio" {*
+ *}id="{@$field->getPrefixedId()}_useText_{@$attributeNumber}" {*
+ *}name="{@$field->getPrefixedId()}[{@$attributeNumber}][useText]" {*
+ *}value="1" {*
+ *}data-no-input-id="{@$field->getPrefixedId()}_useText_{@$attributeNumber}_no"{*
+ *}{if !$attributeData[useText]|empty} checked{/if}{*
+ *}>
+ <label for="{@$field->getPrefixedId()}_useText_{@$attributeNumber}" class="green">
+ <span class="icon icon16 fa-check"></span> {lang}wcf.global.form.boolean.yes{/lang}
+ </label>
+ </li>
+ <li>
+ <input type="radio" {*
+ *}id="{@$field->getPrefixedId()}_useText_{@$attributeNumber}_no" {*
+ *}name="{@$field->getPrefixedId()}[{@$attributeNumber}][useText]" {*
+ *}value="0" {*
+ *}name="{@$field->getPrefixedId()}[{@$attributeNumber}][useText]"{*
+ *}{if $attributeData[useText]|empty} checked{/if}{*
+ *}>
+ <label for="{@$field->getPrefixedId()}_useText_{@$attributeNumber}_no" class="red">
+ <span class="icon icon16 fa-times"></span> {lang}wcf.global.form.boolean.no{/lang}
+ </label>
+ </li>
+ </ol>
+ <small>{lang}wcf.acp.bbcode.attribute.useText.description{/lang}</small>
+ </dd>
+ </dl>
+
+ {event name='attributeFields'}
+ </section>
+{/foreach}
* @see WoltLabSuite/Core/Form/Builder/Field/Dependency/Container/Default#checkContainer
*/
checkContainer: function() {
+ if (elDataBool(this._container, 'ignore-dependencies')) {
+ return;
+ }
+
// only consider containers that have not been hidden by their own dependencies
if (DependencyManager.isHiddenByDependencies(this._container)) {
return;
--- /dev/null
+<?php
+namespace wcf\system\form\builder\field\bbcode;
+use wcf\system\form\builder\field\AbstractFormField;
+use wcf\system\form\builder\field\TDefaultIdFormField;
+use wcf\system\form\builder\field\validation\FormFieldValidationError;
+use wcf\system\Regex;
+
+/**
+ * Implementation of a form field for the attributes of a form field.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Form\Builder\Field\BBCode
+ * @since 3.2
+ */
+class BBCodeAttributesFormField extends AbstractFormField {
+ use TDefaultIdFormField;
+
+ /**
+ * @inheritDoc
+ */
+ protected $templateName = '__bbcodeAttributesFormField';
+
+ /**
+ * @inheritDoc
+ */
+ protected $__value = [];
+
+ /**
+ * @inheritDoc
+ */
+ protected static function getDefaultId() {
+ return 'attributes';
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function readValue() {
+ if ($this->getDocument()->hasRequestData($this->getPrefixedId()) && is_array($this->getDocument()->getRequestData($this->getPrefixedId()))) {
+ $this->__value = $this->getDocument()->getRequestData($this->getPrefixedId());
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validate() {
+ foreach ($this->getValue() as $attributeNumber => $attributeData) {
+ if (!empty($attributeData['attributeHtml']) && mb_strlen($attributeData['attributeHtml']) > 255) {
+ $this->addValidationError(
+ new FormFieldValidationError(
+ $this->getPrefixedId() . '_attributeHtml_' . $attributeNumber,
+ 'wcf.form.field.text.error.maximumLength',
+ [
+ 'length' => mb_strlen($attributeData['attributeHtml']),
+ 'maximumLength' => 255
+ ]
+ )
+ );
+ }
+ if (!empty($attributeData['validationPattern'])) {
+ if (mb_strlen($attributeData['validationPattern']) > 255) {
+ $this->addValidationError(
+ new FormFieldValidationError(
+ $this->getPrefixedId() . '_validationPattern_' . $attributeNumber,
+ 'wcf.form.field.text.error.maximumLength',
+ [
+ 'length' => mb_strlen($attributeData['validationPattern']),
+ 'maximumLength' => 255
+ ]
+ )
+ );
+ }
+ else if (!Regex::compile($attributeData['validationPattern'])->isValid()) {
+ $this->addValidationError(
+ new FormFieldValidationError(
+ $this->getPrefixedId() . '_validationPattern_' . $attributeNumber,
+ 'wcf.acp.bbcode.attribute.validationPattern.error.invalid'
+ )
+ );
+ }
+ }
+ }
+ }
+}
use wcf\data\bbcode\attribute\BBCodeAttributeEditor;
use wcf\data\bbcode\BBCode;
use wcf\data\bbcode\BBCodeEditor;
+use wcf\data\bbcode\BBCodeList;
use wcf\data\package\PackageCache;
+use wcf\system\bbcode\IBBCode;
use wcf\system\database\util\PreparedStatementConditionBuilder;
-use wcf\system\devtools\pip\IIdempotentPackageInstallationPlugin;
+use wcf\system\devtools\pip\IDevtoolsPipEntryList;
+use wcf\system\devtools\pip\IGuiPackageInstallationPlugin;
+use wcf\system\devtools\pip\TXmlGuiPackageInstallationPlugin;
use wcf\system\exception\SystemException;
+use wcf\system\form\builder\container\FormContainer;
+use wcf\system\form\builder\field\bbcode\BBCodeAttributesFormField;
+use wcf\system\form\builder\field\BooleanFormField;
+use wcf\system\form\builder\field\ClassNameFormField;
+use wcf\system\form\builder\field\data\VoidFormFieldDataProcessor;
+use wcf\system\form\builder\field\dependency\NonEmptyFormFieldDependency;
+use wcf\system\form\builder\field\dependency\ValueFormFieldDependency;
+use wcf\system\form\builder\field\IconFormField;
+use wcf\system\form\builder\field\RadioButtonFormField;
+use wcf\system\form\builder\field\TextFormField;
+use wcf\system\form\builder\field\validation\FormFieldValidationError;
+use wcf\system\form\builder\field\validation\FormFieldValidator;
+use wcf\system\form\builder\field\validation\FormFieldValidatorUtil;
+use wcf\system\form\builder\IFormDocument;
use wcf\system\WCF;
use wcf\util\StringUtil;
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @package WoltLabSuite\Core\Acp\Package\Plugin
*/
-class BBCodePackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements IIdempotentPackageInstallationPlugin {
+class BBCodePackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements IGuiPackageInstallationPlugin {
+ use TXmlGuiPackageInstallationPlugin;
+
/**
* @inheritDoc
*/
if (!empty($this->attributes)) {
foreach ($this->attributes as $bbcodeID => $bbcodeAttributes) {
+ if ($bbcodeID != intval($bbcodeID)) {
+ $bbcodeID = BBCode::getBBCodeByTag($bbcodeID)->bbcodeID;
+ }
+
foreach ($bbcodeAttributes as $attributeNo => $attribute) {
BBCodeAttributeEditor::create([
'bbcodeID' => $bbcodeID,
/**
* @inheritDoc
+ * @since 3.1
*/
public static function getSyncDependencies() {
return [];
}
+
+ /**
+ * @inheritDoc
+ * @since 3.2
+ */
+ protected function getElementData(\DOMElement $element, $saveData = false) {
+ $data = [
+ 'attributes' => [],
+ 'bbcodeTag' => $element->getAttribute('name'),
+ 'packageID' => $this->installation->getPackage()->packageID,
+ 'originIsSystem' => 1
+ ];
+
+ $optionalElements = [
+ 'buttonLabel' => 'buttonlabel',
+ 'className' => 'classname',
+ 'htmlClose' => 'htmlclose',
+ 'htmlOpen' => 'htmlopen',
+ 'isBlockElement' => 'isBlockElement',
+ 'isSourceCode' => 'sourcecode',
+ 'wysiwygIcon' => 'wysiwygicon'
+ ];
+ foreach ($optionalElements as $arrayKey => $elementName) {
+ $child = $element->getElementsByTagName($elementName)->item(0);
+ if ($child !== null) {
+ $data[$arrayKey] = $child->nodeValue;
+ }
+ }
+
+ if (!empty($data['wysiwygIcon']) && !empty($data['buttonLabel'])) {
+ $data['showButton'] = 1;
+ }
+
+ // attributes
+ $attributes = $element->getElementsByTagName('attributes')->item(0);
+ if ($attributes !== null) {
+ $optionalAttributeElements = [
+ 'attributeHtml' => 'html',
+ 'required' => 'required',
+ 'useText' => 'usetext',
+ 'validationPattern' => 'validationpattern',
+ ];
+
+ /** @var \DOMElement $attribute */
+ foreach ($attributes->childNodes as $attribute) {
+ $attributeData = ['name' => $attribute->nodeValue];
+
+ foreach ($optionalAttributeElements as $arrayKey => $elementName) {
+ $child = $attribute->getElementsByTagName($elementName)->item(0);
+ if ($child !== null) {
+ $attributeData[$arrayKey] = $child->nodeValue;
+ }
+ }
+
+ $data['attributes'][] = $attributeData;
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * @inheritDoc
+ * @since 3.2
+ */
+ public function getElementIdentifier(\DOMElement $element) {
+ return $element->getAttribute('name');
+ }
+
+ /**
+ * @inheritDoc
+ * @since 3.2
+ */
+ public function addFormFields(IFormDocument $form) {
+ /** @var FormContainer $dataContainer */
+ $dataContainer = $form->getNodeById('data');
+
+ // shared validators
+ $htmlValidator = new FormFieldValidator('format', function(TextFormField $formField) {
+ $value = $formField->getValue();
+ if ($value !== null && $value !== '') {
+ if (in_array(substr($formField->getValue(), 0, 1), ['<', '>'])) {
+ $formField->addValidationError(
+ new FormFieldValidationError(
+ 'leadingBracket',
+ 'wcf.acp.pip.bbcode.htmlOpen.error.leadingBracket'
+ )
+ );
+ }
+ else if (in_array(substr($formField->getValue(), -1, 1), ['<', '>'])) {
+ $formField->addValidationError(
+ new FormFieldValidationError(
+ 'trailingBracket',
+ 'wcf.acp.pip.bbcode.htmlOpen.error.trailingBracket'
+ )
+ );
+ }
+ }
+ });
+
+ // add fields
+ $dataContainer->appendChildren([
+ TextFormField::create('bbcodeTag')
+ ->objectProperty('name')
+ ->label('wcf.acp.pip.bbcode.bbcodeTag')
+ ->description('wcf.acp.pip.bbcode.bbcodeTag.description')
+ ->required()
+ ->maximumLength(191)
+ ->addValidator(FormFieldValidatorUtil::getRegularExpressionValidator(
+ '[a-z0-9]+',
+ 'wcf.acp.pip.bbcode.bbcodeTag'
+ ))
+ ->addValidator(new FormFieldValidator('allNone', function(TextFormField $formField) {
+ if ($formField->getValue() === 'all' || $formField->getValue() === 'none') {
+ $formField->addValidationError(
+ new FormFieldValidationError(
+ 'allNone',
+ 'wcf.acp.pip.bbcode.bbcodeTag.error.allNone'
+ )
+ );
+ }
+ }))
+ ->addValidator(new FormFieldValidator('uniqueness', function(TextFormField $formField) {
+ if (
+ $formField->getDocument()->getFormMode() === IFormDocument::FORM_MODE_CREATE ||
+ $this->editedEntry->getAttribute('name') !== $formField->getValue()
+ ) {
+ $providerList = new BBCodeList();
+ $providerList->getConditionBuilder()->add('bbcodeTag = ?', [$formField->getValue()]);
+
+ if ($providerList->countObjects() > 0) {
+ $formField->addValidationError(
+ new FormFieldValidationError(
+ 'notUnique',
+ 'wcf.acp.pip.bbcode.bbcodeTag.error.notUnique'
+ )
+ );
+ }
+ }
+ })),
+
+ TextFormField::create('htmlOpen')
+ ->objectProperty('htmlopen')
+ ->label('wcf.acp.pip.bbcode.htmlOpen')
+ ->description('wcf.acp.pip.bbcode.htmlOpen.description')
+ ->maximumLength(255)
+ ->addValidator($htmlValidator),
+
+ TextFormField::create('htmlClose')
+ ->objectProperty('htmlclose')
+ ->label('wcf.acp.pip.bbcode.htmlClose')
+ ->description('wcf.acp.pip.bbcode.htmlClose.description')
+ ->maximumLength(255)
+ // note: the language items for the opening tag are reused
+ ->addValidator($htmlValidator),
+
+ BooleanFormField::create('isBlockElement')
+ ->label('wcf.acp.pip.bbcode.isBlockElement')
+ ->description('wcf.acp.pip.bbcode.isBlockElement.description'),
+
+ BooleanFormField::create('isSourceCode')
+ ->objectProperty('sourcecode')
+ ->label('wcf.acp.pip.bbcode.isSourceCode')
+ ->description('wcf.acp.pip.bbcode.isSourceCode.description'),
+
+ ClassNameFormField::create()
+ ->objectProperty('classname')
+ ->implementedInterface(IBBCode::class),
+
+ BooleanFormField::create('showButton')
+ ->objectProperty('showbutton')
+ ->label('wcf.acp.pip.bbcode.showButton')
+ ->description('wcf.acp.pip.bbcode.showButton.description'),
+
+ TextFormField::create('buttonLabel')
+ ->objectProperty('buttonlabel')
+ ->label('wcf.acp.pip.bbcode.buttonLabel')
+ ->description('wcf.acp.pip.bbcode.buttonLabel.description')
+ ->required()
+ ->maximumLength(255),
+
+ RadioButtonFormField::create('iconType')
+ ->label('wcf.acp.pip.bbcode.iconType')
+ ->options([
+ 'filePath' => 'wcf.acp.pip.bbcode.iconType.filePath',
+ 'fontAwesome' => 'wcf.acp.pip.bbcode.iconType.fontAwesome',
+ ])
+ ->required()
+ ->value('fontAwesome'),
+
+ TextFormField::create('iconPath')
+ ->objectProperty('wysiwygicon')
+ ->label('wcf.acp.pip.bbcode.iconPath')
+ ->description('wcf.acp.pip.bbcode.iconPath.description')
+ ->required()
+ ->maximumLength(255)
+ ->addValidator(new FormFieldValidator('fileExists', function(TextFormField $formField) {
+ if (!file_exists(WCF_DIR . 'icon/' . $formField->getValue())) {
+ $formField->addValidationError(
+ new FormFieldValidationError(
+ 'fileDoesNotExist',
+ 'wcf.acp.pip.bbcode.iconPath.error.fileDoesNotExist'
+ )
+ );
+ }
+ })),
+
+ IconFormField::create('fontAwesomeIcon')
+ ->objectProperty('wysiwygicon')
+ ->label('wcf.acp.pip.bbcode.wysiwygIcon')
+ ->required()
+ ]);
+
+ $form->appendChild(
+ FormContainer::create('bbcodeAttributes')
+ ->attribute('data-ignore-dependencies', 1)
+ ->label('wcf.acp.pip.bbcode.attributes')
+ ->appendChild(
+ BBCodeAttributesFormField::create()
+ )
+ );
+
+ // discard the `iconType` value as it is only used to distinguish the two icon input fields
+ $form->getDataHandler()->add(new VoidFormFieldDataProcessor('iconType'));
+
+ // add dependencies
+ /** @var BooleanFormField $showButton */
+ $showButton = $dataContainer->getNodeById('showButton');
+ $iconType = $dataContainer->getNodeById('iconType');
+
+ $dataContainer->getNodeById('buttonLabel')->addDependency(
+ NonEmptyFormFieldDependency::create('showButton')
+ ->field($showButton)
+ );
+ $iconType->addDependency(
+ NonEmptyFormFieldDependency::create('showButton')
+ ->field($showButton)
+ );
+ $dataContainer->getNodeById('iconPath')->addDependency(
+ ValueFormFieldDependency::create('iconType')
+ ->field($iconType)
+ ->values(['filePath'])
+ );
+ $dataContainer->getNodeById('fontAwesomeIcon')->addDependency(
+ ValueFormFieldDependency::create('iconType')
+ ->field($iconType)
+ ->values(['fontAwesome'])
+ );
+ }
+
+ /**
+ * @inheritDoc
+ * @since 3.2
+ */
+ protected function setEntryListKeys(IDevtoolsPipEntryList $entryList) {
+ $entryList->setKeys([
+ 'bbcodeTag' => 'wcf.acp.pip.bbcode.bbcodeTag',
+ 'htmlOpen' => 'wcf.acp.pip.bbcode.htmlOpen',
+ 'className' => 'wcf.form.field.className'
+ ]);
+ }
+
+ /**
+ * @inheritDoc
+ * @since 3.2
+ */
+ protected function sortDocument(\DOMDocument $document) {
+ $this->sortImportDelete($document);
+
+ $compareFunction = static::getSortFunction([
+ [
+ 'isAttribute' => 1,
+ 'name' => 'name'
+ ]
+ ]);
+
+ $this->sortChildNodes($document->getElementsByTagName('import'), $compareFunction);
+ $this->sortChildNodes($document->getElementsByTagName('delete'), $compareFunction);
+ }
+
+ /**
+ * @inheritDoc
+ * @since 3.2
+ */
+ protected function writeEntry(\DOMDocument $document, IFormDocument $form) {
+ $data = $form->getData()['data'];
+
+ $bbcode = $document->createElement($this->tagName);
+ $bbcode->setAttribute('name', $data['name']);
+
+ $fields = [
+ 'classname' => '',
+ 'htmlclose' => '',
+ 'htmlopen' => '',
+ 'isBlockElement' => 0,
+ 'sourcecode' => 0,
+ 'buttonlabel' => '',
+ 'wysiwygicon' => ''
+ ];
+
+ foreach ($fields as $field => $defaultValue) {
+ if (isset($data[$field]) && $data[$field] !== $defaultValue) {
+ $bbcode->appendChild($document->createElement($field, (string) $data[$field]));
+ }
+ }
+
+ if (!empty($data['attributes'])) {
+ $attributes = $document->createElement('attributes');
+ $bbcode->appendChild($attributes);
+
+ foreach ($data['attributes'] as $attributeNumber => $attributeData) {
+ $attribute = $document->createElement('attribute');
+ $attribute->setAttribute('name', (string) $attributeNumber);
+
+ if (!empty($attributeData['attributeHtml'])) {
+ $attribute->appendChild($document->createElement('html', $attributeData['attributeHtml']));
+ }
+ if (!empty($attributeData['validationPattern'])) {
+ $attribute->appendChild($document->createElement('validationpattern', $attributeData['validationPattern']));
+ }
+ if (!empty($attributeData['required'])) {
+ $attribute->appendChild($document->createElement('required', $attributeData['required']));
+ }
+ if (!empty($attributeData['useText'])) {
+ $attribute->appendChild($document->createElement('usetext', $attributeData['useText']));
+ }
+
+ $attributes->appendChild($attribute);
+ }
+ }
+
+ $document->getElementsByTagName('import')->item(0)->appendChild($bbcode);
+
+ return $bbcode;
+ }
}
<item name="wcf.acp.bbcode.add"><![CDATA[Add BBCode]]></item>
<item name="wcf.acp.bbcode.add.userGroupOptionInfo"><![CDATA[Newly created BBCodes will be accessible for everyone. You can restrict usage by editing the specific user group permissions.]]></item>
<item name="wcf.acp.bbcode.attribute"><![CDATA[Attribute]]></item>
+ <item name="wcf.acp.bbcode.numberedAttribute"><![CDATA[Attribute {#$attributeNumber}]]></item>
<item name="wcf.acp.bbcode.attribute.attributeHtml"><![CDATA[Attribute HTML code]]></item>
<item name="wcf.acp.bbcode.attribute.validationPattern"><![CDATA[Regular Expression for Validation]]></item>
<item name="wcf.acp.bbcode.attribute.validationPattern.error.invalid"><![CDATA[The regular expression failed to validate.]]></item>
<item name="wcf.acp.pip.abstractOption.options.optionType.integer.suffix.description"><![CDATA[When setting the option value, the entered suffix (using the language item <code>wcf.acp.option.suffix.{literal}{$suffix}{/literal}</code>) is shown behind the input field.]]></item>
<item name="wcf.acp.pip.abstractOption.options.optionType.text.minLength"><![CDATA[Minimum Length]]></item>
<item name="wcf.acp.pip.abstractOption.options.optionType.text.maxLength"><![CDATA[Maximum Length]]></item>
+ <item name="wcf.acp.pip.bbcode.bbcodeTag"><![CDATA[BBCode Identifier]]></item>
+ <item name="wcf.acp.pip.bbcode.bbcodeTag.description"><![CDATA[The textual BBCode identifier is used to identify BBCodes, for example when updating BBCodes. The identifier may only contain lowercase letters and numbers.]]></item>
+ <item name="wcf.acp.pip.bbcode.bbcodeTag.error.allNone"><![CDATA[The identifier may neither be <code>all</code> nor <code>none</code>.]]></item>
+ <item name="wcf.acp.pip.bbcode.bbcodeTag.error.format"><![CDATA[The identifier may only contain lowercase letters and numbers.]]></item>
+ <item name="wcf.acp.pip.bbcode.bbcodeTag.error.notUnique"><![CDATA[The entered identifier is already used by another BBCode.]]></item>
+ <item name="wcf.acp.pip.bbcode.htmlOpen"><![CDATA[Opening HTML tag]]></item>
+ <item name="wcf.acp.pip.bbcode.htmlOpen.description"><![CDATA[Opening tag of the HTML element wrapping the BBCode’s content (without leading or trailing angle brackets).]]></item>
+ <item name="wcf.acp.pip.bbcode.htmlOpen.error.leadingBracket"><![CDATA[The tag may not begin with an angle bracket.]]></item>
+ <item name="wcf.acp.pip.bbcode.htmlOpen.error.trailingBracket"><![CDATA[The tag may not end with an angle bracket.]]></item>
+ <item name="wcf.acp.pip.bbcode.htmlClose"><![CDATA[Closing HTML tag]]></item>
+ <item name="wcf.acp.pip.bbcode.htmlClose.description"><![CDATA[Closing tag of the HTML element wrapping the BBCode’s content (without leading or trailing angle brackets).]]></item>
+ <item name="wcf.acp.pip.bbcode.attributes"><![CDATA[Attributes]]></item>
+ <item name="wcf.acp.pip.bbcode.isBlockElement"><![CDATA[BBCode represents block element]]></item>
+ <item name="wcf.acp.pip.bbcode.isBlockElement.description"><![CDATA[Block elements can contain line breaks and paragraphs, and generally take up the entire available width. This option must not be selected for inline elements.]]></item>
+ <item name="wcf.acp.pip.bbcode.isSourceCode"><![CDATA[Content represents source code]]></item>
+ <item name="wcf.acp.pip.bbcode.isSourceCode.description"><![CDATA[The content of the BBCode will not be parsed.]]></item>
+ <item name="wcf.acp.pip.bbcode.showButton"><![CDATA[Display button in WYSIWYG editor]]></item>
+ <item name="wcf.acp.pip.bbcode.showButton.description"><![CDATA[The BBCode will be easily available by clicking on the associated button in the WYSIWYG editor toolbar.]]></item>
+ <item name="wcf.acp.pip.bbcode.buttonLabel"><![CDATA[Button Label]]></item>
+ <item name="wcf.acp.pip.bbcode.buttonLabel.description"><![CDATA[The label is shown when hovering over the button in the WYSIWYG editor toolbar.]]></item>
+ <item name="wcf.acp.pip.bbcode.iconType"><![CDATA[Button Icon Type]]></item>
+ <item name="wcf.acp.pip.bbcode.iconType.filePath"><![CDATA[Image]]></item>
+ <item name="wcf.acp.pip.bbcode.iconType.fontAwesome"><![CDATA[FontAwesome Icon]]></item>
+ <item name="wcf.acp.pip.bbcode.iconPath"><![CDATA[Path to icon file]]></item>
+ <item name="wcf.acp.pip.bbcode.iconPath.description"><![CDATA[The entered path has to be relative to <code>{'WCF_DIR'|constant}icon/</code>.]]></item>
+ <item name="wcf.acp.pip.bbcode.iconPath.error.fileDoesNotExist"><![CDATA[The entered file does not exist.]]></item>
+ <item name="wcf.acp.pip.bbcode.wysiwygIcon"><![CDATA[Icon]]></item>
</category>
<category name="wcf.acp.reactionType">
<item name="wcf.form.field.option.error.nonExistent"><![CDATA[The following options do not exist: {implode from=$options item=option}<code>{$option}</code>{/implode}.]]></item>
<item name="wcf.form.field.showOrder"><![CDATA[Position]]></item>
<item name="wcf.form.field.showOrder.firstPosition"><![CDATA[(first position)]]></item>
- <item name="wcf.form.field.text.error.maximumLength"><![CDATA[The entered text{if $language} for language “{$language}”{/if} contains {#$length} characters but it may not be longer than {#$maximumLength} characters.]]></item>
- <item name="wcf.form.field.text.error.minimumLength"><![CDATA[The entered text{if $language} for language “{$language}”{/if} only contains {#$length} characters but it may not be shorter than {#$minimumLength} characters.]]></item>
+ <item name="wcf.form.field.text.error.maximumLength"><![CDATA[The entered text{if $language|isset} for language “{$language}”{/if} contains {#$length} characters but it may not be longer than {#$maximumLength} characters.]]></item>
+ <item name="wcf.form.field.text.error.minimumLength"><![CDATA[The entered text{if $language|isset} for language “{$language}”{/if} only contains {#$length} characters but it may not be shorter than {#$minimumLength} characters.]]></item>
<item name="wcf.form.field.user.error.maxmimumMultiples"><![CDATA[You may not enter more than {#$maximumCount} users but {#$count} user{if $count != 1}s are{else} is{/if} given.]]></item>
<item name="wcf.form.field.user.error.minimumMultiples"><![CDATA[You have to entered at least {#$minimumCount} users but only {#$count} user{if $count != 1}s are{else} is{/if} given.]]></item>
<item name="wcf.form.field.user.error.nonExistent"><![CDATA[{if $nonExistentUsernames|isset}The following users do not exist: {implode from=$nonExistentUsernames item=nonExistentUsername}“{$nonExistentUsername}”{/implode}.{else}The entered user does not exists.{/if}]]></item>