Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions app/templates/Standard/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@
&& empty($action_args)
) {
$action_args = [];
$action_args['vv_attr_id'] = $vv_user['username'];
$action_args['vv_actions'] = [];

// Include the Add link to actions menu unless suppressed by the page
Expand Down Expand Up @@ -171,17 +170,18 @@
];
}
}
}

if(!empty($bulkActions)) {
$action_args['vv_bulk_actions'] = $bulkActions;
// Declare the type of actions being sent so we can produce the bulk actions switch only for top-links.
// XXX Bulk actions are currently being provided if a user has "add" permissions. Review this.
$action_args['vv_actions_type'] = 'top-links';

if(!empty($bulkActions)) {
$action_args['vv_bulk_actions'] = $bulkActions;
}
}
?>

<?php if(!empty($action_args['vv_actions']) || !empty($action_args['vv_people_picker'])): ?>
<?php if(!empty($action_args['vv_actions'])
|| !empty($action_args['vv_people_picker'])
|| !empty($action_args['vv_bulk_actions'])): ?>
<div class="field-actions top-links">
<?= $this->element('menuAction', $action_args); ?>
</div>
Expand Down
28 changes: 28 additions & 0 deletions app/templates/element/bulk/checkbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,36 @@
*/

declare(strict_types = 1);

use App\Lib\Enum\PetitionStatusEnum;

// Set up defaults
$readOnly = false;
$permissionSet = $vv_permission_set ?? null;

if(empty($entity)) {
$id = 'bulk-action-select-all';
} else {
$id = "bulk-action-id-{$entity->id}";

// Special handling for specific models
// GROUP MEMBERS
if ($entity instanceof \App\Model\Entity\GroupMember) {
// If the member came from a Group Nesting, currently no bulk actions (just delete) are allowed
if(isset($entity->group_nesting)) {
$readOnly = true;
}
}
// PETITIONS
if ($entity instanceof \App\Model\Entity\Petition) {
// Currently, we support bulk termination of Petitions.
// A petition can be terminated only if it is not in a completed status.
// Also cross-reference the $permissionsSet for the entity to see if termination is disallowed.
if(in_array($entity->status, PetitionStatusEnum::getCompleted(), true)
|| ($permissionSet[$entity->id]['terminate'] ?? null) === false) {
$readOnly = true;
}
}
}

