Boolean user group options now support 'Never'
authorAlexander Ebert <ebert@woltlab.com>
Sun, 7 Jun 2015 19:30:10 +0000 (21:30 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Sun, 7 Jun 2015 19:35:32 +0000 (21:35 +0200)
The boolean option type allowed only for a simple true/false, which is sometimes quite difficult to work with. For example revoking group permissions is usually accomplished by fiddling with all groups.

The 'Never' option solves this by using the unique value '-1' to revoke a permission regardless how often it was granted.

12 files changed:
wcfsetup/install/files/acp/templates/userGroupBooleanOptionType.tpl [new file with mode: 0644]
wcfsetup/install/files/js/WCF.js
wcfsetup/install/files/lib/data/user/group/UserGroup.class.php
wcfsetup/install/files/lib/system/cache/builder/UserGroupPermissionCacheBuilder.class.php
wcfsetup/install/files/lib/system/option/OptionHandler.class.php
wcfsetup/install/files/lib/system/option/user/group/BooleanUserGroupOptionType.class.php
wcfsetup/install/files/lib/system/option/user/group/IUserGroupGroupOptionType.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/option/user/group/TUserGroupOptionType.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/option/user/group/UserGroupOptionHandler.class.php
wcfsetup/install/files/style/form.less
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

diff --git a/wcfsetup/install/files/acp/templates/userGroupBooleanOptionType.tpl b/wcfsetup/install/files/acp/templates/userGroupBooleanOptionType.tpl
new file mode 100644 (file)
index 0000000..9d2cd50
--- /dev/null
@@ -0,0 +1,16 @@
+<ol class="optionTypeBoolean">
+       <li>
+               <input type="radio" id="{$option->optionName}_yes"{if $value == 1} checked="checked"{/if} name="values[{$option->optionName}]" value="1"{if $disableOptions || $enableOptions} class="jsEnablesOptions" data-is-boolean="true" data-disable-options="[ {@$disableOptions}]" data-enable-options="[ {@$enableOptions}]"{/if}>
+               <label for="{$option->optionName}_yes" class="yes"><span class="icon icon16 fa-check"></span> {lang}wcf.acp.group.option.type.boolean.yes{/lang}</label>
+       </li>
+       <li>
+               <input type="radio" id="{$option->optionName}_no"{if $value == 0} checked="checked"{/if} name="values[{$option->optionName}]" value="0"{if $disableOptions || $enableOptions} class="jsEnablesOptions" data-is-boolean="true" data-disable-options="[ {@$disableOptions}]" data-enable-options="[ {@$enableOptions}]"{/if}>
+               <label for="{$option->optionName}_no" class="no"><span class="icon icon16 fa-times"></span> {lang}wcf.acp.group.option.type.boolean.no{/lang}</label>
+       </li>
+       {if $group === null || !$group->isEveryone()}
+               <li>
+                       <input type="radio" id="{$option->optionName}_never"{if $value == -1} checked="checked"{/if} name="values[{$option->optionName}]" value="-1"{if $disableOptions || $enableOptions} class="jsEnablesOptions" data-is-boolean="true" data-disable-options="[ {@$disableOptions}]" data-enable-options="[ {@$enableOptions}]"{/if}>
+                       <label for="{$option->optionName}_never" class="never"><span class="icon icon16 fa-ban"></span> {lang}wcf.acp.group.option.type.boolean.never{/lang}</label>
+               </li>
+       {/if}
+</ol>
index 328fbf842272b426336d077c9f87c4c801426395..7998f980ee8b1f0f5652e6455b09e1f9c116e7ff 100755 (executable)
@@ -4578,7 +4578,12 @@ WCF.Option.Handler = Class.extend({
                                        
                                        case 'radio':
                                                if (option.prop('checked')) {
-                                                       this._execute(true, $disableOptions, $enableOptions);
+                                                       var isActive = true;
+                                                       if (option.data('isBoolean') && option.val() != 1) {
+                                                               isActive = false;
+                                                       }
+                                                       
+                                                       this._execute(isActive, $disableOptions, $enableOptions);
                                                }
                                        break;
                                }
index 8865be30c397d56a891d83536902114371c14bac..df45bdd8202b735df25210beffd1a79d0e97b97c 100644 (file)
@@ -156,6 +156,15 @@ class UserGroup extends DatabaseObject {
                return false;
        }
        
+       /**
+        * Returns true if this is the 'Everyone' group.
+        * 
+        * @return      boolean
+        */
+       public function isEveryone() {
+               return $this->groupType == self::EVERYONE;
+       }
+       
        /**
         * Returns true if the given groups are accessible for the active user.
         * 
index 5a0623ca70a558b7fc6bd9590a030923d746a665..15d69df24daf717fea919fe1d992e57b6b4ebd53 100644 (file)
@@ -21,69 +21,60 @@ class UserGroupPermissionCacheBuilder extends AbstractCacheBuilder {
         * list of used group option type objects
         * @var array<\wcf\system\option\group\IGroupOptionType>
         */
-       protected $typeObjects = array();
+       protected $typeObjects = [];
        
        /**
         * @see \wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
         */
        public function rebuild(array $parameters) {
-               $data = array();
+               $data = [];
                
-               // get all options
-               $sql = "SELECT  optionName, optionID
-                       FROM    wcf".WCF_N."_user_group_option";
-               $statement = WCF::getDB()->prepareStatement($sql);
-               $statement->execute();
+               // get option values
+               $conditions = new PreparedStatementConditionBuilder();
+               $conditions->add("option_value.groupID IN (?)", [ $parameters ]);
                
-               $options = array();
+               $sql = "SELECT          option_table.optionName, option_table.optionType, option_value.optionValue
+                       FROM            wcf".WCF_N."_user_group_option_value option_value
+                       LEFT JOIN       wcf".WCF_N."_user_group_option option_table
+                       ON              (option_table.optionID = option_value.optionID)
+                       ".$conditions;
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $statement->execute($conditions->getParameters());
                while ($row = $statement->fetchArray()) {
-                       $options[$row['optionName']] = $row['optionID'];
+                       if (!isset($data[$row['optionName']])) {
+                               $data[$row['optionName']] = [ 'type' => $row['optionType'], 'values' => [] ];
+                       }
+                       
+                       $data[$row['optionName']]['values'][] = $row['optionValue'];
                }
                
-               if (!empty($options)) {
-                       // get needed options
-                       $conditions = new PreparedStatementConditionBuilder();
-                       $conditions->add("option_value.groupID IN (?)", array($parameters));
-                       $conditions->add("option_value.optionID IN (?)", array($options));
-                       
-                       $sql = "SELECT          option_table.optionName, option_table.optionType, option_value.optionValue
-                               FROM            wcf".WCF_N."_user_group_option_value option_value
-                               LEFT JOIN       wcf".WCF_N."_user_group_option option_table
-                               ON              (option_table.optionID = option_value.optionID)
-                               ".$conditions;
-                       $statement = WCF::getDB()->prepareStatement($sql);
-                       $statement->execute($conditions->getParameters());
-                       while ($row = $statement->fetchArray()) {
-                               if (!isset($data[$row['optionName']])) {
-                                       $data[$row['optionName']] = array('type' => $row['optionType'], 'values' => array());
-                               }
-                               
-                               $data[$row['optionName']]['values'][] = $row['optionValue'];
+               // merge values
+               foreach ($data as $optionName => $option) {
+                       if (count($option['values']) == 1) {
+                               $result = $option['values'][0];
                        }
-                       
-                       // merge values
-                       foreach ($data as $optionName => $option) {
-                               if (count($option['values']) == 1) {
-                                       $result = $option['values'][0];
-                               }
-                               else {
-                                       $typeObj = $this->getTypeObject($option['type']);
-                                       $result = array_shift($option['values']);
-                                       foreach ($option['values'] as $value) {
-                                               $newValue = $typeObj->merge($result, $value);
-                                               if ($newValue !== null) {
-                                                       $result = $newValue;
-                                               }
+                       else {
+                               $typeObj = $this->getTypeObject($option['type']);
+                               $result = array_shift($option['values']);
+                               foreach ($option['values'] as $value) {
+                                       $newValue = $typeObj->merge($result, $value);
+                                       if ($newValue !== null) {
+                                               $result = $newValue;
                                        }
                                }
-                               
-                               // unset false values
-                               if ($result === false) {
-                                       unset($data[$optionName]);
-                               }
-                               else {
-                                       $data[$optionName] = $result;
-                               }
+                       }
+                       
+                       // handle special value 'Never' for boolean options
+                       if ($option['type'] === 'boolean' && $result == -1) {
+                               $result = 0;
+                       }
+                       
+                       // unset false values
+                       if ($result === false) {
+                               unset($data[$optionName]);
+                       }
+                       else {
+                               $data[$optionName] = $result;
                        }
                }
                
index dc3742d276dae7d89f1e755693e977bac83accdf..102a0e52be6f739ead2f3d3619e9f41a49595b10 100644 (file)
@@ -7,7 +7,6 @@ use wcf\system\event\EventHandler;
 use wcf\system\exception\SystemException;
 use wcf\system\exception\UserInputException;
 use wcf\system\language\I18nHandler;
-use wcf\system\WCF;
 use wcf\util\ClassUtil;
 use wcf\util\StringUtil;
 
index c25dfa9ff894822b74b989e54134d4f981801d27..3c72f3273176944fcff6af831d709ce0720cd36b 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 namespace wcf\system\option\user\group;
+use wcf\data\option\Option;
 use wcf\system\option\BooleanOptionType;
+use wcf\system\WCF;
 
 /**
  * User group option type implementation for boolean values.
@@ -14,11 +16,42 @@ use wcf\system\option\BooleanOptionType;
  * @subpackage system.option.user.group
  * @category   Community Framework
  */
-class BooleanUserGroupOptionType extends BooleanOptionType implements IUserGroupOptionType {
+class BooleanUserGroupOptionType extends BooleanOptionType implements IUserGroupOptionType, IUserGroupGroupOptionType {
+       use TUserGroupOptionType;
+       
+       /**
+        * @see \wcf\system\option\IOptionType::getFormElement()
+        */
+       public function getFormElement(Option $option, $value) {
+               $options = Option::parseEnableOptions($option->enableOptions);
+               
+               WCF::getTPL()->assign([
+                       'disableOptions' => $options['disableOptions'],
+                       'enableOptions' => $options['enableOptions'],
+                       'group' => $this->userGroup,
+                       'option' => $option,
+                       'value' => $value
+               ]);
+               
+               return WCF::getTPL()->fetch('userGroupBooleanOptionType');
+       }
+       
+       /**
+        * @see \wcf\system\option\IOptionType::getCSSClassName()
+        */
+       public function getCSSClassName() {
+               return '';
+       }
+       
        /**
         * @see \wcf\system\option\user\group\IUserGroupOptionType::merge()
         */
        public function merge($defaultValue, $groupValue) {
+               // force value for 'Never'
+               if ($defaultValue == -1 || $groupValue == -1) {
+                       return -1;
+               }
+               
                // don't save if values are equal or $defaultValue is better
                if ($defaultValue == $groupValue || $defaultValue && !$groupValue) {
                        return null;
diff --git a/wcfsetup/install/files/lib/system/option/user/group/IUserGroupGroupOptionType.class.php b/wcfsetup/install/files/lib/system/option/user/group/IUserGroupGroupOptionType.class.php
new file mode 100644 (file)
index 0000000..dfa1e4e
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+namespace wcf\system\option\user\group;
+use wcf\data\user\group\UserGroup;
+
+/**
+ * Default interface for user group option types requiring the active user group object.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.option.user.group
+ * @category   Community Framework
+ */
+interface IUserGroupGroupOptionType {
+       /**
+        * Sets the active user group object.
+        * 
+        * @param       \wcf\data\user\group\UserGroup          $group
+        */
+       public function setUserGroup(UserGroup $group);
+       
+       /**
+        * Returns the active user group object or null.
+        * 
+        * @return      \wcf\data\user\group\UserGroup
+        */
+       public function getUserGroup();
+}
diff --git a/wcfsetup/install/files/lib/system/option/user/group/TUserGroupOptionType.class.php b/wcfsetup/install/files/lib/system/option/user/group/TUserGroupOptionType.class.php
new file mode 100644 (file)
index 0000000..0b1e9bf
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+namespace wcf\system\option\user\group;
+use wcf\data\user\group\UserGroup;
+
+/**
+ * Default trait for user group option types implementing IUserGroupGroupOptionType.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.option.user.group
+ * @category   Community Framework
+ */
+trait TUserGroupOptionType {
+       /**
+        * user group object
+        * @var \wcf\data\user\group\UserGroup
+        */
+       protected $userGroup = null;
+       
+       /**
+        * @see \wcf\system\option\user\group\IUserGroupGroupOptionType::setUserGroup()
+        */
+       public function setUserGroup(UserGroup $group) {
+               $this->userGroup = $group;
+       }
+       
+       /**
+        * @see \wcf\system\option\user\group\IUserGroupGroupOptionType::getUserGroup()
+        */
+       public function getUserGroup() {
+               return $this->userGroup;
+       }
+}
index 1e176f554c23e29c284f4da2cc017b220218098f..5115a8f535f7b6c5a9bf01cb0ae011b6324e553a 100644 (file)
@@ -45,6 +45,19 @@ class UserGroupOptionHandler extends OptionHandler {
                $this->group = $group;
        }
        
+       /**
+        * @see \wcf\system\option\OptionHandler::getTypeObject()
+        */
+       public function getTypeObject($type) {
+               $objectType = parent::getTypeObject($type);
+               
+               if ($this->group !== null && $objectType instanceof IUserGroupGroupOptionType) {
+                       $objectType->setUserGroup($this->group);
+               }
+               
+               return $objectType;
+       }
+       
        /**
         * @see \wcf\system\option\OptionHandler::checkOption()
         */
index 75723b99961bc0d654df10b43eae1c6dfdc9964e..f1d8c57a1b2fa31a1ccce6c21402f48ee57dd05f 100644 (file)
@@ -543,4 +543,90 @@ select > option {
        .redactor-editor {
                font-size: 16px;
        }
+}
+
+/* BooleanUserGroupOptionType */
+.optionTypeBoolean {
+       display: flex;
+       
+       > li {
+               border: 1px solid @wcfContainerBorderColor;
+               display: flex;
+               flex: 0 1 100px;
+               
+               &:not(:last-child) {
+                       border-right-width: 0;
+               }
+               
+               &:first-child {
+                       border-radius: 3px 0 0 3px;
+               }
+               
+               &:last-child {
+                       border-radius: 0 3px 3px 0;
+               }
+               
+               &:hover > label {
+                       opacity: 1 !important;
+               }
+               
+               > input {
+                       display: none;
+                       
+                       &:not(:checked) + label {
+                               opacity: .6;
+                       }
+                       
+                       &:checked + label {
+                               opacity: 1;
+                               
+                               &.yes {
+                                       background-color: rgb(223, 240, 216);
+                                       color: rgb(60, 118, 61);
+                                       
+                                       > .icon {
+                                               color: rgb(60, 118, 61);
+                                       }
+                               }
+                               
+                               &.no {
+                                       background-color: rgb(242, 222, 222);
+                                       color: rgb(169, 68, 66);
+                                       
+                                       > .icon {
+                                               color: rgb(169, 68, 66);
+                                       }
+                               }
+                               
+                               &.never {
+                                       background-color: rgb(252, 248, 227);
+                                       color: rgb(138, 109, 59);
+                                       
+                                       > .icon {
+                                               color: rgb(138, 109, 59);
+                                       }
+                               }
+                       }
+               }
+               
+               > label {
+                       cursor: pointer;
+                       flex: 1 auto;
+                       font-size: .85rem;
+                       padding: @wcfGapTiny;
+                       text-align: center;
+                       
+                       transition: all .3s linear;
+                       
+                       > .icon {
+                               cursor: pointer !important;
+                       }
+               }
+       }
+}
+
+@media only screen and (max-width: 800px) {
+       .optionTypeBoolean > li {
+               flex: 1;
+       }
 }
\ No newline at end of file
index f62663f69d5285130f261a48555ee66ec97b5b07..4cb6967bd21b3771ace2fe730766d51168a375bb 100644 (file)
                <item name="wcf.acp.group.copy.copyUserGroupOptions.description"><![CDATA[Die neue Benutzergruppe wird die gleichen Berechtigungen besitzen wie die kopierte Benutzergruppe.]]></item>
                <item name="wcf.acp.group.option.admin.paidSubscription.canManageSubscription"><![CDATA[Kann bezahlte Mitgliedschaften verwalten]]></item>
                <item name="wcf.acp.group.option.user.tag.canViewTag"><![CDATA[Kann Tag sehen]]></item>
+               <item name="wcf.acp.group.option.type.boolean.never"><![CDATA[Nie]]></item>
+               <item name="wcf.acp.group.option.type.boolean.no"><![CDATA[Nein]]></item>
+               <item name="wcf.acp.group.option.type.boolean.yes"><![CDATA[Ja]]></item>
        </category>
        
        <category name="wcf.acp.index">
index 012cdb0d79ae72d31616b8b2e891e7f3c398b0aa..d0a22231ced18bc5a755eed13673c9d9fdd187ac 100644 (file)
@@ -439,6 +439,9 @@ Examples for medium ID detection:
                <item name="wcf.acp.group.copy.copyUserGroupOptions.description"><![CDATA[The new user group will have the same permissions as the copied user group.]]></item>
                <item name="wcf.acp.group.option.admin.paidSubscription.canManageSubscription"><![CDATA[Can manage paid subscriptions]]></item>
                <item name="wcf.acp.group.option.user.tag.canViewTag"><![CDATA[Can view tags]]></item>
+               <item name="wcf.acp.group.option.type.boolean.never"><![CDATA[Never]]></item>
+               <item name="wcf.acp.group.option.type.boolean.no"><![CDATA[No]]></item>
+               <item name="wcf.acp.group.option.type.boolean.yes"><![CDATA[Yes]]></item>
        </category>
        
        <category name="wcf.acp.index">