We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
csrf_token missing for admin dashboard workflow actions (resulting in 403)
### Issue Summary

When a `POST` is made to the workflow URLs `admin/pages/workflow/action/` via the dashboard buttons (e.g. Approve) it does not contain the `csrfToken` resulting in a 403 each time.

### Steps to Reproduce

1. Start a new project with `wagtail start myproject`
2. Create a new page & publish
3. Edit that same page but this time put to moderator's approval
4. Go to the dashboard (admin home)
5. Click 'Approve'
6. Expected: Should work & approve the page's moderation, Actual: user is shown a 403 error

* I have confirmed that this issue can be reproduced as described on a fresh Wagtail project: No - Wagtail bakerydemo without changes only.


<img width="1873" alt="Screen Shot 2021-11-08 at 9 10 12 pm" src="https://user-images.githubusercontent.com/1396140/140733351-f955d1ed-283b-45b8-a85e-fc3ce20b6d49.png">


<img width="1873" alt="Screen Shot 2021-11-08 at 9 10 36 pm" src="https://user-images.githubusercontent.com/1396140/140733360-6657420b-6e14-4f28-9233-f81c95812305.png">


### Technical details

* Python version: 2.7.6
* Django version: 3.2.6
* Wagtail version: 2.15
* Browser version: Chrome 95 https://www.whatsmybrowser.org/b/VAX8V

### Likely root cause

* Dashboard renders buttons with `data-workflow-action-url` to indicate the action URL to POST to.
* `client/src/entrypoints/admin/workflow-action.js` has a function `ActivateWorkflowActionsForDashboard` which accepts a `csrfToken` and for non-modal workflow actions (e.g. approve but not approve with comment) it will `POST` to `buttonElement.dataset.workflowActionUrl` which in the approve case is something like `admin/pages/workflow/action/`
* This function is initiated in the template `wagtail/admin/templates/wagtailadmin/home/workflow_pages_to_moderate.html` - see `document.addEventListener('DOMContentLoaded', ActivateWorkflowActionsForDashboard('{{ csrf_token|escapejs }}'));`
* This template is used by `wagtail/admin/views/home.py` - see
* ```class WorkflowPagesToModeratePanel(Component):
    name = 'workflow_pages_to_moderate'
    template_name = 'wagtailadmin/home/workflow_pages_to_moderate.html'
    order = 220```
* However, the `get_context_data` does not appear to return the context variable `csrf_token` at all


### Recommended fix