?>
Expand All @@ -46,11 +72,13 @@
type="checkbox"
value=""
id="<?= $id ?>"
<?= $readOnly ? "disabled" : "" ?>
<?php if(!empty($entity)):?>
data-entity="<?= htmlspecialchars(json_encode($entity), ENT_QUOTES, 'UTF-8') ?>"
data-entity-id="<?= $entity->id ?>"
<?php endif ?>>
<label class="form-check-label" for="<?= $id ?>">
<?= $label ?>
<?= $readOnly ? '<em class="material-symbols-outlined read-only-icon">edit_off</em>' : '' ?>
</label>
</div>
2 changes: 1 addition & 1 deletion app/templates/element/javascript.php
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@
// Bulk edit select all checkbox
$('#bulk-action-select-all').click(function() {
if($(this).is(":checked")) {
$('table.index-table.bulk-edit-mode .form-check-input').prop('checked', true);
$('table.index-table.bulk-edit-mode .form-check-input:not(:disabled)').prop('checked', true);
} else {
$('table.index-table.bulk-edit-mode .form-check-input').prop('checked', false);
}
Expand Down
164 changes: 84 additions & 80 deletions app/templates/element/menuAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@

// If you're debugging a null $vv_actions, there's probably a permissions problem
// you need to trace
$actionsCount = count($vv_actions) + (int)!empty($vv_people_picker);
$actionsCount = count($vv_actions ?? []) + (int)!empty($vv_people_picker);
$actionsCountClass = $actionsCount > 0 ? ' actions-count-' . $actionsCount : '';
$actionsExpandedClass = ($actionsCount > 0 && $actionsCount < 4) ? ' actions-expanded' : '';
$actionsExpandedClass = ($actionsCount > 0 && $actionsCount < 5) ? ' actions-expanded' : '';
$actionsMenuClass = (!empty($vv_actions_class) ? $vv_actions_class : 'field-actions-menu')
. ' dropdown dropleft' . $actionsCountClass . $actionsExpandedClass;
$actionsMenuUid = md5($vv_attr_id);
$actionsMenuUid = md5($vv_attr_id ?? $vv_user['username']);
$actionsType = !empty($vv_actions_type) ? $vv_actions_type : 'row-actions';
$actionsTitle = !empty($vv_actions_title) ? $vv_actions_title : '';
$actionsIcon = !empty($vv_actions_icon) ? $vv_actions_icon : 'settings';
Expand All @@ -58,10 +58,12 @@ class="<?= $actionsMenuClass; ?>">
);

// Sort the actions
usort($vv_actions, function ($item1, $item2) {
if ($item1['order'] == $item2['order']) return 0;
return $item1['order'] < $item2['order'] ? -1 : 1;
});
if($actionsCount > 1) {
usort($vv_actions, function($item1, $item2) {
if($item1['order'] == $item2['order']) return 0;
return $item1['order'] < $item2['order'] ? -1 : 1;
});
}

?>
<ul id="action-list_<?= $actionsMenuUid; ?>" class="dropdown-menu nospin">
Expand All @@ -72,80 +74,82 @@ class="<?= $actionsMenuClass; ?>">
</div>
</li>
<?php endif; ?>
<?php foreach($vv_actions as $action): ?>
<?php
$actionUrl = $this->Url->build($action['url']);
$actionDataAttrs = '';
if(!empty($action['dataAttrs'])) {
foreach($action['dataAttrs'] as $dataAttr) {
$actionDataAttrs = ' ' . $dataAttr[0] . '="' . $dataAttr[1] . '"';
}
}
?>
<li class="action-list-item">
<?php $actionCssClass = (!empty($action['class'])) ? "dropdown-item " . $action['class'] : "dropdown-item"; ?>
<?php if(empty($action['confirm'])): ?>
<a class="<?= $actionCssClass; ?>" href="<?= $actionUrl ?>"<?= !(empty($actionDataAttrs)) ? $actionDataAttrs : '' ?>>
<?php if(!empty($action['icon'])): ?>
<em class="<?= !empty($action['iconClass']) ? $action['iconClass'] : 'material-symbols-outlined'; ?>" aria-hidden="true">
<?= $action['icon']; ?>
</em>
<?php endif; ?>
<span class="action-link-text"><?= $action['label']; ?></span>
</a>
<?php else: ?>
<?php
// Build a link to a confirm dialog box. If the method is "POST", the confirm button of the dialog
// will trigger the postButton built below the menu link. Otherwise, confirm will simply redirect
// to the action's URL.

$postButton = false;
$actionUid = '';
// If we use POST (such as for delete) generate the UID:
if(!empty($action['confirm']['method']) && strtolower($action['confirm']['method']) == 'post') {
$postButton = true;
$actionUid = 'action-' . md5($actionUrl);
}

// Gather the dialog text
$dialogBodyText = !empty($action['confirm']['dg_body_txt']) ? $action['confirm']['dg_body_txt'] : __d('operation','confirm.generic');
$dialogTitle = !empty($action['confirm']['dg_title']) ? $action['confirm']['dg_title'] : $action['label'];
$confirmButtonText = !empty($action['confirm']['dg_confirm_btn']) ? $action['confirm']['dg_confirm_btn'] : __d('operation','confirm');
$cancelButtonText = !empty($action['confirm']['dg_cancel_btn']) ? $action['confirm']['dg_cancel_btn'] : __d('operation','cancel');
$replacements = !empty($action['confirm']['dg_body_txt_replacements']) ? $action['confirm']['dg_body_txt_replacements'] : '';

$dg_onclick = 'javascript:jsConfirmGeneric(\''
. $dialogBodyText . '\',\'' // dialog body text
. $actionUrl . '\',\'' // URL to redirect to on confirm
. $actionUid . '\',\'' // ID of postButton element to click on confirm if not empty
. $confirmButtonText . '\',\'' // dialog confirm button text
. $cancelButtonText . '\',\'' // dialog cancel button
. $dialogTitle . '\',[\'' // dialog title
. $replacements // dialog body text replacement strings
. '\']);';

// Links that launch a dialog box should never put up a spinner.
$actionCssClass .= ' nospin';
?>
<a class="<?= $actionCssClass; ?>" href="#" onclick="<?= $dg_onclick; ?>"
data-bs-toggle="modal" data-bs-target="#dialog">
<?php if(!empty($action['icon'])): ?>
<em class="<?= !empty($action['iconClass']) ? $action['iconClass'] : 'material-symbols'; ?>" aria-hidden="true">
<?= $action['icon']; ?>
</em>
<?php endif; ?>
<?= $action['label']; ?>
</a>
<?php
// If we need a postButton, build it. It will be clicked when the modal dialog confirm button is clicked:
if($postButton) {
print $this->Form->postButton($confirmButtonText,
$action['url'], ['id' => $actionUid, 'class' => 'hidden']);
<?php if($actionsCount > 0): ?>
<?php foreach($vv_actions as $action): ?>
<?php
$actionUrl = $this->Url->build($action['url']);
$actionDataAttrs = '';
if(!empty($action['dataAttrs'])) {
foreach($action['dataAttrs'] as $dataAttr) {
$actionDataAttrs = ' ' . $dataAttr[0] . '="' . $dataAttr[1] . '"';
}
?>
</li>
<?php endif; ?>
<?php endforeach;?>
}
?>
<li class="action-list-item">
<?php $actionCssClass = (!empty($action['class'])) ? "dropdown-item " . $action['class'] : "dropdown-item"; ?>
<?php if(empty($action['confirm'])): ?>
<a class="<?= $actionCssClass; ?>" href="<?= $actionUrl ?>"<?= !(empty($actionDataAttrs)) ? $actionDataAttrs : '' ?>>
<?php if(!empty($action['icon'])): ?>
<em class="<?= !empty($action['iconClass']) ? $action['iconClass'] : 'material-symbols'; ?>" aria-hidden="true">
<?= $action['icon']; ?>
</em>
<?php endif; ?>
<span class="action-link-text"><?= $action['label']; ?></span>
</a>
<?php else: ?>
<?php
// Build a link to a confirm dialog box. If the method is "POST", the confirm button of the dialog
// will trigger the postButton built below the menu link. Otherwise, confirm will simply redirect
// to the action's URL.

$postButton = false;
$actionUid = '';
// If we use POST (such as for delete) generate the UID:
if(!empty($action['confirm']['method']) && strtolower($action['confirm']['method']) == 'post') {
$postButton = true;
$actionUid = 'action-' . md5($actionUrl);
}

// Gather the dialog text
$dialogBodyText = !empty($action['confirm']['dg_body_txt']) ? $action['confirm']['dg_body_txt'] : __d('operation','confirm.generic');
$dialogTitle = !empty($action['confirm']['dg_title']) ? $action['confirm']['dg_title'] : $action['label'];
$confirmButtonText = !empty($action['confirm']['dg_confirm_btn']) ? $action['confirm']['dg_confirm_btn'] : __d('operation','confirm');
$cancelButtonText = !empty($action['confirm']['dg_cancel_btn']) ? $action['confirm']['dg_cancel_btn'] : __d('operation','cancel');
$replacements = !empty($action['confirm']['dg_body_txt_replacements']) ? $action['confirm']['dg_body_txt_replacements'] : '';

$dg_onclick = 'javascript:jsConfirmGeneric(\''
. $dialogBodyText . '\',\'' // dialog body text
. $actionUrl . '\',\'' // URL to redirect to on confirm
. $actionUid . '\',\'' // ID of postButton element to click on confirm if not empty
. $confirmButtonText . '\',\'' // dialog confirm button text
. $cancelButtonText . '\',\'' // dialog cancel button
. $dialogTitle . '\',[\'' // dialog title
. $replacements // dialog body text replacement strings
. '\']);';

// Links that launch a dialog box should never put up a spinner.
$actionCssClass .= ' nospin';
?>
<a class="<?= $actionCssClass; ?>" href="#" onclick="<?= $dg_onclick; ?>"
data-bs-toggle="modal" data-bs-target="#dialog">
<?php if(!empty($action['icon'])): ?>
<em class="<?= !empty($action['iconClass']) ? $action['iconClass'] : 'material-symbols'; ?>" aria-hidden="true">
<?= $action['icon']; ?>
</em>
<?php endif; ?>
<?= $action['label']; ?>
</a>
<?php
// If we need a postButton, build it. It will be clicked when the modal dialog confirm button is clicked:
if($postButton) {
print $this->Form->postButton($confirmButtonText,
$action['url'], ['id' => $actionUid, 'class' => 'hidden']);
}
?>
</li>
<?php endif; ?>
<?php endforeach;?>
<?php endif; ?>
<?php if(!empty($vv_bulk_actions) && $actionsType == 'top-links'): ?>
<li id="bulk-edit-switch-container" class="action-list-item">
<div class="form-switch">
Expand Down
4 changes: 2 additions & 2 deletions app/webroot/css/co-base.css
Original file line number Diff line number Diff line change
Expand Up @@ -1203,8 +1203,8 @@ table.index-table .with-field-actions .form-check {
padding: 1rem;
overflow-y: auto;
}
#bulk-actions-modal .modal-body h3.data-table-modal-header {
margin-bottom: -1.75em;
#bulk-actions-modal .modal-body div.dt-search {
margin-top: -2.5rem;
}
#bulk-actions .progress {
width: 100%;
Expand Down