From 3ea6cb03c3d765dd172010191cc07e161884f4e7 Mon Sep 17 00:00:00 2001 From: Arlen Johnson <arlen@sphericalcowgroup.com> Date: Fri, 13 Jan 2023 15:40:46 -0500 Subject: [PATCH] Improve access to related models (CO-2229) (#57) * Provide tabbed subnavigation for related models. Deprecate Noty.js in favor of Boostrap Alerts. (CO-2229) * Ensure flash messages are available on all pages. (CO-2229) * Clean up information alert on reconcile page. (CO-2229) --- app/resources/locales/en_US/default.po | 3 + app/src/View/Helper/AlertHelper.php | 96 ++++++++++++++ app/templates/AttributeMappings/columns.inc | 5 + .../AttributeMappings/fields-nav.inc | 34 +++++ app/templates/AttributeMaps/fields-nav.inc | 34 +++++ app/templates/Matchgrids/configure.php | 68 ++++++---- app/templates/Matchgrids/delete.php | 2 +- app/templates/Matchgrids/manage.php | 13 +- app/templates/Matchgrids/pending.php | 13 +- app/templates/Matchgrids/reconcile.php | 12 +- app/templates/Matchgrids/select.php | 2 +- app/templates/RuleAttributes/columns.inc | 5 + app/templates/RuleAttributes/fields-nav.inc | 34 +++++ app/templates/Rules/fields-nav.inc | 34 +++++ app/templates/Standard/add-edit-view.php | 69 +++++++--- app/templates/Standard/index.php | 61 ++++++--- app/templates/element/flash.php | 44 +++++++ app/templates/element/flash/default.php | 22 +--- app/templates/element/flash/error.php | 14 +- app/templates/element/flash/information.php | 15 +-- app/templates/element/flash/success.php | 16 +-- app/templates/element/subnavigation.php | 120 ++++++++++++++++++ app/webroot/css/co-base.css | 90 +++++++++++-- app/webroot/css/co-color.css | 17 ++- 24 files changed, 690 insertions(+), 133 deletions(-) create mode 100644 app/src/View/Helper/AlertHelper.php create mode 100644 app/templates/AttributeMappings/fields-nav.inc create mode 100644 app/templates/AttributeMaps/fields-nav.inc create mode 100644 app/templates/RuleAttributes/fields-nav.inc create mode 100644 app/templates/Rules/fields-nav.inc create mode 100644 app/templates/element/flash.php create mode 100644 app/templates/element/subnavigation.php diff --git a/app/resources/locales/en_US/default.po b/app/resources/locales/en_US/default.po index 93d7b2464..c23fc6236 100644 --- a/app/resources/locales/en_US/default.po +++ b/app/resources/locales/en_US/default.po @@ -810,3 +810,6 @@ msgstr "Matchgrid Configuration" msgid "match.ti.matchgrid" msgstr "Matchgrid: {0}" + +msgid "match.ti.properties" +msgstr "Properties" diff --git a/app/src/View/Helper/AlertHelper.php b/app/src/View/Helper/AlertHelper.php new file mode 100644 index 000000000..3cb511fac --- /dev/null +++ b/app/src/View/Helper/AlertHelper.php @@ -0,0 +1,96 @@ +<?php +/** + * COmanage Match Alert Helper + * + * Portions licensed to the University Corporation for Advanced Internet + * Development, Inc. ("UCAID") under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. + * + * UCAID licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link https://www.internet2.edu/comanage COmanage Project + * @package registry + * @since COmanage Match v1.1.0 + * @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + */ + +declare(strict_types=1); + +namespace App\View\Helper; + +use \Cake\View\Helper; +use phpDocumentor\Reflection\Types\Boolean; + +/** + * Helper which will produce Bootstrap based alert + * + * @param string $message Alert message + * @param string $type Define the type of Alert. The value should be one of + * [success,warning,danger,info]. Defaults to 'warning' + * @param boolean $dismissable Can the Alert be dismissed? Defaults to false. + * @param string|null $title Title to display (typically "Success", "Error", or "Warning"). Defaults to null. + * @param boolean $dis_text_dark Disable dark-text fonts for light|info color mode. + * @return mixed - a constructed HTML block + * @since COmanage Registry v5.0.0 + */ +class AlertHelper extends Helper { + + public $helpers = ['Html']; + + public function alert( + string $message, + string $type = 'warning', + bool $dismissable = false, + string $title = null ) { + + $closeButton = ''; + $dismissableClass = ''; + if($dismissable) { + $closeButton = ' + <span class="alert-button"> + <button type="button" class="btn-close nospin" data-bs-dismiss="alert" aria-label="Close"></button> + </span> + '; + $dismissableClass = ' alert-dismissible'; + } + + $titleMarkup = ''; + if(!empty($title)) { + $titleMarkup = '<span class="alert-title-text">' . $title . '</span>'; + } + + return ' + <div class="alert alert-' . $type . $dismissableClass . ' co-alert" role="alert"> + <div class="alert-body d-flex align-items-center"> + <span class="alert-title d-flex align-items-center"> + ' . $this->getAlertIcon($type) . $titleMarkup . ' + </span> + <span class="alert-message"> + ' . $message . ' + </span> + ' . $closeButton . ' + </div> + </div> + '; + } + + public function getAlertIcon(string $type) { + switch($type) { + case('success'): return '<span class="material-icons-outlined alert-icon">check_circle</span>'; + case('info'): return '<span class="material-icons-outlined alert-icon">info</span>'; + default: return '<span class="material-icons-outlined alert-icon">report_problem</span>'; + } + } + +} \ No newline at end of file diff --git a/app/templates/AttributeMappings/columns.inc b/app/templates/AttributeMappings/columns.inc index 648857d6e..f07c34ebd 100644 --- a/app/templates/AttributeMappings/columns.inc +++ b/app/templates/AttributeMappings/columns.inc @@ -46,4 +46,9 @@ $topLinks = [ 'icon' => 'file_download', 'label' => __('match.op.AttributeMappings.install.nicknames.en') ] +]; + +$subnav = [ + 'name' => 'attribute_map', + 'active' => 'mappings' ]; \ No newline at end of file diff --git a/app/templates/AttributeMappings/fields-nav.inc b/app/templates/AttributeMappings/fields-nav.inc new file mode 100644 index 000000000..0010d4300 --- /dev/null +++ b/app/templates/AttributeMappings/fields-nav.inc @@ -0,0 +1,34 @@ +<?php + /** + * COmanage Match Attribute Mappings Edit Navigation + * + * Portions licensed to the University Corporation for Advanced Internet + * Development, Inc. ("UCAID") under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. + * + * UCAID licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link https://www.internet2.edu/comanage COmanage Project + * @package registry + * @since COmanage Match v1.1.0 + * @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + */ + +// XXX: If fields.inc can become configuration only, move the contents of this file into fields.inc +$topLinks = []; + +$subnav = [ + 'name' => 'attribute_map', + 'active' => 'mappings' +]; \ No newline at end of file diff --git a/app/templates/AttributeMaps/fields-nav.inc b/app/templates/AttributeMaps/fields-nav.inc new file mode 100644 index 000000000..87743f4a3 --- /dev/null +++ b/app/templates/AttributeMaps/fields-nav.inc @@ -0,0 +1,34 @@ +<?php + /** + * COmanage Match Attribute Maps Edit Navigation + * + * Portions licensed to the University Corporation for Advanced Internet + * Development, Inc. ("UCAID") under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. + * + * UCAID licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link https://www.internet2.edu/comanage COmanage Project + * @package registry + * @since COmanage Match v1.1.0 + * @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + */ + +// XXX: If fields.inc can become configuration only, move the contents of this file into fields.inc +$topLinks = []; + +$subnav = [ + 'name' => 'attribute_map', + 'active' => 'properties' +]; \ No newline at end of file diff --git a/app/templates/Matchgrids/configure.php b/app/templates/Matchgrids/configure.php index bd1cc45c8..b60485e26 100644 --- a/app/templates/Matchgrids/configure.php +++ b/app/templates/Matchgrids/configure.php @@ -1,38 +1,50 @@ <?php - /** - * COmanage Match Matchgrid Configure Template - * - * Portions licensed to the University Corporation for Advanced Internet - * Development, Inc. ("UCAID") under one or more contributor license agreements. - * See the NOTICE file distributed with this work for additional information - * regarding copyright ownership. - * - * UCAID licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @link http://www.internet2.edu/comanage COmanage Project - * @package match - * @since COmanage Match v1.0.0 - * @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - */ - - declare(strict_types = 1); +/** + * COmanage Match Matchgrid Configure Template + * + * Portions licensed to the University Corporation for Advanced Internet + * Development, Inc. ("UCAID") under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. + * + * UCAID licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link http://www.internet2.edu/comanage COmanage Project + * @package match + * @since COmanage Match v1.0.0 + * @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + */ + +declare(strict_types = 1); + +// $flashArgs pass banner messages to the flash element container +$flashArgs = []; +if(!empty($indexBanners)) { + $flashArgs['vv_index_banners'] = $indexBanners; +} +if(!empty($banners)) { + $flashArgs['vv_banners'] = $banners; +} ?> -<div class="titleNavContainer"> + +<div class="pageTitleContainer"> <div class="pageTitle"> <h1><?= $vv_title; ?></h1> </div> </div> +<?= $this->element('flash', $flashArgs); ?> + <?= __('match.meta.version', [chop(file_get_contents(CONFIG . DS . "VERSION"))]); ?> <section class="inner-content"> diff --git a/app/templates/Matchgrids/delete.php b/app/templates/Matchgrids/delete.php index 72ec888df..96fba000b 100644 --- a/app/templates/Matchgrids/delete.php +++ b/app/templates/Matchgrids/delete.php @@ -28,7 +28,7 @@ declare(strict_types = 1); ?> -<div class="titleNavContainer"> +<div class="pageTitleContainer"> <div class="pageTitle"> <h1><?= $vv_title; ?></h1> </div> diff --git a/app/templates/Matchgrids/manage.php b/app/templates/Matchgrids/manage.php index 9ca7c5cc6..e6f62b9d1 100644 --- a/app/templates/Matchgrids/manage.php +++ b/app/templates/Matchgrids/manage.php @@ -26,8 +26,17 @@ */ declare(strict_types = 1); + +// $flashArgs pass banner messages to the flash element container +$flashArgs = []; +if(!empty($indexBanners)) { + $flashArgs['vv_index_banners'] = $indexBanners; +} +if(!empty($banners)) { + $flashArgs['vv_banners'] = $banners; +} ?> -<div class="titleNavContainer"> +<div class="pageTitleContainer"> <div class="pageTitle"> <h1><?= $vv_title; ?></h1> </div> @@ -63,6 +72,8 @@ </p> <?php endif; ?> + <?= $this->element('flash', $flashArgs); ?> + <!-- Matchgrid Management --> <div id="matchgrid-management" class="call-to-action-blocks"> <div class="call-to-action"> diff --git a/app/templates/Matchgrids/pending.php b/app/templates/Matchgrids/pending.php index bb3e6aacb..95d3e3c72 100644 --- a/app/templates/Matchgrids/pending.php +++ b/app/templates/Matchgrids/pending.php @@ -26,14 +26,25 @@ */ declare(strict_types = 1); + +// $flashArgs pass banner messages to the flash element container +$flashArgs = []; +if(!empty($indexBanners)) { + $flashArgs['vv_index_banners'] = $indexBanners; +} +if(!empty($banners)) { + $flashArgs['vv_banners'] = $banners; +} ?> -<div class="titleNavContainer"> +<div class="pageTitleContainer"> <div class="pageTitle"> <h1><?= $vv_title; ?></h1> </div> </div> +<?= $this->element('flash', $flashArgs); ?> + <h2><?= __('match.rs.pending', [count($vv_pending)]); ?></h2> <table> diff --git a/app/templates/Matchgrids/reconcile.php b/app/templates/Matchgrids/reconcile.php index 0f401fd0a..98672707e 100644 --- a/app/templates/Matchgrids/reconcile.php +++ b/app/templates/Matchgrids/reconcile.php @@ -84,13 +84,23 @@ } } $canAttr = array_merge($canAttr, $tempArr); + + // $flashArgs pass banner messages to the flash element container + $flashArgs = []; + if(!empty($indexBanners)) { + $flashArgs['vv_index_banners'] = $indexBanners; + } + if(!empty($banners)) { + $flashArgs['vv_banners'] = $banners; + } ?> -<div class="titleNavContainer"> +<div class="pageTitleContainer"> <div class="pageTitle"> <h1><?= __('match.op.reconcile.requests'); ?></h1> </div> </div> +<?= $this->element('flash', $flashArgs); ?> <div class="view-controls"> <span class="view-controls-title"><?= __('match.op.highlight'); ?></span> <div class="form-check form-check-inline"> diff --git a/app/templates/Matchgrids/select.php b/app/templates/Matchgrids/select.php index fbe116a5d..b4b7ff3eb 100644 --- a/app/templates/Matchgrids/select.php +++ b/app/templates/Matchgrids/select.php @@ -29,7 +29,7 @@ use \App\Lib\Enum\PermissionEnum; ?> -<div class="titleNavContainer"> +<div class="pageTitleContainer"> <div class="pageTitle"> <h1><?= $vv_title; ?></h1> </div> diff --git a/app/templates/RuleAttributes/columns.inc b/app/templates/RuleAttributes/columns.inc index d9788b62e..bace8c84e 100644 --- a/app/templates/RuleAttributes/columns.inc +++ b/app/templates/RuleAttributes/columns.inc @@ -42,4 +42,9 @@ $indexColumns = [ 'class' => 'SearchTypeEnum', 'sortable' => true ] +]; + +$subnav = [ + 'name' => 'rule', + 'active' => 'attributes' ]; \ No newline at end of file diff --git a/app/templates/RuleAttributes/fields-nav.inc b/app/templates/RuleAttributes/fields-nav.inc new file mode 100644 index 000000000..448d29bf6 --- /dev/null +++ b/app/templates/RuleAttributes/fields-nav.inc @@ -0,0 +1,34 @@ +<?php + /** + * COmanage Match Rules Attributes Edit Navigation + * + * Portions licensed to the University Corporation for Advanced Internet + * Development, Inc. ("UCAID") under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. + * + * UCAID licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link https://www.internet2.edu/comanage COmanage Project + * @package registry + * @since COmanage Match v1.1.0 + * @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + */ + +// XXX: If fields.inc can become configuration only, move the contents of this file into fields.inc +$topLinks = []; + +$subnav = [ + 'name' => 'rule', + 'active' => 'attributes' +]; \ No newline at end of file diff --git a/app/templates/Rules/fields-nav.inc b/app/templates/Rules/fields-nav.inc new file mode 100644 index 000000000..dc914bfec --- /dev/null +++ b/app/templates/Rules/fields-nav.inc @@ -0,0 +1,34 @@ +<?php + /** + * COmanage Match Rules Edit Navigation + * + * Portions licensed to the University Corporation for Advanced Internet + * Development, Inc. ("UCAID") under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. + * + * UCAID licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link https://www.internet2.edu/comanage COmanage Project + * @package registry + * @since COmanage Match v1.1.0 + * @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + */ + +// XXX: If fields.inc can become configuration only, move the contents of this file into fields.inc +$topLinks = []; + +$subnav = [ + 'name' => 'rule', + 'active' => 'properties' +]; \ No newline at end of file diff --git a/app/templates/Standard/add-edit-view.php b/app/templates/Standard/add-edit-view.php index 69176818a..e0c8d235c 100644 --- a/app/templates/Standard/add-edit-view.php +++ b/app/templates/Standard/add-edit-view.php @@ -32,10 +32,57 @@ $action = $this->request->getParam('action'); // $this->name = Models $modelsName = $this->name; + +// Include subnavigation structures on add/edit/view pages +// XXX: if fields.inc is made configuration only, move the contents of fields-nav.inc into fields.inc +// Include subnav on all but the top-level object's Add view when subnav exists. +if((!empty($vv_primary_link_model) && $vv_primary_link_model != 'Matchgrids') || $action != 'add') { + if(file_exists(ROOT . DS . "templates" . DS . $modelsName . DS . "fields-nav.inc")) { + include(ROOT . DS . "templates" . DS . $modelsName . DS . "fields-nav.inc"); + } +} + +// $flashArgs pass banner messages to the flash element container +$flashArgs = []; +if(!empty($banners)) { + // XXX this doesn't work yet because we don't include fields.inc until later + // either create a second file to include earlier, or use a function to emit + // the fields (which would be more consistent with how Views render...) + $flashArgs['vv_banners'] = $banners; +} ?> -<div class="titleNavContainer"> + +<?php if(!empty($subnav)): ?> + <div id="subnavigation"> + <div class="supertitle"> + <h1> + <?php + if(!empty($vv_primary_link_obj) + && !empty($vv_primary_link_model) + && $vv_primary_link_model != 'Matchgrids') { + print $vv_primary_link_obj->name; + } else { + // we are editing the top-level object + print $vv_obj-> name; + } + ?> + </h1> + </div> + + <?php /* Flash Messages are placed below supertitle when subnavigation exists. */ ?> + <?= $this->element('flash', $flashArgs); ?> + + <?= $this->element('subnavigation', $subnav); ?> + </div> +<?php endif; ?> + +<div class="pageTitleContainer"> <div class="pageTitle"> - <h1><?= $vv_title; ?></h1> + <?php if(empty($subnav)): ?> + <h1><?= $vv_title; ?></h1> + <?php else: ?> + <h2><?= $vv_title; ?></h2> + <?php endif; ?> </div> <?php if( @@ -180,20 +227,10 @@ <?php endif; ?> </div> -<?php -// XXX this doesn't work yet because we don't include fields.inc until later -// either create a second file to include earlier, or use a function to emit -// the fields (which would be more consistent with how Views render...) -?> -<?php if(!empty($banners)): ?> - <?php foreach($banners as $b): ?> - <div class="co-info-topbox"> - <em class="material-icons">info</em> - <?php print $b; ?> - </div> - <?php endforeach; // $banners ?> -<?php endif; // $banners ?> - +<?php if(empty($subnav)): ?> + <?php /* Flash Messages are placed below the main title when there's no subnavigation. */ ?> + <?= $this->element('flash', $flashArgs); ?> +<?php endif; ?> <?php // By default, the form will POST to the current controller diff --git a/app/templates/Standard/index.php b/app/templates/Standard/index.php index edaf51630..592154357 100644 --- a/app/templates/Standard/index.php +++ b/app/templates/Standard/index.php @@ -83,10 +83,45 @@ function _column_key($modelsName, $c, $tz=null) { // Otherwise look for the general key return __('match.fd.'.$c); } + +// $flashArgs pass banner messages to the flash element container +$flashArgs = []; +if(!empty($indexBanners)) { + $flashArgs['vv_index_banners'] = $indexBanners; +} +if(!empty($banners)) { + $flashArgs['vv_banners'] = $banners; +} ?> -<div class="titleNavContainer"> + +<?php if(!empty($subnav)): ?> + <div id="subnavigation"> + <div class="supertitle"> + <h1> + <?php + if(!empty($vv_primary_link_obj) + && !empty($vv_primary_link_model) + && $vv_primary_link_model != 'Matchgrids') { + print $vv_primary_link_obj->name; + } + ?> + </h1> + </div> + + <?php /* Flash Messages are placed below supertitle when subnavigation exists. */ ?> + <?= $this->element('flash', $flashArgs); ?> + + <?= $this->element('subnavigation', $subnav); ?> + </div> +<?php endif; ?> + +<div class="pageTitleContainer"> <div class="pageTitle"> - <h1><?= $vv_title; ?></h1> + <?php if(empty($subnav)): ?> + <h1><?= $vv_title; ?></h1> + <?php else: ?> + <h2><?= $vv_title; ?></h2> + <?php endif; ?> </div> <?php if($vv_permissions['add']): ?> @@ -203,24 +238,12 @@ function _column_key($modelsName, $c, $tz=null) { ?> <?php endif; ?> </div> -<?php if(!empty($indexBanners)): ?> - <?php foreach($indexBanners as $b): ?> - <div class="co-info-topbox"> - <em class="material-icons">info</em> - <?= $b; ?> - </div> - <?php endforeach; // $indexBanners ?> -<?php endif; // $indexBanners ?> - -<?php if(!empty($banners)): ?> - <?php foreach($banners as $b): ?> - <div class="co-info-topbox"> - <em class="material-icons">info</em> - <?= $b; ?> - </div> - <?php endforeach; // $banners ?> -<?php endif; // $banners ?> +<?php if(empty($subnav)): ?> + <?php /* Flash Messages are placed below the main title when there's no subnavigation. */ ?> + <?= $this->element('flash', $flashArgs); ?> +<?php endif; ?> + <!-- Search block --> <?php if(!empty($enableSearch)): ?> <?= $this->element('search'); ?> diff --git a/app/templates/element/flash.php b/app/templates/element/flash.php new file mode 100644 index 000000000..d8e2e3b0f --- /dev/null +++ b/app/templates/element/flash.php @@ -0,0 +1,44 @@ +<?php + /* + * COmanage Match Flash Message Container + * + * Portions licensed to the University Corporation for Advanced Internet + * Development, Inc. ("UCAID") under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. + * + * UCAID licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link https://www.internet2.edu/comanage COmanage Project + * @package registry + * @since COmanage Registry v1.1.0 + * @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + */ +?> + +<!-- Flash Messages and defined Info Banners --> +<div class="alert-container" id="flash-messages"> + <?= $this->Flash->render() ?> + + <?php if(!empty($vv_index_banners)): ?> + <?php foreach($vv_index_banners as $b): ?> + <?= $this->Alert->alert($b, 'warning') ?> + <?php endforeach; // $vv_index_banners ?> + <?php endif; // $vv_index_banners ?> + + <?php if(!empty($vv_banners)): ?> + <?php foreach($vv_banners as $b): ?> + <?= $this->Alert->alert($b, 'warning') ?> + <?php endforeach; // $vv_banners ?> + <?php endif; // $vv_banners ?> +</div> \ No newline at end of file diff --git a/app/templates/element/flash/default.php b/app/templates/element/flash/default.php index 9f1622fb8..e4cf46da5 100644 --- a/app/templates/element/flash/default.php +++ b/app/templates/element/flash/default.php @@ -1,4 +1,3 @@ - <?php /* * COmanage Match Default Flash Template @@ -26,26 +25,11 @@ * @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) */ - // Reference examples from Cake 3 - $class = 'message'; - if (!empty($params['class'])) { - $class .= ' ' . $params['class']; - } if (!isset($params['escape']) || $params['escape'] !== false) { $message = h($message); } ?> -<?php /* -<div class="<?= h($class) ?>" onclick="this.classList.add('hidden');"><?= $message ?></div> -*/ ?> - -<?php - if(!empty($message)) { - // Strip tags then escape quotes before handing Flash message to noty.js - $filteredMessage = filter_var(filter_var($message,FILTER_SANITIZE_STRING,FILTER_FLAG_NO_ENCODE_QUOTES),FILTER_SANITIZE_ADD_SLASHES); - // Replace all newlines with html breaks - $filteredMessage = str_replace(array("\r", "\n"), '<br/>', $filteredMessage); - print "<script>generateFlash('" . $filteredMessage . "', '" . $class . "');</script>"; - } -?> +<?php if(!empty($message)): ?> + <?= $this->Alert->alert($message, 'warning', true) ?> +<?php endif; ?> diff --git a/app/templates/element/flash/error.php b/app/templates/element/flash/error.php index 70ef60569..0e964314c 100644 --- a/app/templates/element/flash/error.php +++ b/app/templates/element/flash/error.php @@ -26,15 +26,11 @@ */ if (!isset($params['escape']) || $params['escape'] !== false) { - $message = h($message); // XXX probably redundant - } - - if(!empty($message)) { - // Strip tags then escape quotes before handing Flash message to noty.js - $filteredMessage = filter_var(filter_var($message,FILTER_SANITIZE_STRING,FILTER_FLAG_NO_ENCODE_QUOTES),FILTER_SANITIZE_ADD_SLASHES); - // Replace all newlines with html breaks - $filteredMessage = str_replace(array("\r", "\n"), '<br/>', $filteredMessage); - print "<script>generateFlash('<span class=\"material-icons\">error</span>" . $filteredMessage . "', 'error');</script>"; + $message = h($message); } ?> +<?php if(!empty($message)): ?> + <?= $this->Alert->alert($message, 'danger', true) ?> +<?php endif; ?> + diff --git a/app/templates/element/flash/information.php b/app/templates/element/flash/information.php index 62bdcd5bd..a2aa4cb2a 100644 --- a/app/templates/element/flash/information.php +++ b/app/templates/element/flash/information.php @@ -24,17 +24,12 @@ * @since COmanage Match v1.0.0 * @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) */ - + if (!isset($params['escape']) || $params['escape'] !== false) { - $message = h($message); // XXX probably redundant - } - - if(!empty($message)) { - // Strip tags then escape quotes before handing Flash message to noty.js - $filteredMessage = filter_var(filter_var($message,FILTER_SANITIZE_STRING,FILTER_FLAG_NO_ENCODE_QUOTES),FILTER_SANITIZE_ADD_SLASHES); - // Replace all newlines with html breaks - $filteredMessage = str_replace(array("\r", "\n"), '<br/>', $filteredMessage); - print "<script>generateFlash('<span class=\"material-icons\">info</span>" . $filteredMessage . "', 'information');</script>"; + $message = h($message); } ?> +<?php if(!empty($message)): ?> + <?= $this->Alert->alert($message, 'information', true) ?> +<?php endif; ?> \ No newline at end of file diff --git a/app/templates/element/flash/success.php b/app/templates/element/flash/success.php index 6ac190ddf..43bbe2af1 100644 --- a/app/templates/element/flash/success.php +++ b/app/templates/element/flash/success.php @@ -24,16 +24,12 @@ * @since COmanage Match v1.0.0 * @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) */ - + if (!isset($params['escape']) || $params['escape'] !== false) { - $message = h($message); // XXX probably redundant - } - - if(!empty($message)) { - // Strip tags then escape quotes before handing Flash message to noty.js - $filteredMessage = filter_var(filter_var($message,FILTER_SANITIZE_STRING,FILTER_FLAG_NO_ENCODE_QUOTES),FILTER_SANITIZE_ADD_SLASHES); - // Replace all newlines with html breaks - $filteredMessage = str_replace(array("\r", "\n"), '<br/>', $filteredMessage); - print "<script>generateFlash('<span class=\"material-icons\">check_circle</span>" . $filteredMessage . "', 'success');</script>"; + $message = h($message); } ?> + +<?php if(!empty($message)): ?> + <?= $this->Alert->alert($message, 'success', true) ?> +<?php endif; ?> diff --git a/app/templates/element/subnavigation.php b/app/templates/element/subnavigation.php new file mode 100644 index 000000000..6120d0058 --- /dev/null +++ b/app/templates/element/subnavigation.php @@ -0,0 +1,120 @@ +<?php +/* + * COmanage Match Subnavigation Tabs Element + * + * Portions licensed to the University Corporation for Advanced Internet + * Development, Inc. ("UCAID") under one or more contributor license agreements. + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. + * + * UCAID licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link http://www.internet2.edu/comanage COmanage Project + * @package match + * @since COmanage Match v1.1.0 + * @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + */ + use Cake\View\Helper; + + $linkFilter = []; + $curId = NULL; + $curController = $this->request->getParam('controller'); + $curAction = $this->request->getParam('action'); + + if(!empty($vv_primary_link) && !empty($this->request->getQuery($vv_primary_link))) { + // This will work for most top-level index views + $curId = $this->request->getQuery($vv_primary_link); + $linkFilter = [$vv_primary_link => $curId]; + } elseif (!empty($vv_obj)) { + // This will work for most top-level edit views + $curId = $vv_obj->id; + // XXX This is brittle. If we extend the subnav more generally, support this better from the controller. + if ($curController == 'Rules' && $curAction == 'edit') { + $linkFilter = ['rule_id' => $vv_obj->id]; + } elseif ($curController == 'AttributeMaps' && $curAction == 'edit') { + $linkFilter = ['attribute_map_id' => $vv_obj->id]; + } else { + if(!empty($vv_primary_link) && !empty($vv_primary_link_obj->id)) { + $curId = $vv_primary_link_obj->id; + $linkFilter = [$vv_primary_link => $vv_primary_link_obj->id]; + } + } + } +?> + +<!-- Top-Level Subnavigation Tabs --> +<nav id="cm-<?= $name ?>-subnav-tabs" class="cm-subnav-tabs"> + <ul class="nav nav-tabs"> + + <?php if ($name == 'rule'): ?> + <!-- Rule Subnavigation --> + <li class="nav-item"> + <?php + $linkClass = ($active == 'properties') ? 'nav-link active' : 'nav-link'; + print $this->Html->link( + __('match.ti.properties'), + [ 'controller' => 'rules', + 'action' => 'edit', + $curId + ], + ['class' => $linkClass] + ); + ?> + </li> + <li class="nav-item"> + <?php + $linkClass = ($active == 'attributes') ? 'nav-link active' : 'nav-link'; + print $this->Html->link( + __('match.ct.RuleAttributes', 2), + [ 'controller' => 'rule_attributes', + 'action' => 'index', + '?' => $linkFilter + ], + ['class' => $linkClass] + ); + ?> + </li> + <?php endif; ?> + + <?php if ($name == 'attribute_map'): ?> + <!-- Attribute Maps Subnavigation --> + <li class="nav-item"> + <?php + $linkClass = ($active == 'properties') ? 'nav-link active' : 'nav-link'; + print $this->Html->link( + __('match.ti.properties'), + [ 'controller' => 'attribute_maps', + 'action' => 'edit', + $curId + ], + ['class' => $linkClass] + ); + ?> + </li> + <li class="nav-item"> + <?php + $linkClass = ($active == 'mappings') ? 'nav-link active' : 'nav-link'; + print $this->Html->link( + __('match.ct.AttributeMappings', 2), + [ 'controller' => 'attribute_mappings', + 'action' => 'index', + '?' => $linkFilter + ], + ['class' => $linkClass] + ); + ?> + </li> + <?php endif; ?> + + </ul> +</nav> \ No newline at end of file diff --git a/app/webroot/css/co-base.css b/app/webroot/css/co-base.css index d17351bd5..47c5fc25b 100644 --- a/app/webroot/css/co-base.css +++ b/app/webroot/css/co-base.css @@ -341,20 +341,61 @@ body.logged-in #top-menu { margin: 0 4px 0 -10px; vertical-align: text-bottom; } +/* ALERT MESSAGES */ +#flash-messages { + margin: 0; +} +.co-alert { + margin: 0 auto 1em; + border-radius: 0; +} +.co-alert.alert-success { + background-color: var(--cmg-color-green-008); + border-color: var(--cmg-color-green-009); +} +.co-alert.alert-warning { + background-color: var(--cmg-color-yellow-001); + color: var(--cmg-color-yellow-002); + border-color: var(--cmg-color-yellow-003); +} +.co-alert.alert-danger { + background-color: var(--cmg-color-red-001); + color: var(--cmg-color-red-006); + border-color: var(--cmg-color-red-007); +} +.co-alert.alert-information { + background-color: var(--cmg-color-blue-006); + color: var(--cmg-color-blue-007); + border-color: var(--cmg-color-blue-001); +} +.co-alert .alert-icon { + margin-right: 0.1rem; +} +.co-alert .alert-title-text { + margin-right: 0.25em; +} +.co-alert .alert-body { + gap: 1em; +} +/* Alerts in the add-edit form: */ +ul.form-list li.alert-banner { + display: block; + padding: 0; +} +ul.form-list li.alert-banner .co-alert { + margin: 0; + border: none; +} /* General icon and box styling */ .co-info { /* info icon */ float: left; margin: 0.3em 0.3em 0 0; } -.co-alert { /* alert icon */ - float:left; - margin:0 7px 20px 0; -} .co-info-topbox { clear: both; padding: 1em; - background-color: var(--cmg-color-yellow-002); - border: 1px solid var(--cmg-color-lightgray-005); + background-color: var(--cmg-color-yellow-001); + border: 1px solid var(--cmg-color-yellow-003); margin-bottom: 1em; } #lastLogin p { @@ -480,7 +521,7 @@ body.logged-in #top-menu { #breadcrumbs { font-size: 0.9em; } -.titleNavContainer { +.pageTitleContainer { display: flex; justify-content: space-between; margin: 1em 0 0.5em; @@ -502,6 +543,31 @@ body.logged-in #top-menu { .pageTitle .archived { background-color: var(--cmg-color-gray-003); } +/* SUBNAVIGATION & TABS */ +.supertitle { + padding: 1em 0; +} +#subnavigation nav { + padding: 0.5em 0; +} +.cm-subnav-tabs .nav-link { + text-transform: uppercase; + padding: 1em 1.5em; + color: var(--cmg-color-blue-001); +} +.cm-subnav-links .nav-link.active { + color: var(--cmg-color-gray-001); +} +.cm-subnav-links ul.list-inline { + margin: 0.5em 0 0 0; + font-size: 0.9em; +} +.cm-subnav-links .list-inline-item { + margin: 0; +} +.cm-subnav-links .list-inline-item a.nav-link { + padding: 0 1.5em 0.5em 0; +} /* TOP CONTENT LINKS (contextual) */ #topLinks { margin: 1.5em 0 -1.5em 0; @@ -608,7 +674,7 @@ body.logged-in #top-menu { } .top-search input[type=text]:focus, .side-search input[type=text]:focus { - background-color: var(--cmg-color-yellow-003); + background-color: var(--cmg-color-yellow-004); } .top-search .submit-button, .top-search .clear-button, @@ -731,7 +797,7 @@ body.logged-in #top-menu { } #reconcile-table.view-mode-diff td.diff, #reconcile-table.view-mode-both td.diff { - background-color: var(--cmg-color-yellow-003); + background-color: var(--cmg-color-yellow-004); } #reconcile-table tr.defined-attr th { background-color: var(--cmg-color-lightgray-001); @@ -993,7 +1059,7 @@ ul.form-list input[type="password"] { ul.form-list input[type="text"]:focus, ul.form-list input[type="number"]:focus, ul.form-list input[type="password"]:focus { - background-color: var(--cmg-color-yellow-003); + background-color: var(--cmg-color-yellow-004); } ul.form-list select { font-size: 0.9em; @@ -1409,8 +1475,8 @@ td.indented { border: 1px solid var(--cmg-color-blue-006); } .bg-outline-danger { - color: var(--cmg-color-red-005);; - border: 1px solid var(--cmg-color-red-005);; + color: var(--cmg-color-red-005); + border: 1px solid var(--cmg-color-red-005); } .bg-outline-success { color: var(--cmg-color-green-007); diff --git a/app/webroot/css/co-color.css b/app/webroot/css/co-color.css index 6f44f538c..9ca85e9d9 100644 --- a/app/webroot/css/co-color.css +++ b/app/webroot/css/co-color.css @@ -35,7 +35,8 @@ --cmg-color-blue-003: #0c75c0; /* submit buttons, .btn-primary */ --cmg-color-blue-004: #9fc6e2; /* spinner */ --cmg-color-blue-005: #53B1F4; /* link focus borders for keyboard nav */ - --cmg-color-blue-006: #17a2b8; /* info bagde */ + --cmg-color-blue-006: #d4ecff; /* alert: info backgoround */ + --cmg-color-blue-007: #0B3556; /* alert: info text color */ --cmg-color-gray-001: #222; /* body text */ --cmg-color-gray-002: #555; /* headings text */ @@ -60,16 +61,22 @@ --cmg-color-green-005: #030; /* pagination border color */ --cmg-color-green-006: #040; /* pagination button color */ --cmg-color-green-007: #28a745; /* success badge */ + --cmg-color-green-008: #b4ffba; /* alert: success */ + --cmg-color-green-009: #28a745; /* alert: success text color, badge outline */ + --cmg-color-green-010: #acf4b2; /* alert: success border color */ - --cmg-color-yellow-001: #f5f5bb; /* yellow warning */ - --cmg-color-yellow-002: #fffeb4; /* infobox informational area */ - --cmg-color-yellow-003: #ffd; /* forms: focused input; diffing fields for reconciliation */ + --cmg-color-yellow-001: #fffeb4; /* alert: warning */ + --cmg-color-yellow-002: #41412e; /* alert: warning text color */ + --cmg-color-yellow-003: #f6f5ae; /* alert: warning border color */ + --cmg-color-yellow-004: #ffd; /* forms: focused input */ - --cmg-color-red-001: #fcc; /* red warning */ + --cmg-color-red-001: #ffd4d4; /* alert: danger */ --cmg-color-red-002: #c00; /* forms: error icons */ --cmg-color-red-003: #e33; /* title for deleted/archived */ --cmg-color-red-004: #c33; /* button */ --cmg-color-red-005: #dc3545; /* danger badge */ + --cmg-color-red-006: #842029; /* alert: danger text color */ + --cmg-color-red-007: #f8cece; /* alert: danger border color */ --cmg-color-white: #fff; /* white */ --cmg-color-black: #000; /* black */