diff --git a/app/src/View/Helper/MenuHelper.php b/app/src/View/Helper/MenuHelper.php
index 580cecbb3..57ca4035c 100644
--- a/app/src/View/Helper/MenuHelper.php
+++ b/app/src/View/Helper/MenuHelper.php
@@ -53,6 +53,7 @@ public function getMenuOrder($action) {
'Edit' => 10, // edit
'Duplicate' => 15, // content_copy
'Default' => 20, // link - default starting order for arbitrary action menu items
+ 'Add' => 80, // add
'Delete' => 100 // delete
);
@@ -73,6 +74,7 @@ public function getMenuIcon($action) {
}
$icon = array(
+ 'Add' => 'add_circle',
'View' => 'visibility',
'Edit' => 'edit',
'Duplicate' => 'content_copy',
diff --git a/app/templates/AttributeMappings/columns.inc b/app/templates/AttributeMappings/columns.inc
index 0b65c5327..648857d6e 100644
--- a/app/templates/AttributeMappings/columns.inc
+++ b/app/templates/AttributeMappings/columns.inc
@@ -40,14 +40,10 @@ $indexColumns = [
$topLinks = [
[
- 'label' => 'file_download ' .
- __('match.op.AttributeMappings.install.nicknames.en'),
- 'link' => [
- 'action' => 'install',
- '?' => [
- 'mapping' => 'nicknames.en'
- ]
- ],
- 'class' => 'buildbutton'
+ 'action' => 'install',
+ 'query' => ['?' => ['mapping' => 'nicknames.en']],
+ 'class' => 'buildbutton',
+ 'icon' => 'file_download',
+ 'label' => __('match.op.AttributeMappings.install.nicknames.en')
]
];
\ 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 64fb5d644..69176818a 100644
--- a/app/templates/Standard/add-edit-view.php
+++ b/app/templates/Standard/add-edit-view.php
@@ -37,6 +37,147 @@
= $vv_title; ?>
+
+
+ id;
+ $linkArgs = [$vv_obj->id];
+
+ // $linkFilter is used for models that belong to a specific parent model (eg: co_id)
+ $linkFilter = [];
+
+
+ if(!empty($vv_primary_link) && !empty($this->request->getQuery($vv_primary_link))) {
+ $linkFilter = ['?' => [$vv_primary_link => $this->request->getQuery($vv_primary_link)]];
+ $linkArgs = array_merge_recursive($linkArgs, $linkFilter);
+ }
+
+ // Insert additional actions as per the .inc file
+ if(!empty($topLinks)) {
+ if(!isset($entity->status) || $entity->status == StatusEnum::Active) {
+ $actionOrderDefault = $this->Menu->getMenuOrder('Default');
+ foreach($topLinks as $a) {
+ if($vv_permissions[ $a['action'] ]) {
+ $actionOrder = !empty($a['order']) ? $a['order'] : $actionOrderDefault++;
+ $actionIcon = !empty($a['icon']) ? $a['icon'] : $this->Menu->getMenuIcon('Default');
+ $actionClass = !empty($a['class']) ? $a['class'] : '';
+ $actionUrl = '';
+ $actionLabel = '';
+ $actionOnClick = []; // used for confirmation dialog
+
+ // Generate the link text and urls:
+
+ // If we have a .confirm text, we need to generate a confirm dialog box
+ $confirmKey = 'match.op.'.$a['action'].'.confirm';
+ $confirmTxt = __($confirmKey);
+
+ if($confirmTxt != $confirmKey) {
+ // We found the localized string
+ $actionPostBtnArray = array_merge_recursive(['action' => $a['action']], $linkArgs);
+ $actionUrl = $this->Url->build(array_merge_recursive(['action' => $a['action']], $linkArgs));
+ // XXX should be configurable which field we put in, maybe displayField?
+ $action_args['vv_actions'][] = array(
+ 'order' => $actionOrder,
+ 'icon' => $actionIcon,
+ 'url' => 'javascript:void(0);',
+ 'label' => __('match.op.' . $a['action']),
+ 'class' => !empty($actionClass) ? $actionClass . ' nospin' : 'nospin',
+ 'onclick' => array(
+ 'dg_bd_txt' => __($confirmKey, [$entity->id]), // dialog body text
+ 'dg_post_btn_array' => $actionPostBtnArray, // postButton array for building the postButton
+ 'dg_url' => $actionUrl, // action url for building a unique ID
+ 'dg_conf_btn' => __('match.op.confirm'), // dialog confirm button text
+ 'dg_cancel_btn' => __('match.op.cancel'), // dialog cancel button text
+ 'dg_title' => __('match.op.confirm'), // dialog box title
+ 'dg_bd_txt_repl_str' => '' // dialog body text replacement strings
+ ),
+ );
+ } elseif(!empty($a['controller'])) {
+ // We're linking into a related controller
+ $queryParams = [ $tableFK => $entity->id ];
+
+ if(!empty($a['query']) && is_callable($a['query'])) {
+ $queryParams = $a['query']($vv_cur_mg->id, $entity);
+ }
+
+ $actionLabel = __('match.ct.' . Inflector::camelize(Inflector::pluralize($a['controller'])), [99]);
+ $actionUrl = $this->Url->build([
+ 'controller' => $a['controller'],
+ 'action' => $a['action'],
+ '?' => $queryParams
+ ]);
+ } else {
+ $actionLabel = __('match.op.' . $a['action']);
+ $actionUrl = $this->Url->build(
+ array_merge_recursive(['action' => $a['action']], $linkArgs)
+ );
+ }
+
+ // If a specific label is sent in the config, use it instead
+ if(!empty($a['label'])) {
+ $actionLabel = $a['label'];
+ }
+
+ // Set the action link configuration
+ $action_args['vv_actions'][] = array(
+ 'order' => $actionOrder,
+ 'icon' => $actionIcon,
+ 'url' => $actionUrl,
+ 'label' => $actionLabel,
+ 'class' => $actionClass,
+ 'onclick' => $actionOnClick
+ );
+ }
+ }
+ }
+ }
+
+ // Duplicate
+ if(isset($vv_permissions['duplicate']) && $vv_permissions['duplicate']) {
+ $action_args['vv_actions'][] = array(
+ 'order' => $this->Menu->getMenuOrder('Duplicate'),
+ 'icon' => $this->Menu->getMenuIcon('Duplicate'),
+ 'url' => $this->Url->build(array_merge_recursive(['action' => 'duplicate'], $linkArgs)),
+ 'label' => __('match.op.duplicate')
+ );
+ }
+
+ // Delete
+ if($vv_permissions['delete']) {
+ $actionPostBtnArray = array_merge_recursive(['action' => 'delete'], $linkArgs);
+ $actionUrl = $this->Url->build(array_merge_recursive(['action' => 'delete'], $linkArgs));
+ $action_args['vv_actions'][] = array(
+ 'order' => $this->Menu->getMenuOrder('Delete'),
+ 'icon' => $this->Menu->getMenuIcon('Delete'),
+ 'url' => 'javascript:void(0);',
+ 'label' => __('match.op.delete'),
+ 'class' => 'deletebutton nospin',
+ 'onclick' => array(
+ 'dg_bd_txt' => __('match.op.delete.confirm', [$vv_obj->id]),
+ 'dg_post_btn_array' => $actionPostBtnArray,
+ 'dg_url' => $actionUrl,
+ 'dg_conf_btn' => __('match.op.remove'),
+ 'dg_cancel_btn' => __('match.op.cancel'),
+ 'dg_title' => __('match.op.remove'),
+ 'dg_bd_txt_repl_str' => ''
+ ),
+ );
+ }
+
+ if(!empty($action_args['vv_actions'])) {
+ print '';
+ print $this->element('menuAction', $action_args);
+ print '
';
+ }
+ ?>
+
-
- -
- 'add']);
-
- // Allow model-specific overrides of the add link. If false,
- // the override doesn't want the add link to render.
- if(isset($addLinkFilter) && is_callable($addLinkFilter)) {
- $addLink = $addLinkFilter($addLink, $this->request);
- }
- ?>
- = $addLink
- ? $this->Html->link('add_circle ' .
- __('match.op.add.a', __('match.ct.'.$vv_modelname, [1])),
- $addLink,
- ['escape' => false])
- : ""
- ?>
-
- ' .
- $this->Html->link(
- $t['label'],
- array_merge_recursive($linkFilter, $t['link']),
- ['escape' => false, 'class' => $t['class']]
- ) . '
- ';
+ status) || $entity->status == StatusEnum::Active) {
+ $actionOrderDefault = $this->Menu->getMenuOrder('Default');
+ foreach($topLinks as $a) {
+ if($vv_permissions[ $a['action'] ]) {
+ $actionOrder = !empty($a['order']) ? $a['order'] : $actionOrderDefault++;
+ $actionIcon = !empty($a['icon']) ? $a['icon'] : $this->Menu->getMenuIcon('Default');
+ $actionClass = !empty($a['class']) ? $a['class'] : '';
+ $actionUrl = '';
+ $actionLabel = '';
+ $actionOnClick = []; // used for confirmation dialog
+
+ // Generate the link text and urls:
+
+ // If we have a .confirm text, we need to generate a confirm dialog box
+ $confirmKey = 'match.op.'.$a['action'].'.confirm';
+ $confirmTxt = __($confirmKey);
+
+ if($confirmTxt != $confirmKey) {
+ // We found the localized string
+ $actionPostBtnArray = ['action' => $a['action']];
+ $actionUrl = $this->Url->build(['action' => $a['action']]);
+ // XXX should be configurable which field we put in, maybe displayField?
+ $action_args['vv_actions'][] = array(
+ 'order' => $actionOrder,
+ 'icon' => $actionIcon,
+ 'url' => 'javascript:void(0);',
+ 'label' => __('match.op.' . $a['action']),
+ 'class' => !empty($actionClass) ? $actionClass . ' nospin' : 'nospin',
+ 'onclick' => array(
+ 'dg_bd_txt' => __($confirmKey, [$entity->id]), // dialog body text
+ 'dg_post_btn_array' => $actionPostBtnArray, // postButton array for building the postButton
+ 'dg_url' => $actionUrl, // action url for building a unique ID
+ 'dg_conf_btn' => __('match.op.confirm'), // dialog confirm button text
+ 'dg_cancel_btn' => __('match.op.cancel'), // dialog cancel button text
+ 'dg_title' => __('match.op.confirm'), // dialog box title
+ 'dg_bd_txt_repl_str' => '' // dialog body text replacement strings
+ ),
+ );
+ } elseif(!empty($a['controller'])) {
+ // We're linking into a related controller
+ $queryParams = [ $tableFK => $entity->id ];
+
+ if(!empty($a['query']) && is_callable($a['query'])) {
+ $queryParams = $a['query']($vv_cur_mg->id, $entity);
+ }
+
+ $actionLabel = __('match.ct.' . Inflector::camelize(Inflector::pluralize($a['controller'])), [99]);
+ $actionUrl = $this->Url->build([
+ 'controller' => $a['controller'],
+ 'action' => $a['action'],
+ '?' => $queryParams
+ ]);
+ } else {
+ $actionLabel = __('match.op.' . $a['action']);
+ $urlAttrs = ['action' => $a['action']];
+ $urlAttrs = array_merge_recursive($linkFilter, $urlAttrs);
+ if(!empty($a['query'])) {
+ $urlAttrs = array_merge_recursive($urlAttrs,$a['query']);
+ }
+ $actionUrl = $this->Url->build($urlAttrs);
+ }
+
+ // If a specific label is sent in the config, use it instead
+ if(!empty($a['label'])) {
+ $actionLabel = $a['label'];
}
+
+ // Set the action link configuration
+ $action_args['vv_actions'][] = array(
+ 'order' => $actionOrder,
+ 'icon' => $actionIcon,
+ 'url' => $actionUrl,
+ 'label' => $actionLabel,
+ 'class' => $actionClass,
+ 'onclick' => $actionOnClick
+ );
}
}
- ?>
-
+ }
+ }
+
+ // Add
+ $addLink = array_merge($linkFilter, ['action' => 'add']);
+
+ // Allow model-specific overrides of the add link. If false,
+ // the override doesn't want the add link to render.
+ if(isset($addLinkFilter) && is_callable($addLinkFilter)) {
+ $addLink = $addLinkFilter($addLink, $this->request);
+ }
+
+ $action_args['vv_actions'][] = array(
+ 'order' => $this->Menu->getMenuOrder('Add'),
+ 'icon' => $this->Menu->getMenuIcon('Add'),
+ 'url' => $this->Url->build($addLink),
+ 'label' => __('match.op.add.a', __('match.ct.'.$vv_modelname, [1]))
+ );
+
+ // Output the topLinks
+ if(!empty($action_args['vv_actions'])) {
+ print '';
+ print $this->element('menuAction', $action_args);
+ print '
';
+ }
+ ?>
diff --git a/app/webroot/css/co-base.css b/app/webroot/css/co-base.css
index 8c6b8f862..5d7063372 100644
--- a/app/webroot/css/co-base.css
+++ b/app/webroot/css/co-base.css
@@ -477,13 +477,12 @@ body.logged-in #top-menu {
#topLinks {
margin: 1.5em 0 -1.5em 0;
padding: 0;
- font-size: 0.9em;
}
#topLinks li {
list-style: none;
display: inline-block;
- margin: 0.5em;
- padding: 0 0.5em;
+ margin: 0.5em 0;
+ padding: 0;
}
#topLinks a {
text-decoration: none;
@@ -498,6 +497,9 @@ body.logged-in #top-menu {
#main #topLinks a:hover {
text-decoration: underline;
}
+#topLinks a.dropdown-item.deletebutton {
+ color: var(--cmg-color-red-002);
+}
/* INNER CONTENT - for non-table-based layouts */
#content {
padding: 1em 1.5em;
diff --git a/app/webroot/css/co-responsive.css b/app/webroot/css/co-responsive.css
index 4fc32f4c8..2e4da7c18 100644
--- a/app/webroot/css/co-responsive.css
+++ b/app/webroot/css/co-responsive.css
@@ -216,6 +216,7 @@
.field-actions .dropdown-menu {
position: static;
display: inline-block;
+ min-width: 0;
font-size: 0.9em;
border: none;
background-color: transparent;