diff --git a/resources/js/components/data-list/HasActions.js b/resources/js/components/data-list/HasActions.js index 6b05e31209..6e58a50db1 100644 --- a/resources/js/components/data-list/HasActions.js +++ b/resources/js/components/data-list/HasActions.js @@ -14,7 +14,9 @@ export default { this.$events.$emit('clear-selections'); this.$events.$emit('reset-action-modals'); - if (response.message !== false) { + if (response.success === false) { + this.$toast.error(response.message || __("Action failed")); + } else { this.$toast.success(response.message || __("Action completed")); } diff --git a/resources/lang/de.json b/resources/lang/de.json index 5c27a393d9..8626052c5b 100644 --- a/resources/lang/de.json +++ b/resources/lang/de.json @@ -20,6 +20,7 @@ "A valid blueprint is required.": "Ein gültiger Blueprint ist erforderlich.", "Above": "Oberhalb", "Action completed": "Aktion abgeschlossen", + "Action failed": "Aktion fehlgeschlagen", "Activate Account": "Account aktivieren", "Activation URL": "Aktivierungs-URL", "Active": "Aktiv", diff --git a/resources/lang/de_CH.json b/resources/lang/de_CH.json index 706fb3ddea..6b00b1b5b8 100644 --- a/resources/lang/de_CH.json +++ b/resources/lang/de_CH.json @@ -20,6 +20,7 @@ "A valid blueprint is required.": "Ein gültiger Blueprint ist erforderlich.", "Above": "Oberhalb", "Action completed": "Aktion abgeschlossen", + "Action failed": "Aktion fehlgeschlagen", "Activate Account": "Account aktivieren", "Activation URL": "Aktivierungs-URL", "Active": "Aktiv", diff --git a/resources/lang/fr.json b/resources/lang/fr.json index 5c0b233132..2ae9a8a1c4 100644 --- a/resources/lang/fr.json +++ b/resources/lang/fr.json @@ -20,6 +20,7 @@ "A valid blueprint is required.": "Un Blueprint valide est exigé.", "Above": "Au dessus", "Action completed": "Action terminée", + "Action failed": "Action échouée", "Activate Account": "Activer le compte", "Activation URL": "URL d'activation", "Active": "actif", diff --git a/resources/lang/nl.json b/resources/lang/nl.json index 19d771d78a..c2e65ce364 100644 --- a/resources/lang/nl.json +++ b/resources/lang/nl.json @@ -20,6 +20,7 @@ "A valid blueprint is required.": "Een geldige blueprint is vereist.", "Above": "Boven", "Action completed": "Actie voltooid", + "Action failed": "Actie gefaald", "Activate Account": "Activeer Account", "Activation URL": "Activatie-url", "Active": "Actief", diff --git a/src/Actions/Delete.php b/src/Actions/Delete.php index 30dbd99877..4c6da3259f 100644 --- a/src/Actions/Delete.php +++ b/src/Actions/Delete.php @@ -59,7 +59,21 @@ public function bypassesDirtyWarning(): bool public function run($items, $values) { - $items->each->delete(); + $failures = $items->reject(fn ($entry) => $entry->delete()); + $total = $items->count(); + + if ($failures->isNotEmpty()) { + $success = $total - $failures->count(); + if ($total === 1) { + throw new \Exception(__('Item could not be deleted')); + } elseif ($success === 0) { + throw new \Exception(__('Items could not be deleted')); + } else { + throw new \Exception(__(':success/:total items were deleted', ['total' => $total, 'success' => $success])); + } + } + + return trans_choice('Item deleted|Items deleted', $total); } public function redirect($items, $values) diff --git a/src/Actions/DeleteMultisiteEntry.php b/src/Actions/DeleteMultisiteEntry.php index 3eb19e32b8..4d4b70e995 100644 --- a/src/Actions/DeleteMultisiteEntry.php +++ b/src/Actions/DeleteMultisiteEntry.php @@ -53,7 +53,21 @@ public function run($items, $values) $items->each->deleteDescendants(); } - $items->each->delete(); + $failures = $items->reject(fn ($entry) => $entry->delete()); + $total = $items->count(); + + if ($failures->isNotEmpty()) { + $success = $total - $failures->count(); + if ($total === 1) { + throw new \Exception(__('Entry could not be deleted')); + } elseif ($success === 0) { + throw new \Exception(__('Entries could not be deleted')); + } else { + throw new \Exception(__(':success/:total entries were deleted', ['total' => $total, 'success' => $success])); + } + } + + return trans_choice('Entry deleted|Entries deleted', $total); } private function canChangeBehavior(): bool diff --git a/src/Actions/Publish.php b/src/Actions/Publish.php index d573174cbc..14ee1c9eb3 100644 --- a/src/Actions/Publish.php +++ b/src/Actions/Publish.php @@ -49,8 +49,20 @@ public function buttonText() public function run($entries, $values) { - $entries->each(function ($entry) { - $entry->publish(['user' => User::current()]); - }); + $failures = $entries->reject(fn ($entry) => $entry->publish(['user' => User::current()])); + $total = $entries->count(); + + if ($failures->isNotEmpty()) { + $success = $total - $failures->count(); + if ($total === 1) { + throw new \Exception(__('Entry could not be published')); + } elseif ($success === 0) { + throw new \Exception(__('Entries could not be published')); + } else { + throw new \Exception(__(':success/:total entries were published', ['total' => $total, 'success' => $success])); + } + } + + return trans_choice('Entry published|Entries published', $total); } } diff --git a/src/Actions/Unpublish.php b/src/Actions/Unpublish.php index 2d27e0f8dc..c30e807da3 100644 --- a/src/Actions/Unpublish.php +++ b/src/Actions/Unpublish.php @@ -44,8 +44,20 @@ public function buttonText() public function run($entries, $values) { - $entries->each(function ($entry) { - $entry->unpublish(['user' => User::current()]); - }); + $failures = $entries->reject(fn ($entry) => $entry->unpublish(['user' => User::current()])); + $total = $entries->count(); + + if ($failures->isNotEmpty()) { + $success = $total - $failures->count(); + if ($total === 1) { + throw new \Exception(__('Entry could not be unpublished')); + } elseif ($success === 0) { + throw new \Exception(__('Entries could not be unpublished')); + } else { + throw new \Exception(__(':success/:total entries were unpublished', ['total' => $total, 'success' => $success])); + } + } + + return trans_choice('Entry unpublished|Entries unpublished', $total); } } diff --git a/src/Data/Publishable.php b/src/Data/Publishable.php index 79c46f7c2b..861628b9d7 100644 --- a/src/Data/Publishable.php +++ b/src/Data/Publishable.php @@ -23,7 +23,11 @@ public function publish($options = []) return $this->publishWorkingCopy($options); } - $this->published(true)->save(); + $saved = $this->published(true)->save(); + + if (! $saved) { + return false; + } return $this; } diff --git a/src/Http/Controllers/CP/ActionController.php b/src/Http/Controllers/CP/ActionController.php index 62a3860058..5a022df989 100644 --- a/src/Http/Controllers/CP/ActionController.php +++ b/src/Http/Controllers/CP/ActionController.php @@ -2,6 +2,7 @@ namespace Statamic\Http\Controllers\CP; +use Exception; use Illuminate\Http\Request; use Statamic\Facades\Action; use Statamic\Facades\User; @@ -35,8 +36,14 @@ public function run(Request $request) abort_unless($unauthorized->isEmpty(), 403, __('You are not authorized to run this action.')); $values = $action->fields()->addValues($request->all())->process()->values()->all(); + $successful = true; - $response = $action->run($items, $values); + try { + $response = $action->run($items, $values); + } catch (Exception $e) { + $response = empty($msg = $e->getMessage()) ? __('Action failed') : $msg; + $successful = false; + } if ($redirect = $action->redirect($items, $values)) { return [ @@ -52,6 +59,7 @@ public function run(Request $request) } $response = $response ?: []; + $response['success'] = $successful; if (Arr::get($context, 'view') === 'form') { $response['data'] = $this->getItemData($items->first(), $context);