diff --git a/docs/reference/hooks.md b/docs/reference/hooks.md index 5c713c5ba9..4938dfcb8f 100644 --- a/docs/reference/hooks.md +++ b/docs/reference/hooks.md @@ -779,7 +779,11 @@ The `get_url`, `is_shown`, `get_context_data` and `render_html` methods all acce - `page` - for `view` = `'edit'` or `'revisions_revert'`, the page being edited - `parent_page` - for `view` = `'create'`, the parent page of the page being created - `request` - the current request object -- `user_page_permissions` - a `UserPagePermissionsProxy` object for the current user, to test permissions against +- `user_page_permissions` - a `UserPagePermissionsProxy` object for the current user, to test permissions against (deprecated) + + ```{versionchanged} 5.0 + The `user_page_permissions` context variable is deprecated. If you use `user_page_permissions.for_page(page)`, replace it with `page.permissions_for_user(user)` instead. To make queries based on the user's permissions, use `wagtail.permission_policies.pages.PagePermissionPolicy`. + ``` ```python from wagtail import hooks diff --git a/wagtail/models/__init__.py b/wagtail/models/__init__.py index 6dde71c239..5ba295f08e 100644 --- a/wagtail/models/__init__.py +++ b/wagtail/models/__init__.py @@ -2931,55 +2931,105 @@ class UserPagePermissionsProxy: def __init__(self, user): from wagtail.permission_policies.pages import PagePermissionPolicy - warnings.warn( - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", - category=RemovedInWagtail60Warning, - stacklevel=2, - ) - self.user = user self.permission_policy = PagePermissionPolicy() - self.permissions = self.permission_policy.get_cached_permissions_for_user(user) + + @cached_property + def permissions(self): + return self.permission_policy.get_cached_permissions_for_user(self.user) def revisions_for_moderation(self): """Return a queryset of page revisions awaiting moderation that this user has publish permission on""" + warnings.warn( + "UserPagePermissionsProxy.revisions_for_moderation() is deprecated. " + "Use wagtail.permission_policies.pages.PagePermissionPolicy.revisions_for_moderation(user) instead.", + category=RemovedInWagtail60Warning, + stacklevel=2, + ) return self.permission_policy.revisions_for_moderation(self.user) def for_page(self, page): """Return a PagePermissionTester object that can be used to query whether this user has permission to perform specific tasks on the given page""" - return PagePermissionTester(self.user, page) + warnings.warn( + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", + category=RemovedInWagtail60Warning, + stacklevel=2, + ) + return page.permissions_for_user(self.user) def explorable_pages(self): """Return a queryset of pages that the user has access to view in the explorer (e.g. add/edit/publish permission). Includes all pages with specific group permissions and also the ancestors of those pages (in order to enable navigation in the explorer)""" + warnings.warn( + "UserPagePermissionsProxy.explorable_pages() is deprecated. " + "Use wagtail.permission_policies.pages.PagePermissionPolicy." + "explorable_instances(user) instead.", + category=RemovedInWagtail60Warning, + stacklevel=2, + ) return self.permission_policy.explorable_instances(self.user) def editable_pages(self): """Return a queryset of the pages that this user has permission to edit""" + warnings.warn( + "UserPagePermissionsProxy.editable_pages() is deprecated. " + "Use wagtail.permission_policies.pages.PagePermissionPolicy." + 'instances_user_has_permission_for(user, "edit") instead.', + category=RemovedInWagtail60Warning, + stacklevel=2, + ) return self.permission_policy.instances_user_has_permission_for( self.user, "edit" ) def can_edit_pages(self): """Return True if the user has permission to edit any pages""" + warnings.warn( + "UserPagePermissionsProxy.can_edit_pages() is deprecated. " + "Use wagtail.permission_policies.pages.PagePermissionPolicy." + 'user_has_permission(user, "edit") instead.', + category=RemovedInWagtail60Warning, + stacklevel=2, + ) return self.editable_pages().exists() def publishable_pages(self): """Return a queryset of the pages that this user has permission to publish""" + warnings.warn( + "UserPagePermissionsProxy.publishable_pages() is deprecated. " + "Use wagtail.permission_policies.pages.PagePermissionPolicy." + 'instances_user_has_permission_for(user, "publish") instead.', + category=RemovedInWagtail60Warning, + stacklevel=2, + ) return self.permission_policy.instances_user_has_permission_for( self.user, "publish" ) def can_publish_pages(self): """Return True if the user has permission to publish any pages""" + warnings.warn( + "UserPagePermissionsProxy.can_publish_pages() is deprecated. " + "Use wagtail.permission_policies.pages.PagePermissionPolicy." + 'user_has_permission(user, "publish") instead.', + category=RemovedInWagtail60Warning, + stacklevel=2, + ) return self.publishable_pages().exists() def can_remove_locks(self): """Returns True if the user has permission to unlock pages they have not locked""" + warnings.warn( + "UserPagePermissionsProxy.can_remove_locks() is deprecated. " + "Use wagtail.permission_policies.pages.PagePermissionPolicy." + 'user_has_permission(user, "unlock") instead.', + category=RemovedInWagtail60Warning, + stacklevel=2, + ) return self.permission_policy.user_has_permission(self.user, "unlock") diff --git a/wagtail/tests/test_page_permissions.py b/wagtail/tests/test_page_permissions.py index 5f989eb792..250cda603d 100644 --- a/wagtail/tests/test_page_permissions.py +++ b/wagtail/tests/test_page_permissions.py @@ -420,18 +420,37 @@ class TestPagePermission(TestCase): url_path="/home/events/someone-elses-event/" ) + user_perms = UserPagePermissionsProxy(event_editor) + + # To be replaced with the suggestion in the warning's message with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.editable_pages() is deprecated. " + "Use wagtail.permission_policies.pages.PagePermissionPolicy." + 'instances_user_has_permission_for(user, "edit") instead.', ): - # Replace with PagePermissionPolicy().instances_user_has_permission_for() - user_perms = UserPagePermissionsProxy(event_editor) - - editable_pages = user_perms.editable_pages() - can_edit_pages = user_perms.can_edit_pages() - publishable_pages = user_perms.publishable_pages() - can_publish_pages = user_perms.can_publish_pages() + editable_pages = user_perms.editable_pages() + with self.assertWarnsMessage( + RemovedInWagtail60Warning, + "UserPagePermissionsProxy.can_edit_pages() is deprecated. " + "Use wagtail.permission_policies.pages.PagePermissionPolicy." + 'user_has_permission(user, "edit") instead.', + ): + can_edit_pages = user_perms.can_edit_pages() + with self.assertWarnsMessage( + RemovedInWagtail60Warning, + "UserPagePermissionsProxy.publishable_pages() is deprecated. " + "Use wagtail.permission_policies.pages.PagePermissionPolicy." + 'instances_user_has_permission_for(user, "publish") instead.', + ): + publishable_pages = user_perms.publishable_pages() + with self.assertWarnsMessage( + RemovedInWagtail60Warning, + "UserPagePermissionsProxy.can_publish_pages() is deprecated. " + "Use wagtail.permission_policies.pages.PagePermissionPolicy." + 'user_has_permission(user, "publish") instead.', + ): + can_publish_pages = user_perms.can_publish_pages() self.assertFalse(editable_pages.filter(id=homepage.id).exists()) self.assertTrue(editable_pages.filter(id=christmas_page.id).exists()) @@ -462,15 +481,15 @@ class TestPagePermission(TestCase): ) about_us_page = Page.objects.get(url_path="/home/about-us/") + user_perms = UserPagePermissionsProxy(event_editor) + with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.explorable_pages() is deprecated. " + "Use wagtail.permission_policies.pages.PagePermissionPolicy." + "explorable_instances(user) instead.", ): - # Replace with PagePermissionPolicy().explorable_instances() - user_perms = UserPagePermissionsProxy(event_editor) - - explorable_pages = user_perms.explorable_pages() + explorable_pages = user_perms.explorable_pages() # Verify all pages below /home/events/ are explorable self.assertTrue(explorable_pages.filter(id=christmas_page.id).exists()) @@ -506,19 +525,19 @@ class TestPagePermission(TestCase): email="corporateeditor@example.com" ) - with self.assertWarnsMessage( - RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", - ): - # Replace with PagePermissionPolicy().explorable_instances() - user_perms = UserPagePermissionsProxy(corporate_editor) + user_perms = UserPagePermissionsProxy(corporate_editor) about_us_page = Page.objects.get(url_path="/home/about-us/") businessy_events = Page.objects.get(url_path="/home/events/businessy-events/") events_page = Page.objects.get(url_path="/home/events/") - explorable_pages = user_perms.explorable_pages() + with self.assertWarnsMessage( + RemovedInWagtail60Warning, + "UserPagePermissionsProxy.explorable_pages() is deprecated. " + "Use wagtail.permission_policies.pages.PagePermissionPolicy." + "explorable_instances(user) instead.", + ): + explorable_pages = user_perms.explorable_pages() self.assertTrue(explorable_pages.filter(id=about_us_page.id).exists()) self.assertTrue(explorable_pages.filter(id=businessy_events.id).exists()) @@ -537,18 +556,17 @@ class TestPagePermission(TestCase): url_path="/home/events/someone-elses-event/" ) - with self.assertWarnsMessage( - RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", - ): - # Replace with PagePermissionPolicy().instances_user_has_permission_for() - user_perms = UserPagePermissionsProxy(event_moderator) + user_perms = UserPagePermissionsProxy(event_moderator) - editable_pages = user_perms.editable_pages() - can_edit_pages = user_perms.can_edit_pages() - publishable_pages = user_perms.publishable_pages() - can_publish_pages = user_perms.can_publish_pages() + # To be replaced with the suggestion in the warning's message + with self.assertWarns(RemovedInWagtail60Warning): + editable_pages = user_perms.editable_pages() + with self.assertWarns(RemovedInWagtail60Warning): + can_edit_pages = user_perms.can_edit_pages() + with self.assertWarns(RemovedInWagtail60Warning): + publishable_pages = user_perms.publishable_pages() + with self.assertWarns(RemovedInWagtail60Warning): + can_publish_pages = user_perms.can_publish_pages() self.assertFalse(editable_pages.filter(id=homepage.id).exists()) self.assertTrue(editable_pages.filter(id=christmas_page.id).exists()) @@ -577,18 +595,17 @@ class TestPagePermission(TestCase): url_path="/home/events/someone-elses-event/" ) - with self.assertWarnsMessage( - RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", - ): - # Replace with PagePermissionPolicy().instances_user_has_permission_for() - user_perms = UserPagePermissionsProxy(user) + user_perms = UserPagePermissionsProxy(user) - editable_pages = user_perms.editable_pages() - can_edit_pages = user_perms.can_edit_pages() - publishable_pages = user_perms.publishable_pages() - can_publish_pages = user_perms.can_publish_pages() + # To be replaced with the suggestion in the warning's message + with self.assertWarns(RemovedInWagtail60Warning): + editable_pages = user_perms.editable_pages() + with self.assertWarns(RemovedInWagtail60Warning): + can_edit_pages = user_perms.can_edit_pages() + with self.assertWarns(RemovedInWagtail60Warning): + publishable_pages = user_perms.publishable_pages() + with self.assertWarns(RemovedInWagtail60Warning): + can_publish_pages = user_perms.can_publish_pages() self.assertFalse(editable_pages.filter(id=homepage.id).exists()) self.assertFalse(editable_pages.filter(id=christmas_page.id).exists()) @@ -619,18 +636,17 @@ class TestPagePermission(TestCase): url_path="/home/events/someone-elses-event/" ) - with self.assertWarnsMessage( - RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", - ): - # Replace with PagePermissionPolicy().instances_user_has_permission_for() - user_perms = UserPagePermissionsProxy(user) + user_perms = UserPagePermissionsProxy(user) - editable_pages = user_perms.editable_pages() - can_edit_pages = user_perms.can_edit_pages() - publishable_pages = user_perms.publishable_pages() - can_publish_pages = user_perms.can_publish_pages() + # To be replaced with the suggestion in the warning's message + with self.assertWarns(RemovedInWagtail60Warning): + editable_pages = user_perms.editable_pages() + with self.assertWarns(RemovedInWagtail60Warning): + can_edit_pages = user_perms.can_edit_pages() + with self.assertWarns(RemovedInWagtail60Warning): + publishable_pages = user_perms.publishable_pages() + with self.assertWarns(RemovedInWagtail60Warning): + can_publish_pages = user_perms.can_publish_pages() self.assertTrue(editable_pages.filter(id=homepage.id).exists()) self.assertTrue(editable_pages.filter(id=christmas_page.id).exists()) @@ -659,18 +675,17 @@ class TestPagePermission(TestCase): url_path="/home/events/someone-elses-event/" ) - with self.assertWarnsMessage( - RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", - ): - # Replace with PagePermissionPolicy().instances_user_has_permission_for() - user_perms = UserPagePermissionsProxy(user) + user_perms = UserPagePermissionsProxy(user) - editable_pages = user_perms.editable_pages() - can_edit_pages = user_perms.can_edit_pages() - publishable_pages = user_perms.publishable_pages() - can_publish_pages = user_perms.can_publish_pages() + # To be replaced with the suggestion in the warning's message + with self.assertWarns(RemovedInWagtail60Warning): + editable_pages = user_perms.editable_pages() + with self.assertWarns(RemovedInWagtail60Warning): + can_edit_pages = user_perms.can_edit_pages() + with self.assertWarns(RemovedInWagtail60Warning): + publishable_pages = user_perms.publishable_pages() + with self.assertWarns(RemovedInWagtail60Warning): + can_publish_pages = user_perms.can_publish_pages() self.assertFalse(editable_pages.filter(id=homepage.id).exists()) self.assertFalse(editable_pages.filter(id=christmas_page.id).exists()) @@ -720,16 +735,21 @@ class TestPagePermission(TestCase): christmas_page = EventPage.objects.get(url_path="/home/events/christmas/") locked_page = Page.objects.get(url_path="/home/my-locked-page/") + user_perms = UserPagePermissionsProxy(user) + with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(user) - user_perms = UserPagePermissionsProxy(user) + perms = user_perms.for_page(christmas_page) - perms = user_perms.for_page(christmas_page) - locked_perms = user_perms.for_page(locked_page) + with self.assertWarnsMessage( + RemovedInWagtail60Warning, + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", + ): + locked_perms = user_perms.for_page(locked_page) self.assertTrue(perms.can_lock()) self.assertFalse( @@ -743,10 +763,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(user) perms = UserPagePermissionsProxy(user).for_page(christmas_page) self.assertTrue(perms.can_lock()) @@ -762,10 +781,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(user) perms = UserPagePermissionsProxy(user).for_page(christmas_page) self.assertTrue(perms.can_lock()) @@ -787,10 +805,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(user) perms = UserPagePermissionsProxy(user).for_page(christmas_page) # Unlike in the previous test, the user can unlock this page as it was them who locked @@ -803,10 +820,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(user) perms = UserPagePermissionsProxy(user).for_page(christmas_page) self.assertFalse(perms.can_lock()) @@ -818,10 +834,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(user) perms = UserPagePermissionsProxy(user).for_page(christmas_page) self.assertFalse(perms.can_lock()) @@ -839,10 +854,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(user) perms = UserPagePermissionsProxy(user).for_page(christmas_page) self.assertTrue(perms.can_lock()) @@ -856,10 +870,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(user) perms = UserPagePermissionsProxy(user).for_page(christmas_page) self.assertFalse(perms.page_locked()) @@ -876,10 +889,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(user) perms = UserPagePermissionsProxy(user).for_page(christmas_page) # The user who locked the page shouldn't see the page as locked @@ -890,10 +902,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(other_user) other_perms = UserPagePermissionsProxy(other_user).for_page(christmas_page) self.assertTrue(other_perms.page_locked()) @@ -910,10 +921,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(user) perms = UserPagePermissionsProxy(user).for_page(christmas_page) # The user who locked the page should now also see the page as locked @@ -924,10 +934,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(other_user) other_perms = UserPagePermissionsProxy(other_user).for_page(christmas_page) self.assertTrue(other_perms.page_locked()) @@ -943,10 +952,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(moderator) moderator_perms = UserPagePermissionsProxy(moderator).for_page( christmas_page ) @@ -957,10 +965,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(superuser) superuser_perms = UserPagePermissionsProxy(superuser).for_page( christmas_page ) @@ -971,10 +978,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(editor) editor_perms = UserPagePermissionsProxy(editor).for_page(christmas_page) # the editor is not in the group assigned to moderate the task, so the page should @@ -991,10 +997,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(moderator) moderator_perms = UserPagePermissionsProxy(moderator).for_page( christmas_page ) @@ -1006,10 +1011,9 @@ class TestPagePermission(TestCase): with self.assertWarnsMessage( RemovedInWagtail60Warning, - "UserPagePermissionsProxy is deprecated. " - "Use wagtail.permission_policies.pages.PagePermissionPolicy instead.", + "UserPagePermissionsProxy.for_page() is deprecated. " + "Use page.permissions_for_user(user) instead.", ): - # Replace with page.permissions_for_user(editor) editor_perms = UserPagePermissionsProxy(editor).for_page(christmas_page) # the editor is not in the group assigned to moderate the task, so they can't lock or unlock the page