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 @@
+ '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 @@
+ '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;
+}
?>
-
+
+
+
+
+
+ name;
+ } else {
+ // we are editing the top-level object
+ print $vv_obj-> name;
+ }
+ ?>
+
+
+
+
+ = $this->element('flash', $flashArgs); ?>
+
+ = $this->element('subnavigation', $subnav); ?>
+
+
+
+
-
= $vv_title; ?>
+
+ = $vv_title; ?>
+
+ = $vv_title; ?>
+
-
-
-
-
- info
-
-
-
-
-
+
+
+ = $this->element('flash', $flashArgs); ?>
+
-
+
+
+
+
+
+ name;
+ }
+ ?>
+
+
+
+
+ = $this->element('flash', $flashArgs); ?>
+
+ = $this->element('subnavigation', $subnav); ?>
+
+
+
+
-
= $vv_title; ?>
+
+ = $vv_title; ?>
+
+ = $vv_title; ?>
+
@@ -203,24 +238,12 @@ function _column_key($modelsName, $c, $tz=null) {
?>
-
-
-
- info
- = $b; ?>
-
-
-
-
-
-
-
- info
- = $b; ?>
-
-
-
+
+
+ = $this->element('flash', $flashArgs); ?>
+
+
= $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 @@
+
+
+
+
+ = $this->Flash->render() ?>
+
+
+
+ = $this->Alert->alert($b, 'warning') ?>
+
+
+
+
+
+ = $this->Alert->alert($b, 'warning') ?>
+
+
+
\ 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 @@
-
-" onclick="this.classList.add('hidden');">= $message ?>
-*/ ?>
-
-', $filteredMessage);
- print "";
- }
-?>
+
+ = $this->Alert->alert($message, 'warning', true) ?>
+
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"), '
', $filteredMessage);
- print "";
+ $message = h($message);
}
?>
+
+ = $this->Alert->alert($message, 'danger', true) ?>
+
+
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"), '
', $filteredMessage);
- print "";
+ $message = h($message);
}
?>
+
+ = $this->Alert->alert($message, 'information', true) ?>
+
\ 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"), '
', $filteredMessage);
- print "";
+ $message = h($message);
}
?>
+
+
+ = $this->Alert->alert($message, 'success', true) ?>
+
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 @@
+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];
+ }
+ }
+ }
+?>
+
+
+
+
+
+
+
+
+ Html->link(
+ __('match.ti.properties'),
+ [ 'controller' => 'rules',
+ 'action' => 'edit',
+ $curId
+ ],
+ ['class' => $linkClass]
+ );
+ ?>
+
+
+ Html->link(
+ __('match.ct.RuleAttributes', 2),
+ [ 'controller' => 'rule_attributes',
+ 'action' => 'index',
+ '?' => $linkFilter
+ ],
+ ['class' => $linkClass]
+ );
+ ?>
+
+
+
+
+
+
+ Html->link(
+ __('match.ti.properties'),
+ [ 'controller' => 'attribute_maps',
+ 'action' => 'edit',
+ $curId
+ ],
+ ['class' => $linkClass]
+ );
+ ?>
+
+
+ Html->link(
+ __('match.ct.AttributeMappings', 2),
+ [ 'controller' => 'attribute_mappings',
+ 'action' => 'index',
+ '?' => $linkFilter
+ ],
+ ['class' => $linkClass]
+ );
+ ?>
+
+
+
+
+
\ 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 */