--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\DatabaseObject;
+use wcf\data\DatabaseObjectEditor;
+use wcf\form\AbstractForm;
+use wcf\system\exception\UserInputException;
+use wcf\system\language\I18nHandler;
+use wcf\system\language\I18nValue;
+use wcf\system\WCF;
+
+/**
+ * Default implementation for ACP forms with i18n support.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2017 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Acp\Form
+ * @since 3.1
+ */
+abstract class AbstractAcpForm extends AbstractForm {
+ /**
+ * action type
+ * @var string
+ */
+ public $action = 'add';
+
+ /**
+ * @var I18nValue[]
+ */
+ public $i18nValues = [];
+
+ /**
+ * Registers a new i18n value.
+ *
+ * @param I18nValue $value
+ */
+ public function registerI18nValue(I18nValue $value) {
+ $fieldName = $value->getFieldName();
+
+ if (isset($this->i18nValues[$fieldName])) {
+ throw new \InvalidArgumentException("Duplicate value definition for '{$fieldName}'.");
+ }
+ else if (!property_exists($this, $fieldName)) {
+ throw new \UnexpectedValueException("Implementing class does not expose the property '{$fieldName}'.");
+ }
+
+ $this->i18nValues[$fieldName] = $value;
+
+ I18nHandler::getInstance()->register($fieldName);
+ }
+
+ /**
+ * Retrieves an i18n value object.
+ *
+ * @param string $fieldName
+ * @return I18nValue|null
+ */
+ public function getI18nValue($fieldName) {
+ if (isset($this->i18nValues[$fieldName])) {
+ return $this->i18nValues[$fieldName];
+ }
+
+ return null;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function readFormParameters() {
+ parent::readFormParameters();
+
+ if (!empty($this->i18nValues)) {
+ I18nHandler::getInstance()->readValues();
+
+ foreach ($this->i18nValues as $fieldName => $value) {
+ if (I18nHandler::getInstance()->isPlainValue($fieldName)) {
+ $this->{$fieldName} = I18nHandler::getInstance()->getValue($fieldName);
+ }
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validate() {
+ parent::validate();
+
+ foreach ($this->i18nValues as $fieldName => $value) {
+ if (!I18nHandler::getInstance()->validateValue($fieldName, $value->getFlag(I18nValue::REQUIRE_I18N), $value->getFlag(I18nValue::ALLOW_EMPTY))) {
+ throw new UserInputException(
+ $fieldName,
+ (I18nHandler::getInstance()->isPlainValue($fieldName)) ? 'empty' : 'multilingual'
+ );
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function readDataI18n(DatabaseObject $databaseObject) {
+ if (empty($_POST) && !empty($this->i18nValues)) {
+ I18nHandler::getInstance()->readValues();
+
+ foreach ($this->i18nValues as $fieldName => $value) {
+ I18nHandler::getInstance()->setOptions(
+ $fieldName,
+ $value->getPackageID(),
+ $databaseObject->{$fieldName},
+ "{$value->getLanguageItem()}\d+"
+ );
+ }
+ }
+ }
+
+ public function beforeSaveI18n(DatabaseObject $databaseObject) {
+ $values = [];
+
+ foreach ($this->i18nValues as $fieldName => $value) {
+ $this->{$fieldName} = $value->getLanguageItem() . $databaseObject->getObjectID();
+ if (I18nHandler::getInstance()->isPlainValue($fieldName)) {
+ I18nHandler::getInstance()->remove($fieldName);
+
+ $values[$fieldName] = I18nHandler::getInstance()->getValue($fieldName);
+ $this->{$fieldName} = $values[$fieldName];
+ }
+ else {
+ I18nHandler::getInstance()->save(
+ $fieldName,
+ $this->{$fieldName},
+ $value->getLanguageCategory(),
+ $value->getPackageID()
+ );
+
+ $value[$fieldName] = I18nHandler::getInstance()->getValues($fieldName)[WCF::getLanguage()->languageID];
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function saveI18n(DatabaseObject $databaseObject, $editorClass) {
+ $data = [];
+
+ $objectID = $databaseObject->getObjectID();
+ foreach ($this->i18nValues as $fieldName => $value) {
+ if (!I18nHandler::getInstance()->isPlainValue($fieldName)) {
+ $languageItem = $value->getLanguageItem() . $objectID;
+ I18nHandler::getInstance()->save(
+ $fieldName,
+ $languageItem,
+ $value->getLanguageCategory(),
+ $value->getPackageID()
+ );
+
+ $data[$fieldName] = $languageItem;
+ }
+ }
+
+ if (!empty($data)) {
+ /** @var DatabaseObjectEditor $editor */
+ $editor = new $editorClass($databaseObject);
+ $editor->update($data);
+ }
+ }
+
+ /**
+ * Resets the form values and calls the saved event.
+ */
+ public function reset() {
+ $this->saved();
+
+ if (!empty($this->i18nValues)) {
+ foreach ($this->i18nValues as $fieldName => $value) {
+ $this->{$fieldName} = '';
+ }
+
+ I18nHandler::getInstance()->reset();
+ }
+
+ // show success message
+ WCF::getTPL()->assign('success', true);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ if (!empty($this->i18nValues)) {
+ $useRequestData = ($this->action === 'add') ? true : !empty($_POST);
+
+ I18nHandler::getInstance()->assignVariables($useRequestData);
+ }
+
+ WCF::getTPL()->assign([
+ 'action' => $this->action
+ ]);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\language;
+use wcf\data\package\PackageCache;
+
+/**
+ * Represents an i18n value for use with `AbstractAcpForm`.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2017 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Language
+ * @since 3.1
+ */
+class I18nValue {
+ /**
+ * field name
+ * @var string
+ */
+ protected $fieldName = '';
+
+ /**
+ * bit-mask to alter validation rules
+ * @var integer
+ */
+ protected $flags = 0;
+
+ /**
+ * language item template, placeholder or id will be appended
+ * @var string
+ */
+ protected $languageItem = '';
+
+ /**
+ * language item category
+ * @var string
+ */
+ protected $languageItemCategory = '';
+
+ /**
+ * package name used for the `packageID` reference
+ * @var string
+ */
+ protected $languageItemPackage = '';
+
+ /**
+ * allow an empty value, that includes providing no value at all
+ */
+ const ALLOW_EMPTY = 1;
+
+ /**
+ * require localized values, disallowing plain values
+ */
+ const REQUIRE_I18N = 2;
+
+ /**
+ * I18nValue constructor.
+ *
+ * @param string $fieldName
+ */
+ public function __construct($fieldName) {
+ $this->fieldName = $fieldName;
+ }
+
+ /**
+ * Sets the language item configuration.
+ *
+ * @param string $item
+ * @param string $category
+ * @param string $package
+ */
+ public function setLanguageItem($item, $category, $package) {
+ $this->languageItem = $item;
+ $this->languageItemCategory = $category;
+ $this->languageItemPackage = $package;
+ }
+
+ /**
+ * Sets bit flags.
+ *
+ * @param integer $flags
+ */
+ public function setFlags($flags) {
+ $this->flags = $flags;
+ }
+
+ /**
+ * Returns true if given flag is set.
+ *
+ * @param integer $flag
+ * @return boolean
+ */
+ public function getFlag($flag) {
+ return (($this->flags & $flag) === $flag);
+ }
+
+ /**
+ * Returns the field identifier.
+ *
+ * @return string
+ */
+ public function getFieldName() {
+ return $this->fieldName;
+ }
+
+ /**
+ * Returns the language item template.
+ *
+ * @return string
+ */
+ public function getLanguageItem() {
+ return $this->languageItem;
+ }
+
+ /**
+ * Returns the language category.
+ *
+ * @return string
+ */
+ public function getLanguageCategory() {
+ return $this->languageItemCategory;
+ }
+
+ /**
+ * Returns the package id.
+ *
+ * @return string
+ */
+ public function getPackageID() {
+ return PackageCache::getInstance()->getPackageID($this->languageItemPackage);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function __toString() {
+ return $this->getFieldName();
+ }
+}