Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): delete multiple keys together #1861

Merged
merged 2 commits into from
May 23, 2022
Merged
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
16 changes: 1 addition & 15 deletions www/cpauth/account.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,7 @@
throw new ClientException($e->getMessage(), "/account");
}
} elseif ($type == "delete-api-key") {
try {
$id = filter_input(INPUT_POST, 'api-key-id', FILTER_SANITIZE_NUMBER_INT);
$request_context->getClient()->deleteApiKey(intval($id));

$protocol = $request_context->getUrlProtocol();
$host = Util::getSetting('host');
$route = '/account';
$redirect_uri = "{$protocol}://{$host}{$route}";

header("Location: {$redirect_uri}");
exit();
} catch (Exception $e) {
error_log($e->getMessage());
throw new ClientException("There was an error", "/account");
}
AccountHandler::deleteApiKey($request_context);
} elseif ($type == "resend-verification-email") {
try {
$request_context->getClient()->resendEmailVerification();
Expand Down
15 changes: 13 additions & 2 deletions www/css/account.css
Original file line number Diff line number Diff line change
Expand Up @@ -1095,7 +1095,8 @@ table.sortable th:not([aria-sort]) button:hover span::after {
justify-content: flex-start;
}

.create-delete-button-container button {
.create-delete-button-container button,
.create-delete-button-container label {
appearance: none;
border: 0;
height: 3.4em;
Expand All @@ -1108,6 +1109,9 @@ table.sortable th:not([aria-sort]) button:hover span::after {
cursor: pointer;
color: #323232;
}
.create-delete-button-container label {
line-height: 3.6em;
}

.create-delete-button-container .new-api-key {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M17 9V10H10V17H9V10H2V9H9V2H10V9H17Z' fill='%23006AD4'/%3E%3C/svg%3E");
Expand All @@ -1117,7 +1121,7 @@ table.sortable th:not([aria-sort]) button:hover span::after {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M12 4V3C12 2.73478 11.8946 2.48043 11.7071 2.29289C11.5196 2.10536 11.2652 2 11 2H8C7.73478 2 7.48043 2.10536 7.29289 2.29289C7.10536 2.48043 7 2.73478 7 3V4H2V5H4V17C4 17.2652 4.10536 17.5196 4.29289 17.7071C4.48043 17.8946 4.73478 18 5 18H14C14.2652 18 14.5196 17.8946 14.7071 17.7071C14.8946 17.5196 15 17.2652 15 17V5H17V4H12ZM8 3H11V4H8V3ZM14 17H5V5H14V17ZM8 15H7V7H8V15ZM10 15H9V7H10V15ZM12 15H11V7H12V15Z' fill='%23006AD4'/%3E%3C/svg%3E");
}

.create-delete-button-container .delete-key:disabled {
.create-delete-button-container .delete-key.disabled {
color: inherit;
opacity: 0.3;
cursor: auto;
Expand Down Expand Up @@ -1205,6 +1209,13 @@ table.sortable th:not([aria-sort]) button:hover span::after {
/* API key length */
}

.api-consumers .select-all-box {
position: relative;
top: .5em;
left: 3px;
width: 3em;
}

.billing-frequency-selector {
text-align: center;
margin-bottom: 2.5rem;
Expand Down
89 changes: 89 additions & 0 deletions www/js/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,84 @@ window.SortableTable = SortableTable;

window.HiddenContent = HiddenContent;
}(window));

/**
* DeleteApiKeyBoxSet
*/
(function(window){
class DeleteApiKeyBoxSet {
constructor(mainCheckbox) {
this.selectAllBox = mainCheckbox;
const form = this.selectAllBox.closest('form');
const individualBoxes = form.querySelectorAll('[data-apikeybox=individual]');
const deleteButtonLabel = document.querySelector('[data-apikeybox=delete-button]');
const deleteButton = form.querySelector('input[type=submit]');

this.individualBoxes = individualBoxes;
this.deleteButton = deleteButton;
this.deleteButtonLabel = deleteButtonLabel;

this.selectAllBox.addEventListener('change', this.handleMainBoxChange.bind(this));
this.individualBoxes.forEach((box) => {
box.addEventListener('change', this.handleIndividualBoxChange.bind(this));
});
}

handleMainBoxChange (e) {
if (e.target.checked) {
this.deleteButton.removeAttribute("disabled");
this.deleteButton.disabled = false;
this.deleteButtonLabel.classList.remove('disabled');

this.individualBoxes.forEach((box) => {
box.setAttribute("checked", "true");
box.checked = true;
});
} else {
this.deleteButton.setAttribute("disabled", "disabled");
this.deleteButton.disabled = true;
this.deleteButtonLabel.classList.add('disabled');

this.individualBoxes.forEach((box) => {
box.removeAttribute("checked");
box.checked = false;
});
}
}

handleIndividualBoxChange (e) {
if (e.target.checked) {
this.deleteButton.removeAttribute("disabled");
this.deleteButton.disabled = false;
this.deleteButtonLabel.classList.remove('disabled');

const allChecked = Array.from(this.individualBoxes).every((box) => box.checked);
if (allChecked) {
this.selectAllBox.setAttribute("checked", "true");
this.selectAllBox.checked = true;
}
} else {
const boxes = Array.from(this.individualBoxes);
const anyUnchecked = boxes.some((box) => !box.checked);
const allUnchecked = boxes.every((box) => !box.checked);

if (anyUnchecked) {
this.selectAllBox.removeAttribute("checked");
this.selectAllBox.checked = false;
}

if (allUnchecked) {
this.deleteButton.setAttribute("disabled", "disabled");
this.deleteButton.disabled = true;
this.deleteButtonLabel.classList.add('disabled');
}
}
}
}

window.DeleteApiKeyBoxSet = DeleteApiKeyBoxSet;
}(window));

/**
* Attach all the listeners
*/
Expand Down Expand Up @@ -478,6 +556,11 @@ window.SortableTable = SortableTable;
new HiddenContent(hiddenContentCells[i]);
}

var deleteApiKeyBoxes = document.querySelectorAll('[data-apikeybox=select-all]');
for (var i = 0; i < deleteApiKeyBoxes.length; i++) {
new DeleteApiKeyBoxSet(deleteApiKeyBoxes[i]);
}

attachListenerToBillingFrequencySelector();
handleRunUpdate();
});
Expand Down Expand Up @@ -509,6 +592,12 @@ window.SortableTable = SortableTable;
for (var i = 0; i < hiddenContentCells.length; i++) {
new HiddenContent(hiddenContentCells[i]);
}

var deleteApiKeyBoxes = document.querySelectorAll('[data-apikeybox=select-all]');
for (var i = 0; i < deleteApiKeyBoxes.length; i++) {
new DeleteApiKeyBoxSet(deleteApiKeyBoxes[i]);
}

attachListenerToBillingFrequencySelector();
handleRunUpdate();
}
Expand Down
4 changes: 1 addition & 3 deletions www/src/CPClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -379,10 +379,8 @@ public function createApiKey(string $name): array
}
}

