From a98501eb4786bd79e064e6894b65f6b8e7115bd2 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Mon, 8 Apr 2019 10:39:20 +0200 Subject: [PATCH] New group type: Owner (5) --- .../files/acp/templates/optionFieldList.tpl | 14 ++++- .../files/acp/templates/userGroupAdd.tpl | 40 +++++++++++++ .../files/acp/templates/userGroupList.tpl | 3 + .../lib/acp/form/UserGroupEditForm.class.php | 11 ++++ .../files/lib/data/user/User.class.php | 26 +++++++- .../lib/data/user/group/UserGroup.class.php | 60 ++++++++++++++++--- .../UserGroupPermissionCacheBuilder.class.php | 28 +++++++++ .../group/UserGroupOptionHandler.class.php | 34 +++++++---- wcfsetup/install/lang/de.xml | 3 + wcfsetup/install/lang/en.xml | 3 + wcfsetup/setup/db/install.sql | 2 +- 11 files changed, 202 insertions(+), 22 deletions(-) diff --git a/wcfsetup/install/files/acp/templates/optionFieldList.tpl b/wcfsetup/install/files/acp/templates/optionFieldList.tpl index 740eed8e1f..4ed68fe6ba 100644 --- a/wcfsetup/install/files/acp/templates/optionFieldList.tpl +++ b/wcfsetup/install/files/acp/templates/optionFieldList.tpl @@ -1,4 +1,5 @@ {if !$isGuestGroup|isset}{assign var=isGuestGroup value=false}{/if} +{if !$groupIsOwner|isset}{assign var=groupIsOwner value=false}{/if} {foreach from=$options item=optionData} {assign var=option value=$optionData[object]} {if $errorType|is_array && $errorType[$option->optionName]|isset} @@ -7,7 +8,16 @@ {assign var=error value=''} {/if}
- {if $isSearchMode|empty || !$optionData[hideLabelInSearch]}{/if} + + {if $isSearchMode|empty || !$optionData[hideLabelInSearch]} + + {/if} +
{@$optionData[html]} {if $error} @@ -21,4 +31,4 @@ {lang __optional=true}{@$langPrefix}{$option->optionName}.description{/lang}
-{/foreach} \ No newline at end of file +{/foreach} diff --git a/wcfsetup/install/files/acp/templates/userGroupAdd.tpl b/wcfsetup/install/files/acp/templates/userGroupAdd.tpl index bd03a7537c..94e8fd5e73 100644 --- a/wcfsetup/install/files/acp/templates/userGroupAdd.tpl +++ b/wcfsetup/install/files/acp/templates/userGroupAdd.tpl @@ -71,6 +71,10 @@

{lang}wcf.acp.group.excludedInTinyBuild.notice{/lang}

{/if} +{if $action == 'edit' && $group->isOwner()} +

{lang}wcf.acp.group.type.owner.description{/lang}

+{/if} + {if $warningSelfEdit|isset}

{lang}wcf.acp.group.edit.warning.selfIsMember{/lang}

