mirror of
https://github.com/wagtail/wagtail.git
synced 2024-12-01 11:41:20 +01:00
Prevent multiple TaskState approvals/rejections. Allow TaskState to be passed through to workflow_action to prevent race conditions: ie reviewer approving while in the meantime edits have been made. Add a new task preview view to allow preview of the revision in moderation in a specific task.
This commit is contained in:
parent
4c66ebe36d
commit
3a361c9665
@ -3,6 +3,5 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<p>{% blocktrans with title=page.get_admin_display_title task=task.name %}The page "{{ title }}" has been approved in moderation stage "{{ task }}".{% endblocktrans %}</p>
|
<p>{% blocktrans with title=page.get_admin_display_title task=task.name %}The page "{{ title }}" has been approved in moderation stage "{{ task }}".{% endblocktrans %}</p>
|
||||||
<p>{% trans "You can preview the page here:" %} <a href="{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:preview_on_edit' page.id %}">{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:preview_on_edit' page.id %}</a><br/></p>
|
|
||||||
<p>{% trans "You can edit the page here:"%} <a href="{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}">{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}</a></p>
|
<p>{% trans "You can edit the page here:"%} <a href="{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}">{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}</a></p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -3,6 +3,5 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% blocktrans with title=page.get_admin_display_title|safe task=task.name %}The page "{{ title }}" has been approved in moderation stage "{{ task }}".{% endblocktrans %}
|
{% blocktrans with title=page.get_admin_display_title|safe task=task.name %}The page "{{ title }}" has been approved in moderation stage "{{ task }}".{% endblocktrans %}
|
||||||
{% trans "You can preview the page here:" %} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:preview_on_edit' page.id %}
|
|
||||||
{% trans "You can edit the page here:"%} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}
|
{% trans "You can edit the page here:"%} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -3,6 +3,5 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% blocktrans with title=page.get_admin_display_title|safe task=task.name %}The page "{{ title }}" has been rejected in moderation stage "{{ task }}".{% endblocktrans %}
|
{% blocktrans with title=page.get_admin_display_title|safe task=task.name %}The page "{{ title }}" has been rejected in moderation stage "{{ task }}".{% endblocktrans %}
|
||||||
|
|
||||||
{% trans "You can edit the page here:"%} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}
|
{% trans "You can edit the page here:"%} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<p>{% blocktrans with task=task.name title=page.get_admin_display_title %}The page "{{ title }}" has been submitted for approval in moderation stage "{{ task }}".{% endblocktrans %}</p>
|
<p>{% blocktrans with task=task.name title=page.get_admin_display_title %}The page "{{ title }}" has been submitted for approval in moderation stage "{{ task }}".{% endblocktrans %}</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{% trans "You can preview the page here:" %} <a href="{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:preview_on_edit' page.id %}">{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:preview_on_edit' page.id %}</a><br/>
|
{% trans "You can preview the page here:" %} <a href="{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:workflow_preview' page.id task.id %}">{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:workflow_preview' page.id task.id %}</a><br/>
|
||||||
{% trans "You can edit the page here:" %} <a href="{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}">{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}</a>
|
{% trans "You can edit the page here:" %} <a href="{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}">{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}</a>
|
||||||
</p>
|
</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
{% blocktrans with task=task.name title=page.get_admin_display_title %}The page "{{ title }}" has been submitted for approval to moderation stage "{{ task }}".{% endblocktrans %}
|
{% blocktrans with task=task.name title=page.get_admin_display_title %}The page "{{ title }}" has been submitted for approval to moderation stage "{{ task }}".{% endblocktrans %}
|
||||||
|
|
||||||
|
|
||||||
{% trans "You can preview the page here:" %} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:preview_on_edit' page.id %}
|
{% trans "You can preview the page here:" %} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:workflow_preview' page.id task.id %}
|
||||||
{% trans "You can edit the page here:" %} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}
|
{% trans "You can edit the page here:" %} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
<p>{% blocktrans with workflow=workflow.name title=page.get_admin_display_title %}The page "{{ title }}" has been submitted for moderation to workflow "{{ workflow }}".{% endblocktrans %}</p>
|
<p>{% blocktrans with workflow=workflow.name title=page.get_admin_display_title %}The page "{{ title }}" has been submitted for moderation to workflow "{{ workflow }}".{% endblocktrans %}</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{% trans "You can preview the page here:" %} <a href="{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:preview_on_edit' page.id %}">{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:preview_on_edit' page.id %}</a><br/>
|
|
||||||
{% trans "You can edit the page here:" %} <a href="{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}">{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}</a>
|
{% trans "You can edit the page here:" %} <a href="{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}">{{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}</a>
|
||||||
</p>
|
</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
{% blocktrans with workflow=workflow.name title=page.get_admin_display_title %}The page "{{ title }}" has been submitted for moderation to workflow "{{ workflow }}".{% endblocktrans %}
|
{% blocktrans with workflow=workflow.name title=page.get_admin_display_title %}The page "{{ title }}" has been submitted for moderation to workflow "{{ workflow }}".{% endblocktrans %}
|
||||||
|
|
||||||
|
|
||||||
{% trans "You can preview the page here:" %} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:preview_on_edit' page.id %}
|
|
||||||
{% trans "You can edit the page here:" %} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}
|
{% trans "You can edit the page here:" %} {{ settings.BASE_URL }}{% url 'wagtailadmin_pages:edit' page.id %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -174,12 +174,11 @@
|
|||||||
actionFieldElement.value = buttonElement.dataset.workflowAction;
|
actionFieldElement.value = buttonElement.dataset.workflowAction;
|
||||||
formElement.appendChild(actionFieldElement);
|
formElement.appendChild(actionFieldElement);
|
||||||
|
|
||||||
var nextFieldElement = document.createElement('input');
|
var taskFieldElement = document.createElement('input');
|
||||||
nextFieldElement.type = 'hidden';
|
taskFieldElement.type = 'hidden';
|
||||||
nextFieldElement.name = 'next';
|
taskFieldElement.name = 'task_id';
|
||||||
{% url 'wagtailadmin_home' as dashboard_url %}
|
taskFieldElement.value = {{ current_task_state.id }};
|
||||||
nextFieldElement.value = '{{ dashboard_url|escapejs }}';
|
formElement.appendChild(taskFieldElement);
|
||||||
formElement.appendChild(nextFieldElement);
|
|
||||||
|
|
||||||
document.body.appendChild(formElement);
|
document.body.appendChild(formElement);
|
||||||
formElement.submit();
|
formElement.submit();
|
||||||
|
@ -26,6 +26,7 @@ urlpatterns = [
|
|||||||
path('<int:page_id>/copy/', pages.copy, name='copy'),
|
path('<int:page_id>/copy/', pages.copy, name='copy'),
|
||||||
|
|
||||||
path('workflow/action/<int:page_id>/', pages.workflow_action, name='workflow_action'),
|
path('workflow/action/<int:page_id>/', pages.workflow_action, name='workflow_action'),
|
||||||
|
path('workflow/preview/<int:page_id>/<int:task_id>/', pages.preview_revision_for_task, name='workflow_preview'),
|
||||||
|
|
||||||
path('moderation/<int:revision_id>/approve/', pages.approve_moderation, name='approve_moderation'),
|
path('moderation/<int:revision_id>/approve/', pages.approve_moderation, name='approve_moderation'),
|
||||||
path('moderation/<int:revision_id>/reject/', pages.reject_moderation, name='reject_moderation'),
|
path('moderation/<int:revision_id>/reject/', pages.reject_moderation, name='reject_moderation'),
|
||||||
|
@ -651,11 +651,7 @@ def edit(request, page_id):
|
|||||||
'has_unsaved_changes': has_unsaved_changes,
|
'has_unsaved_changes': has_unsaved_changes,
|
||||||
'page_locked': page_perms.page_locked(),
|
'page_locked': page_perms.page_locked(),
|
||||||
'workflow_actions': page.current_workflow_task.get_actions(page, request.user) if page.current_workflow_task else [],
|
'workflow_actions': page.current_workflow_task.get_actions(page, request.user) if page.current_workflow_task else [],
|
||||||
'task_statuses': task_statuses,
|
'current_task_state': page.current_workflow_task_state
|
||||||
'current_task_number': current_task_number,
|
|
||||||
'task_name': task_name,
|
|
||||||
'workflow_name': workflow_name,
|
|
||||||
'total_tasks': total_tasks
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -1196,20 +1192,27 @@ def workflow_action(request, page_id):
|
|||||||
|
|
||||||
redirect_to = request.POST.get('next', None)
|
redirect_to = request.POST.get('next', None)
|
||||||
if not redirect_to or not is_safe_url(url=redirect_to, allowed_hosts={request.get_host()}):
|
if not redirect_to or not is_safe_url(url=redirect_to, allowed_hosts={request.get_host()}):
|
||||||
redirect_to = reverse('wagtailadmin_explore', args=[page.get_parent().id])
|
redirect_to = reverse('wagtailadmin_pages:edit', args=[page_id])
|
||||||
|
|
||||||
if not page.workflow_in_progress():
|
if not page.workflow_in_progress():
|
||||||
messages.error(request, _("The page '{0}' is not currently awaiting moderation.").format(page.get_admin_display_title()))
|
messages.error(request, _("The page '{0}' is not currently awaiting moderation.").format(page.get_admin_display_title()))
|
||||||
return redirect(redirect_to)
|
return redirect(redirect_to)
|
||||||
|
|
||||||
actions = page.current_workflow_task.get_actions(page, request.user)
|
task_state_id = request.POST.get('task_state_id', None)
|
||||||
|
|
||||||
|
task_state = TaskState.objects.get(id=task_state_id) if task_state_id else page.current_workflow_task_state
|
||||||
|
task_state = task_state.specific
|
||||||
|
|
||||||
|
task = task_state.task.specific
|
||||||
|
|
||||||
|
actions = task.get_actions(page, request.user)
|
||||||
|
|
||||||
action_name = request.POST.get('action')
|
action_name = request.POST.get('action')
|
||||||
|
|
||||||
if action_name not in set(action[0] for action in actions):
|
if action_name not in set(action[0] for action in actions):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
page.current_workflow_task.on_action(page.current_workflow_task_state, request.user, action_name)
|
task.on_action(task_state, request.user, action_name)
|
||||||
|
|
||||||
return redirect(redirect_to)
|
return redirect(redirect_to)
|
||||||
|
|
||||||
@ -1236,6 +1239,33 @@ def preview_for_moderation(request, revision_id):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@require_GET
|
||||||
|
def preview_revision_for_task(request, page_id, task_id):
|
||||||
|
"""Preview the revision linked to the in-progress TaskState of a specified Task. This enables pages in moderation
|
||||||
|
to be edited and new TaskStates linked to the new revisions created, with preview links remaining valid"""
|
||||||
|
|
||||||
|
page = Page.objects.get(id=page_id)
|
||||||
|
task = Task.objects.get(id=task_id).specific
|
||||||
|
try:
|
||||||
|
task_state = TaskState.objects.get(page_revision__page=page, task=task, status=TaskState.STATUS_IN_PROGRESS)
|
||||||
|
except TaskState.DoesNotExist:
|
||||||
|
messages.error(request, _("The page '{0}' is not currently awaiting moderation in task '{1}'.").format(page.get_admin_display_title(), task.name))
|
||||||
|
return redirect('wagtailadmin_home')
|
||||||
|
|
||||||
|
revision = task_state.page_revision
|
||||||
|
|
||||||
|
if not task.get_actions(page, request.user):
|
||||||
|
raise PermissionDenied
|
||||||
|
|
||||||
|
page_to_view = revision.as_page_object()
|
||||||
|
|
||||||
|
# TODO: provide workflow actions within this view
|
||||||
|
|
||||||
|
return page_to_view.make_preview_request(request, page.default_preview_mode, extra_request_attrs={
|
||||||
|
'revision_id': revision.id
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
def lock(request, page_id):
|
def lock(request, page_id):
|
||||||
# Get the page
|
# Get the page
|
||||||
|
@ -9,7 +9,7 @@ from django.contrib.auth.models import Group, Permission
|
|||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core import checks
|
from django.core import checks
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError, PermissionDenied
|
||||||
from django.core.handlers.base import BaseHandler
|
from django.core.handlers.base import BaseHandler
|
||||||
from django.core.handlers.wsgi import WSGIRequest
|
from django.core.handlers.wsgi import WSGIRequest
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
@ -2742,12 +2742,16 @@ class WorkflowState(models.Model):
|
|||||||
return Task.objects.filter(workflow_tasks__workflow=self.workflow, active=True).exclude(Q(task_states__page_revision=self.page.get_latest_revision()), Q(task_states__status=TaskState.STATUS_APPROVED) | Q(task_states__status=TaskState.STATUS_SKIPPED)).order_by('workflow_tasks__sort_order').first()
|
return Task.objects.filter(workflow_tasks__workflow=self.workflow, active=True).exclude(Q(task_states__page_revision=self.page.get_latest_revision()), Q(task_states__status=TaskState.STATUS_APPROVED) | Q(task_states__status=TaskState.STATUS_SKIPPED)).order_by('workflow_tasks__sort_order').first()
|
||||||
|
|
||||||
def cancel(self, user=None):
|
def cancel(self, user=None):
|
||||||
|
if self.status != self.STATUS_IN_PROGRESS:
|
||||||
|
raise PermissionDenied
|
||||||
self.status = 'cancelled'
|
self.status = 'cancelled'
|
||||||
self.save()
|
self.save()
|
||||||
workflow_cancelled.send(sender=self.__class__, instance=self, user=user)
|
workflow_cancelled.send(sender=self.__class__, instance=self, user=user)
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def finish(self, user=None):
|
def finish(self, user=None):
|
||||||
|
if self.status != self.STATUS_IN_PROGRESS:
|
||||||
|
raise PermissionDenied
|
||||||
self.status = 'approved'
|
self.status = 'approved'
|
||||||
self.save()
|
self.save()
|
||||||
self.on_finish()
|
self.on_finish()
|
||||||
@ -2827,6 +2831,8 @@ class TaskState(models.Model):
|
|||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def approve(self, user=None):
|
def approve(self, user=None):
|
||||||
|
if self.status != self.STATUS_IN_PROGRESS:
|
||||||
|
raise PermissionDenied
|
||||||
self.status = 'approved'
|
self.status = 'approved'
|
||||||
self.finished_at = timezone.now()
|
self.finished_at = timezone.now()
|
||||||
self.save()
|
self.save()
|
||||||
@ -2836,6 +2842,8 @@ class TaskState(models.Model):
|
|||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def reject(self, user=None):
|
def reject(self, user=None):
|
||||||
|
if self.status != self.STATUS_IN_PROGRESS:
|
||||||
|
raise PermissionDenied
|
||||||
self.status = 'rejected'
|
self.status = 'rejected'
|
||||||
self.finished_at = timezone.now()
|
self.finished_at = timezone.now()
|
||||||
self.save()
|
self.save()
|
||||||
|
Loading…
Reference in New Issue
Block a user