public function deleteApiKey(int $id): array
public function deleteApiKey(array $ids): array
{
$ids = [$id];

$gql = (new Mutation('wptApiKeyBulkDelete'))
->setVariables([
new Variable('ids', '[Int!]', true)
Expand Down
29 changes: 29 additions & 0 deletions www/src/Handlers/Account.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,33 @@ public static function cancelSubscription(RequestContext $request_context)
throw new ClientException("There was an error", "/account");
}
}

public static function deleteApiKey(RequestContext $request_context)
{
try {
$api_key_ids = $_POST['api-key-id'];
if (!empty($api_key_ids)) {
$sanitized_keys = array_filter($api_key_ids, function ($v) {
return filter_var($v, FILTER_SANITIZE_NUMBER_INT);
});
$ints = array_map(function ($v) {
return intval($v);
}, $sanitized_keys);

$request_context->getClient()->deleteApiKey($ints);
}


$protocol = $request_context->getUrlProtocol();
$host = Util::getSetting('host');
$route = '/account';
$redirect_uri = "{$protocol}://{$host}{$route}";

header("Location: {$redirect_uri}");
exit();
} catch (\Exception $e) {
error_log($e->getMessage());
throw new ClientException("There was an error", "/account");
}
}
}
116 changes: 58 additions & 58 deletions www/templates/account/includes/billing-data.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
<div class="create-key-container">
<div class="create-delete-button-container">
<button type="button" class="new-api-key" data-toggle="open" data-targetid="create-api-key-toggle-area">New Api Key</button>
<button type="button" class="delete-key" disabled>Delete</button>
<label for="delete-api-key-submit-input" class="delete-key disabled" data-apikeybox="delete-button" disabled>Delete</label>
</div>
<div class="toggleable" id="create-api-key-toggle-area">
<form method="POST" action="/account">
Expand All @@ -112,62 +112,62 @@
</div>
</div>
<div class="info">
<table class="sortable">
<caption>
<span class="sr-only">API Consumers table, column headers with buttons are sortable.</span>
</caption>
<thead>
<tr>
<th class="no-sort">Delete</th>
<th aria-sort="ascending">
<button>
Name
<span aria-hidden="true"></span>
</button>
</th>
<th>
<button>
API Key
<span aria-hidden="true"></span>
</button>
</th>
<th>
<button>
Create Date
<span aria-hidden="true"></span>
</button>
</th>
<th>
<button>
Last Updated
<span aria-hidden="true"></span>
</button>
</th>
</tr>
</thead>
<tbody>
<?php foreach($wptApiKey as $row): ?>
<tr>
<td>
<form method='POST' action='/account'>
<input type='hidden' name='api-key-id' value='<?= $row['id'] ?>' />
<input type='hidden' name='type' value='delete-api-key' />
<input type='hidden' name='csrf_token' value='<?= $csrf_token ?>' />
<button type='submit'>Delete</button>
</form>
</td>
<td><?= $row['name'] ?></td>
<td class="hidden-content">
<button type="button" class="view-button">View</button>
<span class="hidden-area closed">
<span class="api-key"><?= $row['apiKey'] ?></span>
<button type="button" class="hide-button"><span class="sr-only">Close</span></button></td>
</span>
<td><?= date_format(date_create($row['createDate']), 'M d Y H:i:s e') ?></td>
<td><?= date_format(date_create($row['changeDate']), 'M d Y H:i:s e') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<form method='POST' action='/account'>
<table class="sortable">
<caption>
<span class="sr-only">API Consumers table, column headers with buttons are sortable.</span>
</caption>
<thead>
<tr>
<th class="no-sort select-all-box"><label class="sr-only" for="select-all-api-keys">Select all api keys</label><input type="checkbox" name="select-all-api-keys" data-apikeybox="select-all" /></th>
<th aria-sort="ascending">
<button>
Name
<span aria-hidden="true"></span>
</button>
</th>
<th>
<button>
API Key
<span aria-hidden="true"></span>
</button>
</th>
<th>
<button>
Create Date
<span aria-hidden="true"></span>
</button>
</th>
<th>
<button>
Last Updated
<span aria-hidden="true"></span>
</button>
</th>
</tr>
</thead>
<tbody>
<?php foreach($wptApiKey as $row): ?>
<tr>
<td>
<input type='checkbox' data-apikeybox="individual" name='api-key-id[]' value='<?= $row['id'] ?>' />
</td>
<td><?= $row['name'] ?></td>
<td class="hidden-content">
<button type="button" class="view-button">View</button>
<span class="hidden-area closed">
<span class="api-key"><?= $row['apiKey'] ?></span>
<button type="button" class="hide-button"><span class="sr-only">Close</span></button></td>
</span>
<td><?= date_format(date_create($row['createDate']), 'M d Y H:i:s e') ?></td>
<td><?= date_format(date_create($row['changeDate']), 'M d Y H:i:s e') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<input type='hidden' name='type' value='delete-api-key' />
<input type='hidden' name='csrf_token' value='<?= $csrf_token ?>' />
<input type="submit" id="delete-api-key-submit-input" class="sr-only" />
</form>
</div>
</div>
4 changes: 2 additions & 2 deletions www/templates/account/my-account.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@

<?php if ($is_paid) {
include_once __DIR__ . '/includes/billing-data.php';
include_once __DIR__ . '/includes/modals/subscription-plan.php';
include_once __DIR__ . '/includes/modals/payment-info.php';
} else {
include_once __DIR__ . '/includes/signup.php';
} ?>
Expand All @@ -66,7 +68,5 @@
<?php
include_once __DIR__ . '/includes/modals/contact-info.php';
include_once __DIR__ . '/includes/modals/password.php';
include_once __DIR__ . '/includes/modals/subscription-plan.php';
include_once __DIR__ . '/includes/modals/payment-info.php';
?>
<!-- /Modals -->