Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Provide topLinks (e.g. delete) on Edit pages; Use the MenuHelper and …
…the menuAction element to handle all topLinks (CO-2512) (#46)
arlen committed Oct 27, 2022
1 parent 04dce0d commit 553d2fe
Showing 6 changed files with 262 additions and 44 deletions.
2 changes: 2 additions & 0 deletions 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',
14 changes: 5 additions & 9 deletions app/templates/AttributeMappings/columns.inc
@@ -40,14 +40,10 @@ $indexColumns = [

$topLinks = [
[
'label' => '<em class="material-icons" aria-hidden="true">file_download</em> ' .
__('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')
]
];
141 changes: 141 additions & 0 deletions app/templates/Standard/add-edit-view.php
@@ -37,6 +37,147 @@
<div class="pageTitle">
<h1><?= $vv_title; ?></h1>
</div>

<?php if(
$action == 'edit' &&
(!empty($topLinks) ||
(isset($vv_permissions['duplicate']) && $vv_permissions['duplicate']) ||
$vv_permissions['delete'])): ?>
<?php
// topLinks menu dropdown / button listing
$action_args = array();
$action_args['vv_attr_mdl'] = "Edit";
$action_args['vv_attr_id'] = $vv_obj->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 '<div id="topLinks" class="field-actions">';
print $this->element('menuAction', $action_args);
print '</div>';
}
?>
<?php endif; ?>
</div>

<?php
140 changes: 108 additions & 32 deletions app/templates/Standard/index.php
@@ -90,41 +90,117 @@ function _column_key($modelsName, $c, $tz=null) {
</div>

<?php if($vv_permissions['add']): ?>
<ul id="topLinks">
<li>
<?php
$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);
}
?>
<?= $addLink
? $this->Html->link('<em class="material-icons" aria-hidden="true">add_circle</em> ' .
__('match.op.add.a', __('match.ct.'.$vv_modelname, [1])),
$addLink,
['escape' => false])
: ""
?>
</li>
<?php
if(!empty($topLinks)) {
foreach($topLinks as $t) {
if($vv_permissions[ $t['link']['action'] ]) {
print '<li>' .
$this->Html->link(
$t['label'],
array_merge_recursive($linkFilter, $t['link']),
['escape' => false, 'class' => $t['class']]
) . '
</li>';
<?php
// topLinks menu dropdown / button listing
$action_args = array();
$action_args['vv_attr_mdl'] = "IndexTopLinks";

// 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 = ['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
);
}
}
?>
</ul>
}
}

// 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 '<div id="topLinks" class="field-actions">';
print $this->element('menuAction', $action_args);
print '</div>';
}
?>
<?php endif; ?>
</div>
<?php if(!empty($indexBanners)): ?>
8 changes: 5 additions & 3 deletions app/webroot/css/co-base.css
@@ -505,13 +505,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;
@@ -526,6 +525,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;
1 change: 1 addition & 0 deletions 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;

0 comments on commit 553d2fe

Please sign in to comment.