//<![CDATA[
$(function() {
WCF.TabMenu.init();
+
+ var $availableLanguages = { {implode from=$availableLanguages key=languageID item=languageName}{@$languageID}: '{$languageName}'{/implode} };
+ var $groupNameValues = { {implode from=$groupName_i18n key=languageID item=value}'{@$languageID}': '{$value}'{/implode} };
+ new WCF.MultipleLanguageInput('groupName', false, $groupNameValues, $availableLanguages);
});
//]]>
</script>
{if $additionalButtons[$group->groupID]|isset}{@$additionalButtons[$group->groupID]}{/if}
</td>
<td class="columnID columnGroupID"><p>{@$group->groupID}</p></td>
- <td class="columnTitle columnGroupName">{if $group->isEditable()}<p><a title="{lang}wcf.acp.group.edit{/lang}" href="index.php?form=UserGroupEdit&groupID={@$group->groupID}{@SID_ARG_2ND}">{$group->groupName}</a>{else}{$group->groupName}</p>{/if}</td>
+ <td class="columnTitle columnGroupName">{if $group->isEditable()}<p><a title="{lang}wcf.acp.group.edit{/lang}" href="index.php?form=UserGroupEdit&groupID={@$group->groupID}{@SID_ARG_2ND}">{lang}{$group->groupName}{/lang}</a>{else}{lang}{$group->groupName}{/lang}</p>{/if}</td>
<td class="columnDigits columnMembers"><p><a title="{lang}wcf.acp.group.showMembers{/lang}" href="index.php?form=UserSearch&groupID={@$group->groupID}{@SID_ARG_2ND}">{#$group->members}</p></a></td>
{if $additionalColumns[$group->groupID]|isset}{@$additionalColumns[$group->groupID]}{/if}
*/
wcfIsset: function(id) {
return !!$('#' + $.wcfEscapeID(id)).length;
+ },
+
+ /**
+ * Returns the length of an object.
+ *
+ * @param object targetObject
+ * @return integer
+ */
+ getLength: function(targetObject) {
+ var $length = 0;
+
+ for (var $key in targetObject) {
+ if (targetObject.hasOwnProperty($key)) {
+ $length++;
+ }
+ }
+
+ return $length;
}
});
}
};
+/**
+ * Handles multiple language input fields.
+ *
+ * @param string elementID
+ * @param boolean forceSelection
+ * @param object values
+ * @param object availableLanguages
+ */
+WCF.MultipleLanguageInput = function(elementID, forceSelection, values, availableLanguages) { this.init(elementID, forceSelection, values, availableLanguages); };
+WCF.MultipleLanguageInput.prototype = {
+ /**
+ * list of available languages
+ * @var object
+ */
+ _availableLanguages: {},
+
+ /**
+ * initialization state
+ * @var boolean
+ */
+ _didInit: false,
+
+ /**
+ * target input element
+ * @var jQuery
+ */
+ _element: null,
+
+ /**
+ * enables multiple language ability
+ * @var boolean
+ */
+ _isEnabled: false,
+
+ /**
+ * enforce multiple language ability
+ * @var boolean
+ */
+ _forceSelection: false,
+
+ /**
+ * currently active language id
+ * @var integer
+ */
+ _languageID: 0,
+
+ /**
+ * language selection list
+ * @var jQuery
+ */
+ _list: null,
+
+ /**
+ * list of language values on init
+ * @var object
+ */
+ _values: null,
+
+ /**
+ * Initializes multiple language ability for given element id.
+ *
+ * @param integer elementID
+ * @param boolean forceSelection
+ * @param boolean isEnabled
+ * @param object values
+ * @param object availableLanguages
+ */
+ init: function(elementID, forceSelection, values, availableLanguages) {
+ this._element = $('#' + $.wcfEscapeID(elementID));
+ this._forceSelection = forceSelection;
+ this._values = values;
+ this._availableLanguages = availableLanguages;
+
+ // default to current user language
+ this._languageID = LANGUAGE_ID;
+ if (this._element.length == 0) {
+ console.debug("[WCF.MultipleLanguageInput] element id '" + elementID + "' is unknown");
+ return;
+ }
+
+ // build selection handler
+ var $enableOnInit = ($.getLength(this._values) > 0) ? true : false;
+ this._prepareElement($enableOnInit);
+
+ // listen for submit event
+ this._element.parents('form').submit($.proxy(this._submit, this));
+
+ this._didInit = true;
+ },
+
+ /**
+ * Builds language handler.
+ *
+ * @param boolean enableOnInit
+ */
+ _prepareElement: function(enableOnInit) {
+ this._element.wrap('<div class="preInput" />');
+ var $wrapper = this._element.parent();
+ var $button = $('<p class="dropdownCaption"><span>enable i18n</span></p>').prependTo($wrapper);
+ var $span = $button.children('span');
+
+ $span.click($.proxy(this._enable, this));
+ WCF.CloseOverlayHandler.addCallback(this._element.wcfIdentify(), $.proxy(this._closeSelection, this));
+
+ if (enableOnInit) {
+ $span.trigger('click');
+
+ // pre-select current language
+ this._list.children('li').each($.proxy(function(index, listItem) {
+ var $listItem = $(listItem);
+ if ($listItem.data('languageID') == this._languageID) {
+ $listItem.trigger('click');
+ }
+ }, this));
+ }
+ },
+
+ /**
+ * Enables the language selection or shows the selection if already enabled.
+ *
+ * @param object event
+ */
+ _enable: function(event) {
+ if (!this._isEnabled) {
+ var $button = $(event.target);
+ $button.addClass('active');
+
+ // insert list
+ if (this._list === null) {
+ this._list = $('<ul class="dropdown"></ul>').insertAfter($button.parent());
+ this._list.click(function(event) {
+ // discard click event
+ event.stopPropagation();
+ });
+
+ // insert available languages
+ for (var $languageID in this._availableLanguages) {
+ $('<li>' + this._availableLanguages[$languageID] + '</li>').data('languageID', $languageID).click($.proxy(this._changeLanguage, this)).appendTo(this._list);
+ }
+
+ // disable language input
+ $('<li class="divider">disable i18n</li>').click($.proxy(this._disable, this)).appendTo(this._list);
+ }
+
+ this._isEnabled = true;
+ }
+
+ // toggle list
+ if (this._list.is(':visible')) {
+ this._closeSelection();
+ }
+ else {
+ this._showSelection();
+ }
+
+ // discard event
+ event.stopPropagation();
+ },
+
+ /**
+ * Shows the language selection.
+ */
+ _showSelection: function() {
+ if (this._isEnabled) {
+ // display status for each language
+ this._list.children('li').each($.proxy(function(index, listItem) {
+ var $listItem = $(listItem);
+ var $languageID = $listItem.data('languageID');
+
+ if ($languageID) {
+ if (this._values[$languageID] && this._values[$languageID] != '') {
+ $listItem.removeClass('missingValue');
+ }
+ else {
+ $listItem.addClass('missingValue');
+ }
+ }
+ }, this));
+
+ // show list
+ this._list.show();
+ }
+ },
+
+ /**
+ * Closes the language selection.
+ */
+ _closeSelection: function() {
+ this._list.hide();
+ },
+
+ /**
+ * Changes the currently active language.
+ *
+ * @param object event
+ */
+ _changeLanguage: function(event) {
+ var $button = $(event.target);
+
+ // save current value
+ if (this._didInit) {
+ this._values[this._languageID] = this._element.val();
+ }
+
+ // set new language
+ this._languageID = $button.data('languageID');
+ if (this._values[this._languageID]) {
+ this._element.val(this._values[this._languageID]);
+ }
+ else {
+ this._element.val('');
+ }
+
+ // update marking
+ this._list.children('li').removeClass('active');
+ $button.addClass('active');
+
+ // update label
+ this._list.prev('.dropdownCaption').children('span').text(this._availableLanguages[this._languageID]);
+
+ // close selection and set focus on input element
+ this._closeSelection();
+ this._element.focus();
+ },
+
+ /**
+ * Disables language selection for current element.
+ */
+ _disable: function() {
+ // remove active marking
+ this._list.prev('.dropdownCaption').children('span').removeClass('active').text('enable i18n');
+ this._closeSelection();
+
+ // update element value
+ if (this._values[LANGUAGE_ID]) {
+ this._element.val(this._values[LANGUAGE_ID]);
+ }
+ else {
+ // no value for current language found, proceed with empty input
+ this._element.val();
+ }
+
+ this._isEnabled = false;
+ },
+
+ /**
+ * Prepares language variables on before submit.
+ */
+ _submit: function() {
+ // insert hidden form elements on before submit
+ if (!this._isEnabled) {
+ return 0xDEADBEAF;
+ }
+
+ // fetch active value
+ if (this._languageID) {
+ this._values[this._languageID] = this._element.val();
+ }
+
+ var $form = $(this._element.parents('form')[0]);
+ var $elementID = this._element.wcfIdentify();
+
+ for (var $languageID in this._values) {
+ $('<input type="hidden" name="' + $elementID + '_i18n[' + $languageID + ']" value="' + this._values[$languageID] + '" />').appendTo($form);
+ }
+
+ // remove name attribute to prevent conflict with i18n values
+ this._element.removeAttr('name');
+ }
+};
+
/**
* Icon collection used across all JavaScript classes.
*
use wcf\system\menu\acp\ACPMenu;
use wcf\data\user\group\UserGroup;
use wcf\data\user\group\UserGroupAction;
+use wcf\data\user\group\UserGroupEditor;
use wcf\system\exception\UserInputException;
use wcf\system\exception\SystemException;
+use wcf\system\language\I18nHandler;
use wcf\system\WCF;
use wcf\system\WCFACP;
use wcf\util\ClassUtil;
*/
public $additionalFields = array();
+ public function readParameters() {
+ parent::readParameters();
+
+ I18nHandler::getInstance()->register('groupName');
+ }
+
/**
* @see wcf\form\IForm::readFormParameters()
*/
public function readFormParameters() {
parent::readFormParameters();
- if (isset($_POST['groupName'])) $this->groupName = StringUtil::trim($_POST['groupName']);
+ I18nHandler::getInstance()->readValues();
+
+ if (I18nHandler::getInstance()->isPlainValue('groupName')) $this->groupName = I18nHandler::getInstance()->getValue('groupName');
if (isset($_POST['activeTabMenuItem'])) $this->activeTabMenuItem = $_POST['activeTabMenuItem'];
if (isset($_POST['activeSubTabMenuItem'])) $this->activeSubTabMenuItem = $_POST['activeSubTabMenuItem'];
}
// validate group name
try {
- if (empty($this->groupName)) {
+ if (!I18nHandler::getInstance()->validateValue('groupName')) {
throw new UserInputException('groupName');
}
}
);
$groupAction = new UserGroupAction(array(), 'create', $data);
$groupAction->executeAction();
+
+ if (!I18nHandler::getInstance()->isPlainValue('groupName')) {
+ $returnValues = $groupAction->getReturnValues();
+ $groupID = $returnValues['returnValues']->groupID;
+ I18nHandler::getInstance()->save('groupName', 'wcf.acp.group.group'.$groupID, 'wcf.acp.group', 1);
+
+ // update group name
+ $groupEditor = new UserGroupEditor($returnValues['returnValues']);
+ $groupEditor->update(array(
+ 'groupName' => 'wcf.acp.group.group'.$groupID
+ ));
+ }
+
$this->saved();
// show success message
public function assignVariables() {
parent::assignVariables();
+ I18nHandler::getInstance()->assignVariables(1);
+
WCF::getTPL()->assign(array(
'groupName' => $this->groupName,
'optionTree' => $this->optionTree,
use wcf\form\AbstractForm;
use wcf\system\exception\IllegalLinkException;
use wcf\system\exception\PermissionDeniedException;
+use wcf\system\language\I18nHandler;
use wcf\system\WCF;
/**
*/
public function readData() {
if (!count($_POST)) {
+ I18nHandler::getInstance()->setOptions('groupName', $this->group->groupName, 'wcf.acp.group.group\d+');
$this->groupName = $this->group->groupName;
// get default values
public function assignVariables() {
parent::assignVariables();
+ $useRequestData = (count($_POST)) ? true : false;
+ I18nHandler::getInstance()->assignVariables(1, $useRequestData);
+
WCF::getTPL()->assign(array(
'groupID' => $this->group->groupID,
'action' => 'edit'
}
}
}
+
+ $this->groupName = 'wcf.acp.group.group'.$this->group->groupID;
+ if (I18nHandler::getInstance()->isPlainValue('groupName')) {
+ I18nHandler::getInstance()->remove($this->groupName, 1);
+ $this->groupName = I18nHandler::getInstance()->getValue('groupName');
+ }
+ else {
+ I18nHandler::getInstance()->save('groupName', $this->groupName, 'wcf.acp.group', 1);
+ }
+
$data = array(
'data' => array_merge(array('groupName' => $this->groupName), $this->additionalFields),
'options' => $saveOptions
--- /dev/null
+<?php
+namespace wcf\system\language;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\exception\SystemException;
+use wcf\system\SingletonFactory;
+use wcf\system\WCF;
+
+/**
+ * Provides internationalization support for input fields.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.language
+ * @category Community Framework
+ */
+class I18nHandler extends SingletonFactory {
+ /**
+ * list of element ids
+ * @var array<string>
+ */
+ protected $elementIDs = array();
+
+ /**
+ * list of plain values for elements
+ * @var array<string>
+ */
+ protected $plainValues = array();
+
+ /**
+ * i18n values for elements
+ * @var array<array>
+ */
+ protected $i18nValues = array();
+
+ /**
+ * element options
+ * @var array<array>
+ */
+ protected $elementOptions = array();
+
+ /**
+ * Registers a new element id, returns false if element id is already set.
+ *
+ * @param string elementID
+ * @return boolean
+ */
+ public function register($elementID) {
+ if (in_array($elementID, $this->elementIDs)) {
+ return false;
+ }
+
+ $this->elementIDs[] = $elementID;
+ return true;
+ }
+
+ /**
+ * Reads plain and i18n values from request data.
+ */
+ public function readValues() {
+ foreach ($this->elementIDs as $elementID) {
+ if (isset($_POST[$elementID])) {
+ $this->plainValues[$elementID] = $_POST[$elementID];
+ continue;
+ }
+
+ $i18nElementID = $elementID . '_i18n';
+ if (isset($_POST[$i18nElementID]) && is_array($_POST[$i18nElementID])) {
+ $this->i18nValues[$elementID] = array();
+
+ foreach ($_POST[$i18nElementID] as $languageID => $value) {
+ $this->i18nValues[$elementID][$languageID] = $value;
+ }
+
+ continue;
+ }
+
+ throw new SystemException("Missing expected value for element id '".$elementID."'");
+ }
+ }
+
+ /**
+ * Returns true, if given element has disabled i18n functionality.
+ *
+ * @param string elementID
+ * @return boolean
+ */
+ public function isPlainValue($elementID) {
+ if (isset($this->plainValues[$elementID])) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true, if given element has enabled i18n functionality.
+ */
+ public function hasI18nValues($elementID) {
+ if (isset($this->i18nValues[$elementID])) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns plain value for given element.
+ *
+ * @param string elementID
+ * @return string
+ * @see wcf\system\language\I18nHandler::isPlainValue()
+ */
+ public function getValue($elementID) {
+ return $this->plainValues[$elementID];
+ }
+
+ /**
+ * Returns false, if element value is not empty.
+ *
+ * @param string $elementID
+ * @return boolean
+ */
+ public function validateValue($elementID) {
+ if ($this->isPlainValue($elementID)) {
+ if ($this->getValue($elementID) == '') {
+ return false;
+ }
+ }
+ else {
+ foreach ($this->i18nValues[$elementID] as $value) {
+ if (empty($value)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Saves language variable for i18n. Given package id must match the associated
+ * packages, using PACKAGE_ID is highly discouraged as this breaks the ability
+ * to delete unused language items on package uninstallation using foreign keys.
+ *
+ * @param string $elementID
+ * @param string $languageVariable
+ * @param string $languageCategory
+ * @param integer $packageID
+ */
+ public function save($elementID, $languageVariable, $languageCategory, $packageID) {
+ // get language category id
+ $sql = "SELECT languageCategoryID
+ FROM wcf".WCF_N."_language_category
+ WHERE languageCategory = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($languageCategory));
+ $row = $statement->fetchArray();
+ $languageCategoryID = $row['languageCategoryID'];
+
+ $languageIDs = array_keys($this->i18nValues[$elementID]);
+
+ $conditions = new PreparedStatementConditionBuilder();
+ $conditions->add("languageID IN (?)", array($languageIDs));
+ $conditions->add("languageItem = ?", array($languageVariable));
+ $conditions->add("packageID = ?", array($packageID));
+
+ $sql = "SELECT languageItemID, languageID
+ FROM wcf".WCF_N."_language_item
+ ".$conditions;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute($conditions->getParameters());
+ $languageItemIDs = array();
+ while ($row = $statement->fetchArray()) {
+ $languageItemIDs[$row['languageID']] = $row['languageItemID'];
+ }
+
+ $insertLanguageIDs = $updateLanguageIDs = array();
+ foreach ($languageIDs as $languageID) {
+ if (isset($languageItemIDs[$languageID])) {
+ $updateLanguageIDs[] = $languageID;
+ }
+ else {
+ $insertLanguageIDs[] = $languageID;
+ }
+ }
+
+ // insert language items
+ if (count($insertLanguageIDs)) {
+ $sql = "INSERT INTO wcf".WCF_N."_language_item
+ (languageID, languageItem, languageItemValue, languageCategoryID, packageID)
+ VALUES (?, ?, ?, ?, ?)";
+ $statement = WCF::getDB()->prepareStatement($sql);
+
+ foreach ($insertLanguageIDs as $languageID) {
+ $statement->execute(array(
+ $languageID,
+ $languageVariable,
+ $this->i18nValues[$elementID][$languageID],
+ $languageCategoryID,
+ $packageID
+ ));
+ }
+ }
+
+ // update language items
+ if (count($updateLanguageIDs)) {
+ $sql = "UPDATE wcf".WCF_N."_language_item
+ SET languageItemValue = ?
+ WHERE languageItemID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+
+ foreach ($updateLanguageIDs as $languageID) {
+ $statement->execute(array(
+ $this->i18nValues[$elementID][$languageID],
+ $languageItemIDs[$languageID]
+ ));
+ }
+ }
+ }
+
+ /**
+ * Removes previously created i18n language variables.
+ *
+ * @param string $languageVariable
+ * @param integer $packageID
+ */
+ public function remove($languageVariable, $packageID) {
+ $sql = "DELETE FROM wcf".WCF_N."_language_item
+ WHERE languageItem = ?
+ AND packageID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array(
+ $languageVariable,
+ $packageID
+ ));
+ }
+
+ /**
+ * Sets additional options for elements, required if updating values.
+ *
+ * @param integer $elementID
+ * @param string $value
+ * @param string $pattern
+ */
+ public function setOptions($elementID, $value, $pattern) {
+ $this->elementOptions[$elementID] = array(
+ 'pattern' => $pattern,
+ 'value' => $value
+ );
+ }
+
+ /**
+ * Assigns element values to template. Using request data once reading
+ * initial database data is explicitly disallowed. Restrictions on package
+ * id apply as explained in save().
+ *
+ * @param integer $packageID
+ * @param boolean $useRequestData
+ */
+ public function assignVariables($packageID, $useRequestData = true) {
+ foreach ($this->elementIDs as $elementID) {
+ $elementValue = '';
+ $elementI18nValues = array();
+
+ // use POST values instead of querying database
+ if ($useRequestData) {
+ if ($this->isPlainValue($elementID)) {
+ $elementValue = $this->getValue($elementID);
+ }
+ else {
+ if ($this->hasI18nValues($elementID)) {
+ $elementI18nValues = $this->i18nValues[$elementID];
+ }
+ else {
+ $elementI18nValues = array();
+ }
+ }
+ }
+ else {
+ if (preg_match('~^'.$this->elementOptions[$elementID]['pattern'].'$~', $this->elementOptions[$elementID]['value'])) {
+ // use i18n values from language items
+ $sql = "SELECT languageID, languageItemValue
+ FROM wcf".WCF_N."_language_item
+ WHERE languageItem = ?
+ AND packageID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array(
+ $this->elementOptions[$elementID]['value'],
+ $packageID
+ ));
+ while ($row = $statement->fetchArray()) {
+ $elementI18nValues[$row['languageID']] = $row['languageItemValue'];
+ }
+
+ }
+ else {
+ // use data provided by setOptions()
+ $elementValue = $this->elementOptions[$elementID]['value'];
+ }
+ }
+
+ WCF::getTPL()->assign(array(
+ 'availableLanguages' => LanguageFactory::getInstance()->getLanguages(),
+ $elementID => $elementValue,
+ $elementID.'_i18n' => $elementI18nValues
+ ));
+ }
+ }
+}