From 7cbbcd4943a1e471b86fd6f15e7b0f2160e80131 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 26 Jul 2022 12:44:20 +0200 Subject: [PATCH] PrivilegeAudit: Use the `
` tag --- library/Icinga/Web/View/PrivilegeAudit.php | 164 ++++++++++++--------- public/css/icinga/audit.less | 59 ++++---- 2 files changed, 122 insertions(+), 101 deletions(-) diff --git a/library/Icinga/Web/View/PrivilegeAudit.php b/library/Icinga/Web/View/PrivilegeAudit.php index 8af3a7489a..d447adb4ea 100644 --- a/library/Icinga/Web/View/PrivilegeAudit.php +++ b/library/Icinga/Web/View/PrivilegeAudit.php @@ -54,7 +54,7 @@ protected function auditPermission($permission) } } - $header = new HtmlElement('div'); + $header = new HtmlElement('summary'); if (! empty($refusedBy)) { $header->add([ new Icon('times-circle', ['class' => 'refused']), @@ -171,15 +171,24 @@ protected function auditPermission($permission) ])); } + if (empty($rolePaths)) { + return [ + empty($refusedBy) ? (empty($grantedBy) ? null : true) : false, + new HtmlElement( + 'div', + Attributes::create(['class' => 'inheritance-paths']), + $header->setTag('div') + ) + ]; + } + return [ empty($refusedBy) ? (empty($grantedBy) ? null : true) : false, - HtmlElement::create('div', [ - 'class' => [empty($rolePaths) ? null : 'collapsible', 'inheritance-paths'], - 'data-toggle-element' => '.collapsible-control', - 'data-no-persistence' => true, - 'data-visible-height' => 0 + HtmlElement::create('details', [ + 'class' => ['collapsible', 'inheritance-paths'], + 'data-no-persistence' => true ], [ - empty($rolePaths) ? $header : $header->addAttributes(['class' => 'collapsible-control']), + $header->addAttributes(['class' => 'collapsible-control']), $rolePaths ]) ]; @@ -202,7 +211,7 @@ protected function auditRestriction($restriction) } } - $header = new HtmlElement('div'); + $header = new HtmlElement('summary'); if (! empty($restrictedBy)) { $header->add([ new Icon('filter', ['class' => 'restricted']), @@ -277,17 +286,26 @@ protected function auditRestriction($restriction) ]); } + if (empty($roles)) { + return [ + ! empty($restrictedBy), + new HtmlElement( + 'div', + Attributes::create(['class' => 'restrictions']), + $header->setTag('div') + ) + ]; + } + return [ ! empty($restrictedBy), new HtmlElement( - 'div', + 'details', Attributes::create([ - 'class' => [empty($roles) ? null : 'collapsible', 'restrictions'], - 'data-toggle-element' => '.collapsible-control', - 'data-no-persistence' => true, - 'data-visible-height' => 0 + 'class' => ['collapsible', 'restrictions'], + 'data-no-persistence' => true ]), - empty($roles) ? $header : $header->addAttributes(['class' => 'collapsible-control']), + $header->addAttributes(['class' => 'collapsible-control']), new HtmlElement('ul', null, ...$roles) ) ]; @@ -301,39 +319,45 @@ protected function assemble() $this->addHtml(new HtmlElement( 'li', - Attributes::create([ - 'class' => 'collapsible', - 'data-toggle-element' => 'h3', - 'data-visible-height' => 0 - ]), + null, new HtmlElement( - 'h3', - null, - new HtmlElement('span', null, Text::create(t('Administrative Privileges'))), - HtmlElement::create( - 'span', - ['class' => 'audit-preview'], - $wildcardState || $unrestrictedState - ? new Icon('check-circle', ['class' => 'granted']) - : null - ) - ), - new HtmlElement( - 'ol', - Attributes::create(['class' => 'privilege-list']), + 'details', + Attributes::create([ + 'class' => ['collapsible', 'privilege-section'] + ]), new HtmlElement( - 'li', - null, - HtmlElement::create('p', ['class' => 'privilege-label'], t('Administrative Access')), - HtmlElement::create('div', ['class' => 'spacer']), - $wildcardAudit + 'summary', + Attributes::create(['class' => [ + 'collapsible-control', // Helps JS, improves performance a bit + ]]), + new HtmlElement('span', null, Text::create(t('Administrative Privileges'))), + HtmlElement::create( + 'span', + ['class' => 'audit-preview'], + $wildcardState || $unrestrictedState + ? new Icon('check-circle', ['class' => 'granted']) + : null + ), + new Icon('angles-down', ['class' => 'collapse-icon']), + new Icon('angles-left', ['class' => 'expand-icon']) ), new HtmlElement( - 'li', - null, - HtmlElement::create('p', ['class' => 'privilege-label'], t('Unrestricted Access')), - HtmlElement::create('div', ['class' => 'spacer']), - $unrestrictedAudit + 'ol', + Attributes::create(['class' => 'privilege-list']), + new HtmlElement( + 'li', + null, + HtmlElement::create('p', ['class' => 'privilege-label'], t('Administrative Access')), + HtmlElement::create('div', ['class' => 'spacer']), + $wildcardAudit + ), + new HtmlElement( + 'li', + null, + HtmlElement::create('p', ['class' => 'privilege-label'], t('Unrestricted Access')), + HtmlElement::create('div', ['class' => 'spacer']), + $unrestrictedAudit + ) ) ) )); @@ -420,30 +444,36 @@ protected function assemble() $label = [$source, ' ', HtmlElement::create('em', null, t('Module'))]; } - $this->addHtml(HtmlElement::create('li', [ - 'class' => 'collapsible', - 'data-toggle-element' => 'h3', - 'data-visible-height' => 0 - ], [ - new HtmlElement( - 'h3', - null, - HtmlElement::create('span', null, $label), - HtmlElement::create('span', ['class' => 'audit-preview'], [ - $anythingGranted ? new Icon('check-circle', ['class' => 'granted']) : null, - $anythingRefused ? new Icon('times-circle', ['class' => 'refused']) : null, - $anythingRestricted ? new Icon('filter', ['class' => 'restricted']) : null - ]) - ), - $permissionList->isEmpty() ? null : [ - HtmlElement::create('h4', null, t('Permissions')), - $permissionList - ], - $restrictionList->isEmpty() ? null : [ - HtmlElement::create('h4', null, t('Restrictions')), - $restrictionList - ] - ])); + $this->addHtml(new HtmlElement( + 'li', + null, + HtmlElement::create('details', [ + 'class' => ['collapsible', 'privilege-section'] + ], [ + new HtmlElement( + 'summary', + Attributes::create(['class' => [ + 'collapsible-control', // Helps JS, improves performance a bit + ]]), + HtmlElement::create('span', null, $label), + HtmlElement::create('span', ['class' => 'audit-preview'], [ + $anythingGranted ? new Icon('check-circle', ['class' => 'granted']) : null, + $anythingRefused ? new Icon('times-circle', ['class' => 'refused']) : null, + $anythingRestricted ? new Icon('filter', ['class' => 'restricted']) : null + ]), + new Icon('angles-down', ['class' => 'collapse-icon']), + new Icon('angles-left', ['class' => 'expand-icon']) + ), + $permissionList->isEmpty() ? null : [ + HtmlElement::create('h4', null, t('Permissions')), + $permissionList + ], + $restrictionList->isEmpty() ? null : [ + HtmlElement::create('h4', null, t('Restrictions')), + $restrictionList + ] + ]) + )); } } diff --git a/public/css/icinga/audit.less b/public/css/icinga/audit.less index 9e435f86f3..23ab5b9518 100644 --- a/public/css/icinga/audit.less +++ b/public/css/icinga/audit.less @@ -19,16 +19,17 @@ list-style-type: none; } - h3 { + .privilege-section > summary { + font-weight: @font-weight-bold; border-bottom: 1px solid @gray-light; } - h3 em, + .privilege-section > summary em, .previews em, .privilege-label em { color: @text-color-light; } - h3 em { + .privilege-section > summary em { font-weight: normal; } .privilege-label em { @@ -144,10 +145,6 @@ padding: 0; } - > li:not(.collapsed) { - margin-bottom: 2em; - } - .flex-overflow, .privilege-list > li, .inheritance-paths > ol { @@ -165,15 +162,30 @@ // https://codepen.io/unthinkingly/pen/XMwJLG overflow: hidden; } + + > details:last-child { + // The overflow above cuts off the outline of the summary otherwise + margin: -4px; + padding: 4px; + } + } + + .privilege-section { + &:not(.collapsed) { + margin-bottom: 2em; + } } - h3 { + .privilege-section > summary { display: flex; + align-items: baseline; + font-size: 1.167em; + margin: 0.556em 0 0.333em; > :first-child { flex: 3 1 auto; min-width: 20em; - max-width: 40em / 1.167em; // privilege label width + spacer width / h3 font-size + max-width: 40em / 1.167em; // privilege label width + spacer width / summary font-size } .audit-preview { @@ -181,7 +193,7 @@ .icon:before { width: 1.25em; - font-size: 1.25em / 1.167em; // privilege state icon font-size / h3 font-size + font-size: 1.25em / 1.167em; // privilege state icon font-size / summary font-size } } @@ -213,7 +225,7 @@ .restrictions { flex: 1 1 auto; - > div { + > summary { line-height: 1; overflow: hidden; @@ -325,7 +337,7 @@ #layout.minimal-layout, #layout.poor-layout { .privilege-audit { - h3 > :first-child { + .privilege-section > summary > :first-child { flex-grow: 99; } @@ -346,27 +358,6 @@ .privilege-audit .collapsible { .collapsible-control { cursor: pointer; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - } - - .collapsible-control:after { - content: "\f103"; - display: inline-block; - font-family: 'ifont'; - font-weight: normal; - padding: 0 .25em; - margin-right: .25em; - width: 1em; - opacity: .6; - float: right; - } - - &.collapsed .collapsible-control:after { - content: "\e87a"; + .user-select(none); } }