Skip to content

Commit

Permalink
[5.x] Run actions from publish forms (#6375)
Browse files Browse the repository at this point in the history
Co-authored-by: Jason Varga <[email protected]>
  • Loading branch information
jacksleight and jasonvarga authored May 17, 2024
1 parent 5cb5da6 commit d7809ab
Show file tree
Hide file tree
Showing 33 changed files with 438 additions and 63 deletions.
6 changes: 6 additions & 0 deletions resources/css/elements/dropdowns.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
.divider {
@apply h-px bg-gray-400 overflow-hidden;
margin: 6px -8px;
/* Hide dividers that come first, last or immediately after another (due to v-if) */
& + &,
&:first-child,
&:last-child {
display: none;
}
}

.align-left & {
Expand Down
29 changes: 28 additions & 1 deletion resources/js/components/assets/Editor/Editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@
<svg-icon name="trash" class="h-4" />
<span class="rtl:mr-2 ltr:ml-2 hidden @3xl/toolbar:inline-block">{{ __('Delete') }}</span>
</button>

<dropdown-list class="mr-4" v-if="actionsMenu.length">
<data-list-inline-actions
:item="id"
:url="actionUrl"
:actions="actionsMenu"
@started="actionStarted"
@completed="actionCompleted"
/>
</dropdown-list>
</div>

<!-- Image Preview -->
Expand Down Expand Up @@ -271,7 +281,24 @@ export default {
isToolbarVisible()
{
return ! this.readOnly && this.showToolbar;
}
},
actionsMenu()
{
// We filter out the actions that are already in the toolbar.
// We don't want them to appear in the dropdown as well.
// If we filtered them out in PHP they wouldn't appear as buttons.
return this.actions.filter(action => ![
'rename_asset',
'move_asset',
'replace_asset',
'reupload_asset',
'download_asset',
'delete',
'copy_asset_url',
].includes(action.handle));
},
},
mounted() {
Expand Down
20 changes: 18 additions & 2 deletions resources/js/components/data-list/Action.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
@confirm="confirm"
@cancel="reset"
>
<div v-if="confirmationText" v-text="confirmationText" :class="{ 'mb-4': warningText || action.fields.length }" />
<div v-if="confirmationText" v-text="confirmationText" :class="{ 'mb-4': warningText || showDirtyWarning || action.fields.length }" />

<div v-if="warningText" v-text="warningText" class="text-red-500" :class="{ 'mb-4': action.fields.length }" />
<div v-if="warningText" v-text="warningText" class="text-red-500" :class="{ 'mb-4': showDirtyWarning || action.fields.length }" />

<div v-if="showDirtyWarning" v-text="dirtyText" class="text-red-500" :class="{ 'mb-4': action.fields.length }" />

<publish-container
v-if="action.fields.length"
Expand Down Expand Up @@ -57,6 +59,10 @@ export default {
},
errors: {
type: Object
},
isDirty: {
type: Boolean,
default: false,
}
},
Expand All @@ -82,6 +88,16 @@ export default {
return __n(this.action.warningText, this.selections);
},
dirtyText() {
if (! this.isDirty) return;
return __(this.action.dirtyWarningText);
},
showDirtyWarning() {
return this.isDirty && this.action.dirtyWarningText && ! this.action.bypassesDirtyWarning;
},
runButtonText() {
return __n(this.action.buttonText, this.selections);
}
Expand Down
5 changes: 4 additions & 1 deletion resources/js/components/data-list/Actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ export default {
handleActionSuccess(response) {
response.data.text().then(data => {
data = JSON.parse(data);
if (data.redirect) window.location = data.redirect;
if (data.redirect) {
if (data.bypassesDirtyWarning) this.$dirty.disableWarning();
window.location = data.redirect;
}
if (data.callback) Statamic.$callbacks.call(data.callback[0], ...data.callback.slice(1));
this.$emit('completed', true, data);
});
Expand Down
4 changes: 3 additions & 1 deletion resources/js/components/data-list/InlineActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
:action="action"
:selections="1"
:errors="errors"
:is-dirty="isDirty"
@selected="run"
>
<div slot-scope="{ action, select }">
Expand All @@ -30,7 +31,8 @@ export default {
props: {
actions: Array,
item: { required: true }
item: { required: true },
isDirty: { type: Boolean, default: false },
},
computed: {
Expand Down
40 changes: 34 additions & 6 deletions resources/js/components/entries/PublishForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@

<dropdown-list class="rtl:ml-4 ltr:mr-4" v-if="canEditBlueprint">
<dropdown-item :text="__('Edit Blueprint')" :redirect="actions.editBlueprint" />
<li class="divider" />
<data-list-inline-actions
v-if="!isCreating"
:item="values.id"
:url="itemActionUrl"
:actions="itemActions"
:is-dirty="isDirty"
@started="actionStarted"
@completed="actionCompleted"
/>
</dropdown-list>

<div class="pt-px text-2xs text-gray-600 flex rtl:ml-4 ltr:mr-4" v-if="readOnly">
Expand Down Expand Up @@ -297,12 +307,14 @@ import SaveButtonOptions from '../publish/SaveButtonOptions.vue';
import RevisionHistory from '../revision-history/History.vue';
import HasPreferences from '../data-list/HasPreferences';
import HasHiddenFields from '../publish/HasHiddenFields';
import HasActions from '../publish/HasActions';
export default {
mixins: [
HasPreferences,
HasHiddenFields,
HasActions,
],
components: {
Expand Down Expand Up @@ -496,7 +508,14 @@ export default {
saving(saving) {
this.$progress.loading(`${this.publishContainer}-entry-publish-form`, saving);
}
},
title(title) {
if (this.isBase) {
const arrow = this.direction === 'ltr' ? '' : '';
document.title = `${title} ${arrow} ${this.breadcrumbs[1].text} ${arrow} ${this.breadcrumbs[0].text} ${arrow} ${__('Statamic')}`;
}
},
},
Expand Down Expand Up @@ -550,10 +569,6 @@ export default {
}
this.title = response.data.data.title;
this.isWorkingCopy = true;
if (this.isBase) {
const arrow = this.direction === 'ltr' ? '' : '';
document.title = `${this.title} ${arrow} ${this.breadcrumbs[1].text} ${arrow} ${this.breadcrumbs[0].text} ${arrow} ${__('Statamic')}`;
}
if (!this.revisionsEnabled) this.permalink = response.data.data.permalink;
if (!this.isCreating && !this.isAutosave) this.$toast.success(__('Saved'));
this.$refs.container.saved();
Expand Down Expand Up @@ -794,7 +809,20 @@ export default {
}, this.autosaveInterval);
this.$store.commit(`publish/${this.publishContainer}/setAutosaveInterval`, interval);
}
},
afterActionSuccessfullyCompleted(response) {
if (response.data) {
this.title = response.data.title;
if (!this.revisionsEnabled) this.permalink = response.data.permalink;
this.values = this.resetValuesFromResponse(response.data.values);
this.initialPublished = response.data.published;
this.activeLocalization.published = response.data.published;
this.activeLocalization.status = response.data.status;
this.itemActions = response.data.itemActions;
}
},
},
mounted() {
Expand Down
44 changes: 44 additions & 0 deletions resources/js/components/publish/HasActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
export default {

props: {
initialItemActions: Array,
itemActionUrl: String,
},

data() {
return {
itemActions: this.initialItemActions,
}
},

methods: {

actionStarted() {
this.saving = true;
},

actionCompleted(successful=null, response) {
this.saving = false;

if (successful === false) return;

this.$events.$emit('reset-action-modals');

if (response.message !== false) {
this.$toast.success(response.message || __("Action completed"));
}

if (response.data) {
this.itemActions = response.data.itemActions;
}

this.afterActionSuccessfullyCompleted(response);
},

afterActionSuccessfullyCompleted(response) {
//
}

}

}
25 changes: 24 additions & 1 deletion resources/js/components/terms/PublishForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@

<dropdown-list class="rtl:ml-4 ltr:mr-4" v-if="canEditBlueprint">
<dropdown-item :text="__('Edit Blueprint')" :redirect="actions.editBlueprint" />
<li class="divider" />
<data-list-inline-actions
v-if="!isCreating"
:item="values.id"
:url="itemActionUrl"
:actions="itemActions"
:is-dirty="isDirty"
@started="actionStarted"
@completed="actionCompleted"
/>
</dropdown-list>

<div class="pt-px text-2xs text-gray-600 flex rtl:ml-4 ltr:mr-4" v-if="readOnly">
Expand Down Expand Up @@ -243,12 +253,14 @@ import SaveButtonOptions from '../publish/SaveButtonOptions.vue';
import RevisionHistory from '../revision-history/History.vue';
import HasPreferences from '../data-list/HasPreferences';
import HasHiddenFields from '../publish/HasHiddenFields';
import HasActions from '../publish/HasActions';
export default {
mixins: [
HasPreferences,
HasHiddenFields,
HasActions,
],
components: {
Expand Down Expand Up @@ -473,6 +485,7 @@ export default {
.then(() => {
// If revisions are enabled, just emit event.
if (this.revisionsEnabled) {
this.values = this.resetValuesFromResponse(response.data.data.values);
this.$nextTick(() => this.$emit('saved', response));
return;
}
Expand All @@ -493,6 +506,7 @@ export default {
// the hooks are resolved because if this form is being shown in a stack, we only
// want to close it once everything's done.
else {
this.values = this.resetValuesFromResponse(response.data.data.values);
this.$nextTick(() => this.$emit('saved', response));
}
Expand Down Expand Up @@ -620,7 +634,16 @@ export default {
this.localizedFields.push(handle);
this.$refs.container.dirty();
}
},
afterActionSuccessfullyCompleted(response) {
if (response.data) {
this.title = response.data.title;
this.permalink = response.data.permalink;
this.values = this.resetValuesFromResponse(response.data.values);
}
},
},
mounted() {
Expand Down
27 changes: 25 additions & 2 deletions resources/js/components/users/PublishForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@
<h1 class="flex-1" v-text="title" />
<dropdown-list class="rtl:ml-4 ltr:mr-4" v-if="canEditBlueprint">
<dropdown-item :text="__('Edit Blueprint')" :redirect="actions.editBlueprint" />
<li class="divider" />
<data-list-inline-actions
:item="values.id"
:url="itemActionUrl"
:actions="itemActions"
:is-dirty="isDirty"
@started="actionStarted"
@completed="actionCompleted"
/>
</dropdown-list>

<change-password
Expand Down Expand Up @@ -55,11 +64,13 @@
<script>
import ChangePassword from './ChangePassword.vue';
import HasHiddenFields from '../publish/HasHiddenFields';
import HasActions from '../publish/HasActions';
export default {
mixins: [
HasHiddenFields,
HasActions,
],
components: {
Expand Down Expand Up @@ -95,7 +106,11 @@ export default {
hasErrors() {
return this.error || Object.keys(this.errors).length;
}
},
isDirty() {
return this.$dirty.has(this.publishContainer);
},
},
Expand All @@ -111,6 +126,7 @@ export default {
this.$axios[this.method](this.actions.save, this.visibleValues).then(response => {
this.title = response.data.title;
this.values = this.resetValuesFromResponse(response.data.data.values);
if (!response.data.saved) {
return this.$toast.error(`Couldn't save user`)
}
Expand All @@ -128,7 +144,14 @@ export default {
this.$toast.error(__('Something went wrong'));
}
});
}
},
afterActionSuccessfullyCompleted(response) {
if (response.data) {
this.title = response.data.title;
this.values = this.resetValuesFromResponse(response.data.values);
}
},
},
Expand Down
1 change: 1 addition & 0 deletions resources/lang/en/messages.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
'globals_configure_handle_instructions' => 'Used to reference this global set on the frontend. It\'s non-trivial to change later.',
'globals_configure_intro' => 'A global set is a group of variables available across all front-end pages.',
'globals_configure_title_instructions' => 'We recommend a noun representing the set\'s contents. eg. "Brand" or "Company"',
'impersonate_action_confirmation' => 'You will be logged in as this user. You can return to your account using the avatar menu.',
'licensing_config_cached_warning' => 'Any changes you make to your .env or config files will not be detected until you clear the cache. If you are seeing unexpected licensing results here, it may be because of this. You can use the <code>php artisan config:cache</code> command to regenerate the cache.',
'licensing_error_invalid_domain' => 'Invalid domain',
'licensing_error_invalid_edition' => 'License is for :edition edition',
Expand Down
Loading

0 comments on commit d7809ab

Please sign in to comment.