{/if} @@ -219,4 +223,40 @@ +{if $action === 'edit'} + +{/if} + {include file='footer'} diff --git a/wcfsetup/install/files/acp/templates/userGroupList.tpl b/wcfsetup/install/files/acp/templates/userGroupList.tpl index 809dd14764..d525eb1438 100644 --- a/wcfsetup/install/files/acp/templates/userGroupList.tpl +++ b/wcfsetup/install/files/acp/templates/userGroupList.tpl @@ -69,6 +69,9 @@ {else} {lang}{$group->groupName}{/lang} {/if} + {if $group->isOwner()} + + {/if} {if $group->groupType == 1 ||$group->groupType == 2} diff --git a/wcfsetup/install/files/lib/acp/form/UserGroupEditForm.class.php b/wcfsetup/install/files/lib/acp/form/UserGroupEditForm.class.php index 8faefff4b6..d3cdf40c2f 100755 --- a/wcfsetup/install/files/lib/acp/form/UserGroupEditForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/UserGroupEditForm.class.php @@ -116,6 +116,14 @@ class UserGroupEditForm extends UserGroupAddForm { I18nHandler::getInstance()->assignVariables(!empty($_POST)); + $ownerGroupPermissions = []; + if ($this->group->isOwner()) { + $ownerGroupPermissions = UserGroup::getOwnerPermissions(); + $ownerGroupPermissions[] = 'admin.user.accessibleGroups'; + } + + $ownerGroup = UserGroup::getGroupByType(UserGroup::OWNER); + WCF::getTPL()->assign([ 'groupID' => $this->group->groupID, 'group' => $this->group, @@ -124,7 +132,10 @@ class UserGroupEditForm extends UserGroupAddForm { 'groupIsEveryone' => $this->group->groupType == UserGroup::EVERYONE, 'groupIsGuest' => $this->group->groupType == UserGroup::GUESTS, 'groupIsUsers' => $this->group->groupType == UserGroup::USERS, + 'groupIsOwner' => $this->group->isOwner(), 'isUnmentionableGroup' => $this->isUnmentionableGroup ? 1 : 0, + 'ownerGroupPermissions' => $ownerGroupPermissions, + 'ownerGroupID' => $ownerGroup ? $ownerGroup->groupID : null, ]); // add warning when the initiator is in the group diff --git a/wcfsetup/install/files/lib/data/user/User.class.php b/wcfsetup/install/files/lib/data/user/User.class.php index b34fa26781..8b14b3cd51 100644 --- a/wcfsetup/install/files/lib/data/user/User.class.php +++ b/wcfsetup/install/files/lib/data/user/User.class.php @@ -478,8 +478,7 @@ final class User extends DatabaseObject implements IRouteController, IUserConten $this->hasAdministrativePermissions = false; if ($this->userID) { - foreach ($this->getGroupIDs() as $groupID) { - $group = UserGroup::getGroupByID($groupID); + foreach (UserGroup::getGroupsByIDs($this->getGroupIDs()) as $group) { if ($group->isAdminGroup()) { $this->hasAdministrativePermissions = true; break; @@ -491,6 +490,29 @@ final class User extends DatabaseObject implements IRouteController, IUserConten return $this->hasAdministrativePermissions; } + /** + * Returns true, if this user is a member of the owner group. + * + * @return bool + * @since 5.2 + */ + public function hasOwnerAccess() { + static $isOwner; + + if ($isOwner === null) { + $isOwner = false; + + foreach (UserGroup::getGroupsByIDs($this->getGroupIDs()) as $group) { + if ($group->isOwner()) { + $isOwner = true; + break; + } + } + } + + return $isOwner; + } + /** * @inheritDoc */ diff --git a/wcfsetup/install/files/lib/data/user/group/UserGroup.class.php b/wcfsetup/install/files/lib/data/user/group/UserGroup.class.php index abf5f314c6..6da1c1ba72 100644 --- a/wcfsetup/install/files/lib/data/user/group/UserGroup.class.php +++ b/wcfsetup/install/files/lib/data/user/group/UserGroup.class.php @@ -56,6 +56,12 @@ class UserGroup extends DatabaseObject implements ITitledObject { */ const OTHER = 4; + /** + * the owner group is always an administrator group + * @var int + */ + const OWNER = 5; + /** * group cache * @var UserGroup[] @@ -122,7 +128,7 @@ class UserGroup extends DatabaseObject implements ITitledObject { * @throws SystemException */ public static function getGroupByType($type) { - if ($type != self::EVERYONE && $type != self::GUESTS && $type != self::USERS) { + if ($type != self::EVERYONE && $type != self::GUESTS && $type != self::USERS && $type != self::OWNER) { throw new SystemException('invalid value for type argument'); } @@ -197,6 +203,16 @@ class UserGroup extends DatabaseObject implements ITitledObject { return $this->groupType == self::USERS; } + /** + * Returns true if this is the 'Owner' group. + * + * @return bool + * @since 5.2 + */ + public function isOwner() { + return $this->groupType == self::OWNER; + } + /** * Returns true if the given groups are accessible for the active user. * @@ -239,16 +255,25 @@ class UserGroup extends DatabaseObject implements ITitledObject { } /** - * Returns true if the current group is an admin-group. - * Every group that may access EVERY group is an admin-group. + * Returns true if the current group is an admin-group, which requires it to fulfill + * one of these conditions: + * a) The WCFSetup is running and the group id is 4. + * b) This is the 'Owner' group. + * c) The group can access all groups (the 'Owner' group does not count). * * @return boolean */ public function isAdminGroup() { - // workaround for WCF-Setup - if (!PACKAGE_ID && $this->groupID == 4) return true; + // WCFSetup + if (!PACKAGE_ID && $this->groupID == 4) { + return true; + } - $groupIDs = array_keys(self::getGroupsByType()); + if ($this->groupType === self::OWNER) { + return true; + } + + $groupIDs = array_keys(self::getGroupsByType([], [self::OWNER])); $accessibleGroupIDs = explode(',', (string) $this->getGroupOption('admin.user.accessibleGroups')); // no differences -> all groups are included @@ -329,7 +354,7 @@ class UserGroup extends DatabaseObject implements ITitledObject { if (!$this->isAccessible()) return false; // cannot delete static groups - if ($this->groupType == self::EVERYONE || $this->groupType == self::GUESTS || $this->groupType == self::USERS) return false; + if ($this->groupType == self::EVERYONE || $this->groupType == self::GUESTS || $this->groupType == self::USERS || $this->groupType == self::OWNER) return false; return true; } @@ -452,4 +477,25 @@ class UserGroup extends DatabaseObject implements ITitledObject { return self::$cache['groups']; } + + /** + * Returns the list of irrevocable permissions of the owner group. + * + * @return string[] + * @since 5.2 + */ + public static function getOwnerPermissions() { + return [ + 'admin.configuration.canEditOption', + 'admin.configuration.canManageApplication', + 'admin.configuration.package.canInstallPackage', + 'admin.configuration.package.canUninstallPackage', + 'admin.configuration.package.canUpdatePackage', + 'admin.general.canUseAcp', + 'admin.general.canViewPageDuringOfflineMode', + 'admin.user.canEditGroup', + 'admin.user.canEditUser', + 'admin.user.canSearchUser', + ]; + } } diff --git a/wcfsetup/install/files/lib/system/cache/builder/UserGroupPermissionCacheBuilder.class.php b/wcfsetup/install/files/lib/system/cache/builder/UserGroupPermissionCacheBuilder.class.php index c79991a969..d61aa563ca 100644 --- a/wcfsetup/install/files/lib/system/cache/builder/UserGroupPermissionCacheBuilder.class.php +++ b/wcfsetup/install/files/lib/system/cache/builder/UserGroupPermissionCacheBuilder.class.php @@ -68,6 +68,17 @@ class UserGroupPermissionCacheBuilder extends AbstractCacheBuilder { $data[$row['optionName']]['values'][] = $row['optionValue']; } + $includesOwnerGroup = false; + $ownerGroup = UserGroup::getGroupByType(UserGroup::OWNER); + if ($ownerGroup && in_array($ownerGroup->groupID, $parameters)) { + $includesOwnerGroup = true; + } + + $forceGrantPermission = []; + if ($includesOwnerGroup) { + $forceGrantPermission = UserGroup::getOwnerPermissions(); + } + // merge values $neverValues = []; foreach ($data as $optionName => $option) { @@ -89,6 +100,23 @@ class UserGroupPermissionCacheBuilder extends AbstractCacheBuilder { } } + if ($ownerGroup && $optionName === 'admin.user.accessibleGroups') { + $accessibleGroupIDs = explode(',', $result); + if ($includesOwnerGroup) { + // Regardless of the actual permissions, the owner group has access to all groups. + + $accessibleGroupIDs[] = $ownerGroup->groupID; + } + else if (!$includesOwnerGroup && in_array($ownerGroup->groupID, $accessibleGroupIDs)) { + $accessibleGroupIDs = array_diff($accessibleGroupIDs, [$ownerGroup->groupID]); + } + + $result = implode(',', $accessibleGroupIDs); + } + else if ($includesOwnerGroup && in_array($optionName, $forceGrantPermission)) { + $result = 1; + } + // handle special value 'Never' for boolean options if ($option['type'] === 'boolean' && $result == -1) { $neverValues[$optionName] = $optionName; diff --git a/wcfsetup/install/files/lib/system/option/user/group/UserGroupOptionHandler.class.php b/wcfsetup/install/files/lib/system/option/user/group/UserGroupOptionHandler.class.php index 0c1fefcc41..de1fcd01b1 100644 --- a/wcfsetup/install/files/lib/system/option/user/group/UserGroupOptionHandler.class.php +++ b/wcfsetup/install/files/lib/system/option/user/group/UserGroupOptionHandler.class.php @@ -26,7 +26,7 @@ class UserGroupOptionHandler extends OptionHandler { * user group object * @var UserGroup */ - protected $group = null; + protected $group; /** * true if current user can edit every user group @@ -34,6 +34,13 @@ class UserGroupOptionHandler extends OptionHandler { */ protected $isAdmin = null; + /** + * true if the user is part of the owner group + * @var bool + * @since 5.2 + */ + protected $isOwner = null; + /** * Sets current user group. * @@ -114,19 +121,26 @@ class UserGroupOptionHandler extends OptionHandler { */ protected function isAdmin() { if ($this->isAdmin === null) { - $this->isAdmin = false; - - foreach (WCF::getUser()->getGroupIDs() as $groupID) { - if (UserGroup::getGroupByID($groupID)->isAdminGroup()) { - $this->isAdmin = true; - break; - } - } + $this->isAdmin = WCF::getUser()->hasAdministrativeAccess(); } return $this->isAdmin; } + /** + * Returns true, if the current user is a member of the owner group. + * + * @return bool + * @since 5.2 + */ + protected function isOwner() { + if ($this->isOwner === null) { + $this->isOwner = WCF::getUser()->hasOwnerAccess(); + } + + return $this->isOwner; + } + /** * @inheritDoc */ @@ -141,7 +155,7 @@ class UserGroupOptionHandler extends OptionHandler { throw new UserInputException($option->optionName, 'exceedsOwnPermission'); } } - else if ($option->optionName == 'admin.user.accessibleGroups' && $this->group !== null && $this->group->isAdminGroup()) { + else if (!$this->isOwner() && $option->optionName == 'admin.user.accessibleGroups' && $this->group !== null && $this->group->isAdminGroup()) { $hasOtherAdminGroup = false; foreach (UserGroup::getGroupsByType() as $userGroup) { if ($userGroup->groupID != $this->group->groupID && $userGroup->isAdminGroup()) { diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index d69c0fb505..c144dadd23 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -880,6 +880,9 @@ Das Fehlerprotokoll enthält {$data[count]} neue Einträge. Die ersten drei, in + + + diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index 477dd695b2..2e154db23a 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -857,6 +857,9 @@ This protocol file contains {$data[count]} new entries. The first three error me + + + diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql index 9ed46d7ccb..1937a06c4c 100644 --- a/wcfsetup/setup/db/install.sql +++ b/wcfsetup/setup/db/install.sql @@ -2224,7 +2224,7 @@ ALTER TABLE wcf1_notice_dismissed ADD FOREIGN KEY (userID) REFERENCES wcf1_user INSERT INTO wcf1_user_group (groupID, groupName, groupType) VALUES (1, 'wcf.acp.group.group1', 1); -- Everyone INSERT INTO wcf1_user_group (groupID, groupName, groupType) VALUES (2, 'wcf.acp.group.group2', 2); -- Guests INSERT INTO wcf1_user_group (groupID, groupName, groupType) VALUES (3, 'wcf.acp.group.group3', 3); -- Registered Users -INSERT INTO wcf1_user_group (groupID, groupName, groupType) VALUES (4, 'wcf.acp.group.group4', 4); -- Administrators +INSERT INTO wcf1_user_group (groupID, groupName, groupType) VALUES (4, 'wcf.acp.group.group4', 5); -- Administrators INSERT INTO wcf1_user_group (groupID, groupName, groupType) VALUES (5, 'wcf.acp.group.group5', 4); -- Moderators -- default user group options -- 2.20.1