diff --git a/module/group/control.php b/module/group/control.php index 0c2d261ca9..fe0245bf17 100644 --- a/module/group/control.php +++ b/module/group/control.php @@ -371,14 +371,7 @@ class group extends control $this->view->indexPrivs = $indexPrivs; } - if($type == 'byModule') - { - $this->display('group', 'privbymodule'); - } - else - { - $this->display(); - } + $this->display(); } /** diff --git a/module/group/css/managepriv.ui.css b/module/group/css/managepriv.ui.css new file mode 100644 index 0000000000..02398b5180 --- /dev/null +++ b/module/group/css/managepriv.ui.css @@ -0,0 +1,47 @@ +#privByModuleForm .form-row select {height: 250px !important; background-image: none;} +#privByModuleForm .form-row select option:hover {background: #2e7fff;} + +#featureBar > .menu {background: unset;} +#versionSelect {flex: 0 1 12%;} + +/* Manage priv by package. */ +#mainContainer {gap: 16px; height: calc(100vh - 165px);} +#mainContainer > .main {flex: 1 1 75%; overflow-y: auto; padding: 0px; border: 1px solid #FFF; box-sizing: content-box;} +#mainContainer > .side {flex: 1 1 25%;} +#mainContainer .main .btn-group {position: absolute; visibility: hidden; top: 2px; display: inline-block; z-index: 2;} +#mainContainer .main .btn-group .switchBtn {padding: 6px 10px;} +#mainContainer .main .btn-group .switchBtn:first-child {border-radius: 4px 0px 0px 4px;} +#mainContainer .main .btn-group .switchBtn:last-child {border-radius: 0px 4px 4px 0px;} +.priv-panel {background: #fff; padding: 10px 16px; box-shadow: 0 1px 1px rgb(0 0 0 / 5%), 0 2px 6px 0 rgb(0 0 0 / 5%);} +.priv-panel {height: 40%;} +.priv-panel .table-empty-tip {padding: calc(15% - 10px) 10px;} +.priv-panel + .priv-panel .table-empty-tip {padding: calc(30% - 20px) 10px;} +.priv-panel + .priv-panel {margin-top: 16px; height: calc(60% - 16px);} +.priv-panel > .panel-content {height: calc(100% - 25px); overflow-y: auto; padding-left: 4px; margin-top: 10px;} +.priv-panel > .panel-bottom {padding-top: 20px;} +.priv-panel > .panel-title {position: relative;} +.icon-help + .popover {font-weight: 400;} +.menuTree li.has-list:not(:first-child) {margin-top: 20px;} + +#privPackageList tr {border-bottom-width: 0;} +#privPackageList th.module {width: 142px; min-width: 142px; max-width: 142px; background-color: #EDEEF28C; font-weight: 600; padding-left: 20px; border-color: #FFF;} +#privPackageList .module label {overflow: visible;} +#privPackageList td.package-column {padding: 20px 16px; background-color: #FFF;} +#privPackageList div.package {display: inline-block; width: calc(25% - 5px); padding: 10px 0;} +#privPackageList .package + .package {padding: 0;} +#privPackageList .package .check-all {display: inline-block; height: 20px;} +#privPackageList .package input {margin-top: 0px; margin-left: 0px; left: 0px; height: 20px;} +#privPackageList .package label {display: inline-block;} +#privPackageList .privs, #privPackageList .menus-privs {padding: 20px 20px 0 20px; margin: 10px 0px;border: 1px solid #D9DBE1; background-color: #F9FAFB; position: relative; display: block; max-width: unset; box-shadow: unset; z-index: 1;} +#privPackageList .menus-privs {margin-top: 35px; border-style: dashed;} +#privPackageList .priv-toggle.icon {width: 12px; height: 12px; position: relative; display: inline-block; color: #9EA3B0; margin-left: 4px; line-height: 12px; cursor: pointer; top: 2px;} +#privPackageList .priv-toggle.icon:before {content: "\e925"; font-size: 12px; min-width: unset; transform: scale(0.5); font-weight: 900;} +#privPackageList .priv-toggle.icon:after {position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; display: inline-block; content: ""; border: 1px solid; border-radius: 10%;} +#privPackageList .priv-toggle.icon.open:before {content: "\e926";} +#privPackageList .group-item .checkbox-inline label {padding-left: 0} +#privPackageList .group-item {display: inline-block; width: 158px; margin-bottom: 15px;} +#privPackageList .group-item > .checkbox-primary {display: inline-block;} +#privPackageList .priv-toggle.icon {width: 12px; height: 12px; position: relative; display: inline-block; color: #9EA3B0; margin-left: 4px; line-height: 12px;} +#privPackageList .priv-toggle.icon:before {content: "\e925"; font-size: 12px; min-width: unset; transform: scale(0.5); font-weight: 900;} +#privPackageList .priv-toggle.icon:after {position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; display: inline-block; content: ""; border: 1px solid; border-radius: 10%;} +#privPackageList .priv-toggle.icon.open:before {content: "\e926";} diff --git a/module/group/css/privbymodule.ui.css b/module/group/css/privbymodule.ui.css deleted file mode 100644 index a0c6965a3c..0000000000 --- a/module/group/css/privbymodule.ui.css +++ /dev/null @@ -1,2 +0,0 @@ -#privByModuleForm .form-row select {height: 250px !important; background-image: none;} -#privByModuleForm .form-row select option:hover {background: #2e7fff;} diff --git a/module/group/js/managepriv.ui.js b/module/group/js/managepriv.ui.js new file mode 100644 index 0000000000..2630e96eff --- /dev/null +++ b/module/group/js/managepriv.ui.js @@ -0,0 +1,124 @@ +$(function() +{ + positionBtn(); + + $(document).on('change', '#privList > tbody > tr > th .check-all', checkAllChange); + $(document).on('change', '#privPackageList > tbody > tr .check-all', checkAllChange); +}) + +/** + * When check all change. + * + * @access public + * @return void + */ +function checkAllChange() +{ + var id = $(this).find('input[type=checkbox]').attr('id'); + var checked = $(this).find('input[type=checkbox]').prop('checked'); + + if(id == 'allChecker') + { + $('input[type=checkbox]').prop('checked', checked); + + if(checked) $('input[type=checkbox]').attr('checked', checked); + if(!checked) $('input[type=checkbox]').removeAttr('checked'); + $('tbody .checkbox-indeterminate-block').removeClass('checkbox-indeterminate-block'); + } + else if($(this).closest('#privList').length > 0) + { + var moduleName = $(this).closest('th').attr('data-module'); + var packageID = $(this).closest('th').hasClass('package') ? $(this).closest('th').attr('data-package') : ''; + var $children = $(this).closest('th').hasClass('package') ? $(this).closest('tbody').find('[data-divID=' + moduleName + packageID +']') : $(this).closest('tbody').find('[data-module=' + moduleName +']'); + + $children.find('input[type=checkbox]').prop('checked', checked); + $children.find('.checkbox-indeterminate-block').removeClass('checkbox-indeterminate-block'); + + if(checked) $children.find('input[type=checkbox]').attr('checked', checked); + if(!checked) $children.find('input[type=checkbox]').removeAttr('checked'); + + // changeParentChecked($(this), moduleName, packageID); + } + else if($(this).closest('#privPackageList').length > 0) + { + var moduleName = $(this).parent().attr('data-module'); + var packageID = $(this).parent().hasClass('package') ? $(this).parent().attr('data-package') : ''; + var $children = $(this).parent().hasClass('package') ? $(this).closest('td').find('[data-divID=' + moduleName + packageID + ']') : $(this).closest('tbody').find('[data-module=' + moduleName +']'); + + $children.find('input[type=checkbox]').prop('checked', checked); + $children.find('.checkbox-indeterminate-block').removeClass('checkbox-indeterminate-block'); + + if(checked) $children.find('input[type=checkbox]').attr('checked', checked); + if(!checked) $children.find('input[type=checkbox]').removeAttr('checked'); + +// changeParentChecked($(this), moduleName, packageID); + } +// updatePrivTree(null); +} + +window.onsize = function() +{ + positionBtn(); +} + + +/** + * Change btn position. + * + * @access public + * @return void + */ +function positionBtn() +{ + let btnLeft = $('.main.main-content > table').width() - 75; + $('.main.main-content > .btn-group').css('left', btnLeft).css('visibility', 'unset'); +} + +/** + * Control the packages select control for a module. + * + * @access public + * @return void + */ +window.setModulePackages = function() +{ + const module = $(this).val(); + $('#packageBox select').addClass('hidden'); // Hide all select first. + $('#packageBox select').val(''); // Unselect all select. + $("select[data-module='" + module + "']").removeClass('hidden'); // Show the action control for current module. + + updatePrivList('module', module); +} + +/** + * Update the action box when module or package selected. + * + * @param string parentType module|package + * @param string parentList + * @access public + * @return void + */ +function updatePrivList(parentType, parentList) +{ + const selectedModule = $('#module').val(); + $.get($.createLink('group', 'ajaxGetPrivByParents', 'module=' + selectedModule + '&parentType=' + parentType + '&parentList=' + parentList), function(data) + { + $('#actions').replaceWith(data); + }) +} + +/** + * Control the actions select control for a package. + * + * @access public + * @return void + */ +function setActions() +{ + $('#actionBox select').val(''); + + var hasSelectedPackage = ',' + $('#packageBox select').not('.hidden').val().join(',') + ','; + + updatePrivList('package', hasSelectedPackage); + +} diff --git a/module/group/js/privbymodule.ui.js b/module/group/js/privbymodule.ui.js deleted file mode 100644 index e284477d21..0000000000 --- a/module/group/js/privbymodule.ui.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Control the packages select control for a module. - * - * @access public - * @return void - */ -window.setModulePackages = function() -{ - const module = $(this).val(); - $('#packageBox select').addClass('hidden'); // Hide all select first. - $('#packageBox select').val(''); // Unselect all select. - $("select[data-module='" + module + "']").removeClass('hidden'); // Show the action control for current module. - - updatePrivList('module', module); -} - -/** - * Update the action box when module or package selected. - * - * @param string parentType module|package - * @param string parentList - * @access public - * @return void - */ -function updatePrivList(parentType, parentList) -{ - const selectedModule = $('#module').val(); - $.get($.createLink('group', 'ajaxGetPrivByParents', 'module=' + selectedModule + '&parentType=' + parentType + '&parentList=' + parentList), function(data) - { - $('#actions').replaceWith(data); - }) -} - -/** - * Control the actions select control for a package. - * - * @access public - * @return void - */ -function setActions() -{ - $('#actionBox select').val(''); - - var hasSelectedPackage = ',' + $('#packageBox select').not('.hidden').val().join(',') + ','; - - updatePrivList('package', hasSelectedPackage); - -} diff --git a/module/group/ui/managepriv.html.php b/module/group/ui/managepriv.html.php new file mode 100644 index 0000000000..dccb7970a8 --- /dev/null +++ b/module/group/ui/managepriv.html.php @@ -0,0 +1,15 @@ + + * @package group + * @link https://www.zentao.net + */ +namespace zin; + +if($type == 'byGroup') include 'privbygroup.html.php'; +if($type == 'byPackage') include 'privbypackage.html.php'; +if($type == 'byModule') include 'privbymodule.html.php'; diff --git a/module/group/ui/privbypackage.html.php b/module/group/ui/privbypackage.html.php new file mode 100644 index 0000000000..e9550c086e --- /dev/null +++ b/module/group/ui/privbypackage.html.php @@ -0,0 +1,450 @@ + + * @package group + * @link https://www.zentao.net + */ +namespace zin; +if($group->role == 'limited') +{ +} +else +{ + $params = "type=byPackage¶m=$groupID&menu=%s&version=$version"; + $mainNavItems = null; + $i = 0; + $dropDownItems = array(); + foreach($lang->mainNav as $module => $title) + { + if(!is_string($title)) continue; + + $i++; + if($i >= $config->group->maxToolBarCount) + { + $dropDownItems[] = array + ( + 'text' => strip_tags(substr($title, 0, strpos($title, '|'))), + 'url' => inlink('managePriv', sprintf($params, $module)), + 'class' => $menu == $module ? 'active' : '' + ); + } + } + + $i = 0; + foreach($lang->mainNav as $module => $title) + { + if(!is_string($title) || $i >= $config->group->maxToolBarCount) continue; + + $i++; + if($i == $config->group->maxToolBarCount) + { + $mainNavItems[] = li + ( + setClass('nav-item'), + dropdown + ( + btn( + setClass('ghost btn square btn-default'), + $lang->group->more + ), + set::items($dropDownItems), + ) + ); + } + else + { + $mainNavItems[] = li + ( + setClass('nav-item'), + a + ( + setClass($menu == $module ? 'active' : ''), + set::href(inlink('managePriv', sprintf($params, $module))), + strip_tags(substr($title, 0, strpos($title, '|'))) + ) + ); + } + } + + $getMethodItems = function($moduleName, $groupPrivs) + { + global $lang; + $methodItems = array(); + foreach($lang->$moduleName->menus as $method => $name) + { + $methodItems[]= div + ( + setClass('group-item menus-item'), + set('data-id', "{$moduleName}-{$method}"), + set('data-module', $moduleName), + set('data-package', 0), + set('data-divID', "{$moduleName}0"), + checkbox + ( + set::name("actions[{$moduleName}]"), + set::value(isset($groupPrivs[$moduleName][$method]) ? $groupPrivs[$moduleName][$method] : ''), + setID("actions[{$moduleName}]{$method}"), + set::text($name), + set('data-id', "{$moduleName}-{$method}'"), + ) + ); + } + return $methodItems; + }; + + function getPrivsItems($privs, $moduleName) + { + global $lang; + $privsBox = array(); + foreach($privs as $privID => $priv) + { + if(!empty($lang->$moduleName->menus) && ($priv->method == 'browse' or in_array($priv->method, array_keys($lang->$moduleName->menus)))) continue; + $privsBox[] = div + ( + setClass('group-item'), + set('data-id', zget($priv, 'id', 0)), + set('data-module', $moduleName), + set('data-package', $packageID), + set('data-divID', "{$moduleName}{$packageID}"), + div + ( + setClass('checkbox-primary'), + checkbox + ( + set::name("actions[{$priv->module}]"), + setID("actions[{$priv->module}]{$priv->method}"), + set::value(isset($groupPrivs[$priv->module][$priv->method]) ? $priv->method : ''), + set::text($priv->name), + set('data-id', $priv->action), + ) + ) + ); + } + + return $privsBox; + }; + + $getPackagesBox = function($moduleName, $packages, $groupPrivs, $privPackages, $selectPrivs) + { + global $lang; + $packagesBox = array(); + foreach($packages as $packageID => $privs) + { + $packagePrivs = count($privs); + $packageSelect = $selectPrivs[$moduleName][$packageID]; + if(isset($lang->$moduleName->menus)) + { + $menusPrivs = count($lang->$moduleName->menus); + $menusSelect = count(array_intersect(array_keys($lang->$moduleName->menus), array_keys(zget($groupPrivs, $moduleName, array())))); + } + + $packagesBox[] = array + ( + div + ( + setClass('package'), + set('data-module', $moduleName), + set('data-package', $packageID), + set('all-privs', $packagePrivs), + set('select-privs', $packageSelect), + set('data-divID', "{$moduleName}{$packageID}"), + div + ( + setClass('checkbox-primary checkbox-inline checkbox-left check-all'), + checkbox + ( + setID("allCheckerModule{$moduleName}Package{$packageID}"), + set::value(1), + set::checked($packagePrivs == $packageSelect), + set::text(zget($privPackages, $packageID, $lang->group->other)), + set::labelClass(!empty($packageSelect) && $packagePrivs != $packageSelect ? 'text-left checkbox-indeterminate-block' : 'text-left'), + ), + ), + h::i(setClass('priv-toggle icon')), + ), + div + ( + setClass('privs hidden'), + set('data-module', $moduleName), + set('data-package', $packageID), + set('data-divID', "{$moduleName}{$packageID}"), + div(setClass('arrow')), + div + ( + setClass('popover-content'), + isset($lang->$moduleName->menus) ? div + ( + setClass('group-item menus-browse'), + set('data-id', 0), + set('data-module', $moduleName), + set('data-package', 0), + set('data-divID', "{$moduleName}{$packageID}"), + div + ( + setClass('checkbox-primary checkbox-inline checkbox-left check-all'), + checkBox + ( + set::value('browse'), + set::checked($menusPrivs == $menusSelect), + set::text($lang->$moduleName->browse), + set::labelClass(!empty($menusSelect) && $menusPrivs != $menusSelect ? 'text-left checkbox-indeterminate-block' : 'text-left'), + ) + ), + h::i(setClass('priv-toggle icon')), + div + ( + setClass('menus-privs hidden'), + set('data-module', $moduleName), + set('data-package', $packageID), + set('data-divID', "{$moduleName}{$packageID}"), + div(setClass('arrow')), + div + ( + setClass('popover-content'), + $getMethodItems($moduleName, $groupPrivs) + ) + ) + ) : null, + getPrivsItems($privs, $moduleName) + ) + ) + ); + } + return $packagesBox; + }; + + $privBody = null; + foreach($privList as $moduleName => $packages) + { + if(!count((array)$packages)) continue; + $i = 1; + $modulePrivs = count($privList[$moduleName], 1) - count($selectPrivs[$moduleName], 1); + $moduleSelect = array_sum($selectPrivs[$moduleName]); + $moduleTitle = $lang->$moduleName->common; + if(in_array($moduleName, array('doc', 'api'))) $moduleTitle = $lang->$moduleName->manage; + + $privBody[] = h::tr + ( + setClass(cycle('even, bg-gray')), + h::th + ( + setClass('text-middle text-left module'), + set('data-module', $moduleName), + set('all-privs', $modulePrivs), + set('select-privs', $moduleSelect), + div + ( + setClass('checkbox-primary checkbox-inline checkbox-left check-all'), + checkbox + ( + setID("allChecker{$moduleName}"), + set::labelClass(!empty($moduleSelect) && $modulePrivs != $moduleSelect ? 'text-left checkbox-indeterminate-block' : 'text-left'), + set::value(1), + set::checked(!empty($moduleSelect) && $modulePrivs == $moduleSelect), + set::text($moduleTitle), + ), + ) + ), + h::td + ( + setClass('td-sm text-middle text-left package-column'), + set('data-module', $moduleName), + $getPackagesBox($moduleName, $packages, $groupPrivs, $privPackages, $selectPrivs), + ) + ); + } + + div + ( + setID('featureBar'), + menu + ( + setClass('nav nav-feature'), + li + ( + span + ( + icon('lock mr-2'), + $group->name, + ), + ), + li + ( + span + ( + set::class('text-md text-gray'), + html($lang->arrow), + ), + ), + li + ( + setClass('nav-item'), + a + ( + setClass(empty($menu) ? 'active' : ''), + set::href(inlink('managePriv'), sprintf($params, '')), + span($lang->group->all) + ) + ), + $mainNavItems, + li + ( + setClass('nav-item'), + a + ( + setClass($menu == 'general' ? 'active' : ''), + set::href(inlink('managePriv'), sprintf($params, 'general')), + span($lang->group->general) + ) + ), + picker + ( + setID('versionSelect'), + set::name('version'), + set::items($this->lang->group->versions), + set::value($version), + on::change('showPriv'), + ) + ), + ); + + form + ( + setID('managePrivForm'), + div + ( + setID('mainContainer'), + setClass('flex'), + div + ( + setClass('main main-content'), + div + ( + setClass('btn-group'), + a + ( + setClass('btn switchBtn'), + set::href(inlink('managePriv', "type=byPackage¶m=$groupID&menu=$menu&version=$version")), + html(""), + ), + a + ( + setClass('btn switchBtn text-gray'), + set::href(inlink('managePriv', "type=byGroup¶m=$groupID&menu=$menu&version=$version")), + html(""), + ), + ), + h::table + ( + setID('privPackageList'), + setClass('table table-hover table-striped table-bordered'), + h::thead + ( + h::tr + ( + h::th + ( + setClass('module'), + $lang->group->module + ), + h::th + ( + setClass('package'), + $lang->privpackage->common + ) + ) + ), + h::tbody + ( + $privBody + ), + ) + ), + div + ( + setClass('side'), + div + ( + setClass('priv-panel'), + div + ( + setClass('panel-title'), + $lang->group->dependentPrivs, + icon + ( + 'help', + set('data-toggle', 'tooltip'), + set('data-title', $lang->group->dependPrivTips), + set('data-placement', 'right'), + set('data-type', 'white'), + set('data-class-name', 'text-gray border border-light w-40'), + setClass('text-gray'), + ) + ), + div + ( + setClass('panel-content'), + div + ( + setClass('menuTree depend menu-active-primary menu-hover-primary'), + ), + div + ( + setClass('table-empty-tip text-center'), + setClass(count($relatedPrivData['depend']) > 0 ? 'hidden' : ''), + span + ( + setClass('text-gray'), + $lang->noData + ), + ) + ) + ), + div + ( + setClass('priv-panel mt-m'), + div + ( + setClass('panel-title'), + $lang->group->recommendPrivs, + icon + ( + 'help', + set('data-toggle', 'tooltip'), + set('data-title', $lang->group->recommendPrivTips), + set('data-placement', 'right'), + set('data-type', 'white'), + set('data-class-name', 'text-gray border border-light w-40'), + setClass('text-gray'), + ) + ), + div + ( + setClass('panel-content'), + div + ( + setClass('menuTree recommend menu-active-primary menu-hover-primary'), + ), + div + ( + setClass('table-empty-tip text-center'), + setClass(count($relatedPrivData['recommend']) > 0 ? 'hidden' : ''), + span + ( + setClass('text-gray'), + $lang->noData + ), + ) + ) + ) + ) + ), + ); +} + +/* ====== Render page ====== */ +render(); diff --git a/module/group/view/privbypackage.html.php b/module/group/view/privbypackage.html.php index 277187041d..a84bb1236e 100644 --- a/module/group/view/privbypackage.html.php +++ b/module/group/view/privbypackage.html.php @@ -135,7 +135,7 @@ -