* It is probably best to align the `csrf_token` with the Django tag usage instead of passing it down as a variable through the component's context.
* However, it appears that a similar approach has been taken for wagtail/admin/templates/wagtailadmin/home/locked_pages.html (not sure if that is broken also) - but it seems like there is an intentional escapejs usage


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `wagtail/admin/views/home.py`
Content:
```
1 import itertools
2 
3 from warnings import warn
4 
5 from django.conf import settings
6 from django.contrib.auth import get_user_model
7 from django.contrib.auth.decorators import permission_required
8 from django.db import connection
9 from django.db.models import Max, Q
10 from django.forms import Media
11 from django.http import Http404, HttpResponse
12 from django.template.loader import render_to_string
13 from django.template.response import TemplateResponse
14 
15 from wagtail.admin.navigation import get_site_for_user
16 from wagtail.admin.site_summary import SiteSummaryPanel
17 from wagtail.admin.ui.components import Component
18 from wagtail.core import hooks
19 from wagtail.core.models import (
20     Page, PageRevision, TaskState, UserPagePermissionsProxy, WorkflowState)
21 from wagtail.utils.deprecation import RemovedInWagtail217Warning
22 
23 
24 User = get_user_model()
25 
26 
27 # Panels for the homepage
28 
29 class UpgradeNotificationPanel(Component):
30     name = 'upgrade_notification'
31     template_name = 'wagtailadmin/home/upgrade_notification.html'
32     order = 100
33 
34     def render_html(self, parent_context):
35         if parent_context['request'].user.is_superuser and getattr(settings, "WAGTAIL_ENABLE_UPDATE_CHECK", True):
36             return super().render_html(parent_context)
37         else:
38             return ""
39 
40 
41 class PagesForModerationPanel(Component):
42     name = 'pages_for_moderation'
43     template_name = 'wagtailadmin/home/pages_for_moderation.html'
44     order = 200
45 
46     def get_context_data(self, parent_context):
47         request = parent_context['request']
48         context = super().get_context_data(parent_context)
49         user_perms = UserPagePermissionsProxy(request.user)
50         context['page_revisions_for_moderation'] = (
51             user_perms.revisions_for_moderation().select_related('page', 'user').order_by('-created_at')
52         )
53         context['request'] = request
54         return context
55 
56 
57 class UserPagesInWorkflowModerationPanel(Component):
58     name = 'user_pages_in_workflow_moderation'
59     template_name = 'wagtailadmin/home/user_pages_in_workflow_moderation.html'
60     order = 210
61 
62     def get_context_data(self, parent_context):
63         request = parent_context['request']
64         context = super().get_context_data(parent_context)
65         if getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):
66             # Find in progress workflow states which are either requested by the user or on pages owned by the user
67             context['workflow_states'] = (
68                 WorkflowState.objects.active()
69                 .filter(Q(page__owner=request.user) | Q(requested_by=request.user))
70                 .select_related(
71                     'page', 'current_task_state', 'current_task_state__task', 'current_task_state__page_revision'
72                 )
73                 .order_by('-current_task_state__started_at')
74             )
75         else:
76             context['workflow_states'] = WorkflowState.objects.none()
77         context['request'] = request
78         return context
79 
80 
81 class WorkflowPagesToModeratePanel(Component):
82     name = 'workflow_pages_to_moderate'
83     template_name = 'wagtailadmin/home/workflow_pages_to_moderate.html'
84     order = 220
85 
86     def get_context_data(self, parent_context):
87         request = parent_context['request']
88         context = super().get_context_data(parent_context)
89         if getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):
90             states = (
91                 TaskState.objects.reviewable_by(request.user)
92                 .select_related('page_revision', 'task', 'page_revision__page')
93                 .order_by('-started_at')
94             )
95             context['states'] = [
96                 (state, state.task.specific.get_actions(page=state.page_revision.page, user=request.user), state.workflow_state.all_tasks_with_status())
97                 for state in states
98             ]
99         else:
100             context['states'] = []
101         context['request'] = request
102         return context
103 
104 
105 class LockedPagesPanel(Component):
106     name = 'locked_pages'
107     template_name = 'wagtailadmin/home/locked_pages.html'
108     order = 300
109 
110     def get_context_data(self, parent_context):
111         request = parent_context['request']
112         context = super().get_context_data(parent_context)
113         context.update({
114             'locked_pages': Page.objects.filter(
115                 locked=True,
116                 locked_by=request.user,
117             ),
118             'can_remove_locks': UserPagePermissionsProxy(request.user).can_remove_locks(),
119             'request': request,
120         })
121         return context
122 
123 
124 class RecentEditsPanel(Component):
125     name = 'recent_edits'
126     template_name = 'wagtailadmin/home/recent_edits.html'
127     order = 250
128 
129     def get_context_data(self, parent_context):
130         request = parent_context['request']
131         context = super().get_context_data(parent_context)
132 
133         # Last n edited pages
134         edit_count = getattr(settings, 'WAGTAILADMIN_RECENT_EDITS_LIMIT', 5)
135         if connection.vendor == 'mysql':
136             # MySQL can't handle the subselect created by the ORM version -
137             # it fails with "This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'"
138             last_edits = PageRevision.objects.raw(
139                 """
140                 SELECT wp.* FROM
141                     wagtailcore_pagerevision wp JOIN (
142                         SELECT max(created_at) AS max_created_at, page_id FROM
143                             wagtailcore_pagerevision WHERE user_id = %s GROUP BY page_id ORDER BY max_created_at DESC LIMIT %s
144                     ) AS max_rev ON max_rev.max_created_at = wp.created_at ORDER BY wp.created_at DESC
145                  """, [
146                     User._meta.pk.get_db_prep_value(request.user.pk, connection),
147                     edit_count
148                 ]
149             )
150         else:
151             last_edits_dates = (PageRevision.objects.filter(user=request.user)
152                                 .values('page_id').annotate(latest_date=Max('created_at'))
153                                 .order_by('-latest_date').values('latest_date')[:edit_count])
154             last_edits = PageRevision.objects.filter(created_at__in=last_edits_dates).order_by('-created_at')
155 
156         page_keys = [pr.page_id for pr in last_edits]
157         pages = Page.objects.specific().in_bulk(page_keys)
158         context['last_edits'] = [
159             [revision, pages.get(revision.page_id)] for revision in last_edits
160         ]
161         context['request'] = request
162         return context
163 
164 
165 def home(request):
166 
167     panels = [
168         SiteSummaryPanel(request),
169         UpgradeNotificationPanel(),
170         WorkflowPagesToModeratePanel(),
171         PagesForModerationPanel(),
172         UserPagesInWorkflowModerationPanel(),
173         RecentEditsPanel(),
174         LockedPagesPanel(),
175     ]
176 
177     for fn in hooks.get_hooks('construct_homepage_panels'):
178         fn(request, panels)
179 
180     media = Media()
181 
182     for panel in panels:
183         if hasattr(panel, 'render') and not hasattr(panel, 'render_html'):
184             # NOTE: when this deprecation warning is removed the 'fallback_render_method=True' in
185             # wagtailadmin/home.html should be removed too
186             message = (
187                 "Homepage panel %r should provide a render_html method. "
188                 "See https://docs.wagtail.io/en/stable/releases/2.15.html#template-components-2-15"
189                 % panel
190             )
191             warn(message, category=RemovedInWagtail217Warning)
192 
193         # RemovedInWagtail217Warning: this hasattr check can be removed when support for
194         # non-component-based panels ends
195         if hasattr(panel, 'media'):
196             media += panel.media
197 
198     site_details = get_site_for_user(request.user)
199 
200     return TemplateResponse(request, "wagtailadmin/home.html", {
201         'root_page': site_details['root_page'],
202         'root_site': site_details['root_site'],
203         'site_name': site_details['site_name'],
204         'panels': sorted(panels, key=lambda p: p.order),
205         'user': request.user,
206         'media': media,
207     })
208 
209 
210 def error_test(request):
211     raise Exception("This is a test of the emergency broadcast system.")
212 
213 
214 @permission_required('wagtailadmin.access_admin', login_url='wagtailadmin_login')
215 def default(request):
216     """
217     Called whenever a request comes in with the correct prefix (eg /admin/) but
218     doesn't actually correspond to a Wagtail view.
219 
220     For authenticated users, it'll raise a 404 error. Anonymous users will be
221     redirected to the login page.
222     """
223     raise Http404
224 
225 
226 _icons_html = None
227 
228 
229 def icons():
230     global _icons_html
231     if _icons_html is None:
232         icon_hooks = hooks.get_hooks('register_icons')
233         all_icons = sorted(itertools.chain.from_iterable(hook([]) for hook in icon_hooks))
234         _icons_html = render_to_string("wagtailadmin/shared/icons.html", {'icons': all_icons})
235     return _icons_html
236 
237 
238 def sprite(request):
239     return HttpResponse(icons())
240
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/wagtail/admin/views/home.py b/wagtail/admin/views/home.py
--- a/wagtail/admin/views/home.py
+++ b/wagtail/admin/views/home.py
@@ -51,6 +51,7 @@
             user_perms.revisions_for_moderation().select_related('page', 'user').order_by('-created_at')
         )
         context['request'] = request
+        context['csrf_token'] = parent_context['csrf_token']
         return context
 
 
@@ -99,6 +100,7 @@
         else:
             context['states'] = []
         context['request'] = request
+        context['csrf_token'] = parent_context['csrf_token']
         return context
 
 
@@ -117,6 +119,7 @@
             ),
             'can_remove_locks': UserPagePermissionsProxy(request.user).can_remove_locks(),
             'request': request,
+            'csrf_token': parent_context['csrf_token'],
         })
         return context

{"golden_diff": "diff --git a/wagtail/admin/views/home.py b/wagtail/admin/views/home.py\n--- a/wagtail/admin/views/home.py\n+++ b/wagtail/admin/views/home.py\n@@ -51,6 +51,7 @@\n             user_perms.revisions_for_moderation().select_related('page', 'user').order_by('-created_at')\n         )\n         context['request'] = request\n+        context['csrf_token'] = parent_context['csrf_token']\n         return context\n \n \n@@ -99,6 +100,7 @@\n         else:\n             context['states'] = []\n         context['request'] = request\n+        context['csrf_token'] = parent_context['csrf_token']\n         return context\n \n \n@@ -117,6 +119,7 @@\n             ),\n             'can_remove_locks': UserPagePermissionsProxy(request.user).can_remove_locks(),\n             'request': request,\n+            'csrf_token': parent_context['csrf_token'],\n         })\n         return context\n", "issue": "csrf_token missing for admin dashboard workflow actions (resulting in 403)\n### Issue Summary\r\n\r\nWhen a `POST` is made to the workflow URLs `admin/pages/workflow/action/` via the dashboard buttons (e.g. Approve) it does not contain the `csrfToken` resulting in a 403 each time.\r\n\r\n### Steps to Reproduce\r\n\r\n1. Start a new project with `wagtail start myproject`\r\n2. Create a new page & publish\r\n3. Edit that same page but this time put to moderator's approval\r\n4. Go to the dashboard (admin home)\r\n5. Click 'Approve'\r\n6. Expected: Should work & approve the page's moderation, Actual: user is shown a 403 error\r\n\r\n* I have confirmed that this issue can be reproduced as described on a fresh Wagtail project: No - Wagtail bakerydemo without changes only.\r\n\r\n\r\n<img width=\"1873\" alt=\"Screen Shot 2021-11-08 at 9 10 12 pm\" src=\"https://user-images.githubusercontent.com/1396140/140733351-f955d1ed-283b-45b8-a85e-fc3ce20b6d49.png\">\r\n\r\n\r\n<img width=\"1873\" alt=\"Screen Shot 2021-11-08 at 9 10 36 pm\" src=\"https://user-images.githubusercontent.com/1396140/140733360-6657420b-6e14-4f28-9233-f81c95812305.png\">\r\n\r\n\r\n### Technical details\r\n\r\n* Python version: 2.7.6\r\n* Django version: 3.2.6\r\n* Wagtail version: 2.15\r\n* Browser version: Chrome 95 https://www.whatsmybrowser.org/b/VAX8V\r\n\r\n### Likely root cause\r\n\r\n* Dashboard renders buttons with `data-workflow-action-url` to indicate the action URL to POST to.\r\n* `client/src/entrypoints/admin/workflow-action.js` has a function `ActivateWorkflowActionsForDashboard` which accepts a `csrfToken` and for non-modal workflow actions (e.g. approve but not approve with comment) it will `POST` to `buttonElement.dataset.workflowActionUrl` which in the approve case is something like `admin/pages/workflow/action/`\r\n* This function is initiated in the template `wagtail/admin/templates/wagtailadmin/home/workflow_pages_to_moderate.html` - see `document.addEventListener('DOMContentLoaded', ActivateWorkflowActionsForDashboard('{{ csrf_token|escapejs }}'));`\r\n* This template is used by `wagtail/admin/views/home.py` - see\r\n* ```class WorkflowPagesToModeratePanel(Component):\r\n    name = 'workflow_pages_to_moderate'\r\n    template_name = 'wagtailadmin/home/workflow_pages_to_moderate.html'\r\n    order = 220```\r\n* However, the `get_context_data` does not appear to return the context variable `csrf_token` at all\r\n\r\n\r\n### Recommended fix\r\n\r\n* It is probably best to align the `csrf_token` with the Django tag usage instead of passing it down as a variable through the component's context.\r\n* However, it appears that a similar approach has been taken for wagtail/admin/templates/wagtailadmin/home/locked_pages.html (not sure if that is broken also) - but it seems like there is an intentional escapejs usage\r\n\n", "before_files": [{"content": "import itertools\n\nfrom warnings import warn\n\nfrom django.conf import settings\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.auth.decorators import permission_required\nfrom django.db import connection\nfrom django.db.models import Max, Q\nfrom django.forms import Media\nfrom django.http import Http404, HttpResponse\nfrom django.template.loader import render_to_string\nfrom django.template.response import TemplateResponse\n\nfrom wagtail.admin.navigation import get_site_for_user\nfrom wagtail.admin.site_summary import SiteSummaryPanel\nfrom wagtail.admin.ui.components import Component\nfrom wagtail.core import hooks\nfrom wagtail.core.models import (\n    Page, PageRevision, TaskState, UserPagePermissionsProxy, WorkflowState)\nfrom wagtail.utils.deprecation import RemovedInWagtail217Warning\n\n\nUser = get_user_model()\n\n\n# Panels for the homepage\n\nclass UpgradeNotificationPanel(Component):\n    name = 'upgrade_notification'\n    template_name = 'wagtailadmin/home/upgrade_notification.html'\n    order = 100\n\n    def render_html(self, parent_context):\n        if parent_context['request'].user.is_superuser and getattr(settings, \"WAGTAIL_ENABLE_UPDATE_CHECK\", True):\n            return super().render_html(parent_context)\n        else:\n            return \"\"\n\n\nclass PagesForModerationPanel(Component):\n    name = 'pages_for_moderation'\n    template_name = 'wagtailadmin/home/pages_for_moderation.html'\n    order = 200\n\n    def get_context_data(self, parent_context):\n        request = parent_context['request']\n        context = super().get_context_data(parent_context)\n        user_perms = UserPagePermissionsProxy(request.user)\n        context['page_revisions_for_moderation'] = (\n            user_perms.revisions_for_moderation().select_related('page', 'user').order_by('-created_at')\n        )\n        context['request'] = request\n        return context\n\n\nclass UserPagesInWorkflowModerationPanel(Component):\n    name = 'user_pages_in_workflow_moderation'\n    template_name = 'wagtailadmin/home/user_pages_in_workflow_moderation.html'\n    order = 210\n\n    def get_context_data(self, parent_context):\n        request = parent_context['request']\n        context = super().get_context_data(parent_context)\n        if getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):\n            # Find in progress workflow states which are either requested by the user or on pages owned by the user\n            context['workflow_states'] = (\n                WorkflowState.objects.active()\n                .filter(Q(page__owner=request.user) | Q(requested_by=request.user))\n                .select_related(\n                    'page', 'current_task_state', 'current_task_state__task', 'current_task_state__page_revision'\n                )\n                .order_by('-current_task_state__started_at')\n            )\n        else:\n            context['workflow_states'] = WorkflowState.objects.none()\n        context['request'] = request\n        return context\n\n\nclass WorkflowPagesToModeratePanel(Component):\n    name = 'workflow_pages_to_moderate'\n    template_name = 'wagtailadmin/home/workflow_pages_to_moderate.html'\n    order = 220\n\n    def get_context_data(self, parent_context):\n        request = parent_context['request']\n        context = super().get_context_data(parent_context)\n        if getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):\n            states = (\n                TaskState.objects.reviewable_by(request.user)\n                .select_related('page_revision', 'task', 'page_revision__page')\n                .order_by('-started_at')\n            )\n            context['states'] = [\n                (state, state.task.specific.get_actions(page=state.page_revision.page, user=request.user), state.workflow_state.all_tasks_with_status())\n                for state in states\n            ]\n        else:\n            context['states'] = []\n        context['request'] = request\n        return context\n\n\nclass LockedPagesPanel(Component):\n    name = 'locked_pages'\n    template_name = 'wagtailadmin/home/locked_pages.html'\n    order = 300\n\n    def get_context_data(self, parent_context):\n        request = parent_context['request']\n        context = super().get_context_data(parent_context)\n        context.update({\n            'locked_pages': Page.objects.filter(\n                locked=True,\n                locked_by=request.user,\n            ),\n            'can_remove_locks': UserPagePermissionsProxy(request.user).can_remove_locks(),\n            'request': request,\n        })\n        return context\n\n\nclass RecentEditsPanel(Component):\n    name = 'recent_edits'\n    template_name = 'wagtailadmin/home/recent_edits.html'\n    order = 250\n\n    def get_context_data(self, parent_context):\n        request = parent_context['request']\n        context = super().get_context_data(parent_context)\n\n        # Last n edited pages\n        edit_count = getattr(settings, 'WAGTAILADMIN_RECENT_EDITS_LIMIT', 5)\n        if connection.vendor == 'mysql':\n            # MySQL can't handle the subselect created by the ORM version -\n            # it fails with \"This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'\"\n            last_edits = PageRevision.objects.raw(\n                \"\"\"\n                SELECT wp.* FROM\n                    wagtailcore_pagerevision wp JOIN (\n                        SELECT max(created_at) AS max_created_at, page_id FROM\n                            wagtailcore_pagerevision WHERE user_id = %s GROUP BY page_id ORDER BY max_created_at DESC LIMIT %s\n                    ) AS max_rev ON max_rev.max_created_at = wp.created_at ORDER BY wp.created_at DESC\n                 \"\"\", [\n                    User._meta.pk.get_db_prep_value(request.user.pk, connection),\n                    edit_count\n                ]\n            )\n        else:\n            last_edits_dates = (PageRevision.objects.filter(user=request.user)\n                                .values('page_id').annotate(latest_date=Max('created_at'))\n                                .order_by('-latest_date').values('latest_date')[:edit_count])\n            last_edits = PageRevision.objects.filter(created_at__in=last_edits_dates).order_by('-created_at')\n\n        page_keys = [pr.page_id for pr in last_edits]\n        pages = Page.objects.specific().in_bulk(page_keys)\n        context['last_edits'] = [\n            [revision, pages.get(revision.page_id)] for revision in last_edits\n        ]\n        context['request'] = request\n        return context\n\n\ndef home(request):\n\n    panels = [\n        SiteSummaryPanel(request),\n        UpgradeNotificationPanel(),\n        WorkflowPagesToModeratePanel(),\n        PagesForModerationPanel(),\n        UserPagesInWorkflowModerationPanel(),\n        RecentEditsPanel(),\n        LockedPagesPanel(),\n    ]\n\n    for fn in hooks.get_hooks('construct_homepage_panels'):\n        fn(request, panels)\n\n    media = Media()\n\n    for panel in panels:\n        if hasattr(panel, 'render') and not hasattr(panel, 'render_html'):\n            # NOTE: when this deprecation warning is removed the 'fallback_render_method=True' in\n            # wagtailadmin/home.html should be removed too\n            message = (\n                \"Homepage panel %r should provide a render_html method. \"\n                \"See https://docs.wagtail.io/en/stable/releases/2.15.html#template-components-2-15\"\n                % panel\n            )\n            warn(message, category=RemovedInWagtail217Warning)\n\n        # RemovedInWagtail217Warning: this hasattr check can be removed when support for\n        # non-component-based panels ends\n        if hasattr(panel, 'media'):\n            media += panel.media\n\n    site_details = get_site_for_user(request.user)\n\n    return TemplateResponse(request, \"wagtailadmin/home.html\", {\n        'root_page': site_details['root_page'],\n        'root_site': site_details['root_site'],\n        'site_name': site_details['site_name'],\n        'panels': sorted(panels, key=lambda p: p.order),\n        'user': request.user,\n        'media': media,\n    })\n\n\ndef error_test(request):\n    raise Exception(\"This is a test of the emergency broadcast system.\")\n\n\n@permission_required('wagtailadmin.access_admin', login_url='wagtailadmin_login')\ndef default(request):\n    \"\"\"\n    Called whenever a request comes in with the correct prefix (eg /admin/) but\n    doesn't actually correspond to a Wagtail view.\n\n    For authenticated users, it'll raise a 404 error. Anonymous users will be\n    redirected to the login page.\n    \"\"\"\n    raise Http404\n\n\n_icons_html = None\n\n\ndef icons():\n    global _icons_html\n    if _icons_html is None:\n        icon_hooks = hooks.get_hooks('register_icons')\n        all_icons = sorted(itertools.chain.from_iterable(hook([]) for hook in icon_hooks))\n        _icons_html = render_to_string(\"wagtailadmin/shared/icons.html\", {'icons': all_icons})\n    return _icons_html\n\n\ndef sprite(request):\n    return HttpResponse(icons())\n", "path": "wagtail/admin/views/home.py"}], "after_files": [{"content": "import itertools\n\nfrom warnings import warn\n\nfrom django.conf import settings\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.auth.decorators import permission_required\nfrom django.db import connection\nfrom django.db.models import Max, Q\nfrom django.forms import Media\nfrom django.http import Http404, HttpResponse\nfrom django.template.loader import render_to_string\nfrom django.template.response import TemplateResponse\n\nfrom wagtail.admin.navigation import get_site_for_user\nfrom wagtail.admin.site_summary import SiteSummaryPanel\nfrom wagtail.admin.ui.components import Component\nfrom wagtail.core import hooks\nfrom wagtail.core.models import (\n    Page, PageRevision, TaskState, UserPagePermissionsProxy, WorkflowState)\nfrom wagtail.utils.deprecation import RemovedInWagtail217Warning\n\n\nUser = get_user_model()\n\n\n# Panels for the homepage\n\nclass UpgradeNotificationPanel(Component):\n    name = 'upgrade_notification'\n    template_name = 'wagtailadmin/home/upgrade_notification.html'\n    order = 100\n\n    def render_html(self, parent_context):\n        if parent_context['request'].user.is_superuser and getattr(settings, \"WAGTAIL_ENABLE_UPDATE_CHECK\", True):\n            return super().render_html(parent_context)\n        else:\n            return \"\"\n\n\nclass PagesForModerationPanel(Component):\n    name = 'pages_for_moderation'\n    template_name = 'wagtailadmin/home/pages_for_moderation.html'\n    order = 200\n\n    def get_context_data(self, parent_context):\n        request = parent_context['request']\n        context = super().get_context_data(parent_context)\n        user_perms = UserPagePermissionsProxy(request.user)\n        context['page_revisions_for_moderation'] = (\n            user_perms.revisions_for_moderation().select_related('page', 'user').order_by('-created_at')\n        )\n        context['request'] = request\n        context['csrf_token'] = parent_context['csrf_token']\n        return context\n\n\nclass UserPagesInWorkflowModerationPanel(Component):\n    name = 'user_pages_in_workflow_moderation'\n    template_name = 'wagtailadmin/home/user_pages_in_workflow_moderation.html'\n    order = 210\n\n    def get_context_data(self, parent_context):\n        request = parent_context['request']\n        context = super().get_context_data(parent_context)\n        if getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):\n            # Find in progress workflow states which are either requested by the user or on pages owned by the user\n            context['workflow_states'] = (\n                WorkflowState.objects.active()\n                .filter(Q(page__owner=request.user) | Q(requested_by=request.user))\n                .select_related(\n                    'page', 'current_task_state', 'current_task_state__task', 'current_task_state__page_revision'\n                )\n                .order_by('-current_task_state__started_at')\n            )\n        else:\n            context['workflow_states'] = WorkflowState.objects.none()\n        context['request'] = request\n        return context\n\n\nclass WorkflowPagesToModeratePanel(Component):\n    name = 'workflow_pages_to_moderate'\n    template_name = 'wagtailadmin/home/workflow_pages_to_moderate.html'\n    order = 220\n\n    def get_context_data(self, parent_context):\n        request = parent_context['request']\n        context = super().get_context_data(parent_context)\n        if getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):\n            states = (\n                TaskState.objects.reviewable_by(request.user)\n                .select_related('page_revision', 'task', 'page_revision__page')\n                .order_by('-started_at')\n            )\n            context['states'] = [\n                (state, state.task.specific.get_actions(page=state.page_revision.page, user=request.user), state.workflow_state.all_tasks_with_status())\n                for state in states\n            ]\n        else:\n            context['states'] = []\n        context['request'] = request\n        context['csrf_token'] = parent_context['csrf_token']\n        return context\n\n\nclass LockedPagesPanel(Component):\n    name = 'locked_pages'\n    template_name = 'wagtailadmin/home/locked_pages.html'\n    order = 300\n\n    def get_context_data(self, parent_context):\n        request = parent_context['request']\n        context = super().get_context_data(parent_context)\n        context.update({\n            'locked_pages': Page.objects.filter(\n                locked=True,\n                locked_by=request.user,\n            ),\n            'can_remove_locks': UserPagePermissionsProxy(request.user).can_remove_locks(),\n            'request': request,\n            'csrf_token': parent_context['csrf_token'],\n        })\n        return context\n\n\nclass RecentEditsPanel(Component):\n    name = 'recent_edits'\n    template_name = 'wagtailadmin/home/recent_edits.html'\n    order = 250\n\n    def get_context_data(self, parent_context):\n        request = parent_context['request']\n        context = super().get_context_data(parent_context)\n\n        # Last n edited pages\n        edit_count = getattr(settings, 'WAGTAILADMIN_RECENT_EDITS_LIMIT', 5)\n        if connection.vendor == 'mysql':\n            # MySQL can't handle the subselect created by the ORM version -\n            # it fails with \"This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'\"\n            last_edits = PageRevision.objects.raw(\n                \"\"\"\n                SELECT wp.* FROM\n                    wagtailcore_pagerevision wp JOIN (\n                        SELECT max(created_at) AS max_created_at, page_id FROM\n                            wagtailcore_pagerevision WHERE user_id = %s GROUP BY page_id ORDER BY max_created_at DESC LIMIT %s\n                    ) AS max_rev ON max_rev.max_created_at = wp.created_at ORDER BY wp.created_at DESC\n                 \"\"\", [\n                    User._meta.pk.get_db_prep_value(request.user.pk, connection),\n                    edit_count\n                ]\n            )\n        else:\n            last_edits_dates = (PageRevision.objects.filter(user=request.user)\n                                .values('page_id').annotate(latest_date=Max('created_at'))\n                                .order_by('-latest_date').values('latest_date')[:edit_count])\n            last_edits = PageRevision.objects.filter(created_at__in=last_edits_dates).order_by('-created_at')\n\n        page_keys = [pr.page_id for pr in last_edits]\n        pages = Page.objects.specific().in_bulk(page_keys)\n        context['last_edits'] = [\n            [revision, pages.get(revision.page_id)] for revision in last_edits\n        ]\n        context['request'] = request\n        return context\n\n\ndef home(request):\n\n    panels = [\n        SiteSummaryPanel(request),\n        UpgradeNotificationPanel(),\n        WorkflowPagesToModeratePanel(),\n        PagesForModerationPanel(),\n        UserPagesInWorkflowModerationPanel(),\n        RecentEditsPanel(),\n        LockedPagesPanel(),\n    ]\n\n    for fn in hooks.get_hooks('construct_homepage_panels'):\n        fn(request, panels)\n\n    media = Media()\n\n    for panel in panels:\n        if hasattr(panel, 'render') and not hasattr(panel, 'render_html'):\n            # NOTE: when this deprecation warning is removed the 'fallback_render_method=True' in\n            # wagtailadmin/home.html should be removed too\n            message = (\n                \"Homepage panel %r should provide a render_html method. \"\n                \"See https://docs.wagtail.io/en/stable/releases/2.15.html#template-components-2-15\"\n                % panel\n            )\n            warn(message, category=RemovedInWagtail217Warning)\n\n        # RemovedInWagtail217Warning: this hasattr check can be removed when support for\n        # non-component-based panels ends\n        if hasattr(panel, 'media'):\n            media += panel.media\n\n    site_details = get_site_for_user(request.user)\n\n    return TemplateResponse(request, \"wagtailadmin/home.html\", {\n        'root_page': site_details['root_page'],\n        'root_site': site_details['root_site'],\n        'site_name': site_details['site_name'],\n        'panels': sorted(panels, key=lambda p: p.order),\n        'user': request.user,\n        'media': media,\n    })\n\n\ndef error_test(request):\n    raise Exception(\"This is a test of the emergency broadcast system.\")\n\n\n@permission_required('wagtailadmin.access_admin', login_url='wagtailadmin_login')\ndef default(request):\n    \"\"\"\n    Called whenever a request comes in with the correct prefix (eg /admin/) but\n    doesn't actually correspond to a Wagtail view.\n\n    For authenticated users, it'll raise a 404 error. Anonymous users will be\n    redirected to the login page.\n    \"\"\"\n    raise Http404\n\n\n_icons_html = None\n\n\ndef icons():\n    global _icons_html\n    if _icons_html is None:\n        icon_hooks = hooks.get_hooks('register_icons')\n        all_icons = sorted(itertools.chain.from_iterable(hook([]) for hook in icon_hooks))\n        _icons_html = render_to_string(\"wagtailadmin/shared/icons.html\", {'icons': all_icons})\n    return _icons_html\n\n\ndef sprite(request):\n    return HttpResponse(icons())\n", "path": "wagtail/admin/views/home.py"}]}