kopia lustrzana https://github.com/wagtail/wagtail
Add WAGTAIL_EDITING_SESSION_PING_INTERVAL setting
rodzic
78cfab1662
commit
f8564055b1
|
@ -67,6 +67,17 @@ describe('SessionController', () => {
|
|||
handlePing.mockClear();
|
||||
jest.advanceTimersByTime(123456);
|
||||
expect(handlePing).toHaveBeenCalledTimes(6);
|
||||
|
||||
// Setting it to 0 should stop the interval
|
||||
handlePing.mockClear();
|
||||
element.setAttribute('data-w-session-interval-value', '0');
|
||||
await Promise.resolve();
|
||||
|
||||
jest.advanceTimersByTime(20000);
|
||||
expect(handlePing).toHaveBeenCalledTimes(0);
|
||||
jest.advanceTimersByTime(20000);
|
||||
expect(handlePing).toHaveBeenCalledTimes(0);
|
||||
handlePing.mockClear();
|
||||
});
|
||||
|
||||
it('should allow setting a custom interval value on init and changing it afterwards', async () => {
|
||||
|
@ -100,6 +111,17 @@ describe('SessionController', () => {
|
|||
handlePing.mockClear();
|
||||
jest.advanceTimersByTime(123456);
|
||||
expect(handlePing).toHaveBeenCalledTimes(8);
|
||||
|
||||
// Setting it to >= 2**31 should stop the interval
|
||||
handlePing.mockClear();
|
||||
element.setAttribute('data-w-session-interval-value', `${2 ** 31}`);
|
||||
await Promise.resolve();
|
||||
|
||||
jest.advanceTimersByTime(20000);
|
||||
expect(handlePing).toHaveBeenCalledTimes(0);
|
||||
jest.advanceTimersByTime(20000);
|
||||
expect(handlePing).toHaveBeenCalledTimes(0);
|
||||
handlePing.mockClear();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -128,6 +128,10 @@ export class SessionController extends Controller<HTMLElement> {
|
|||
*/
|
||||
addInterval(): void {
|
||||
this.clearInterval();
|
||||
// Values outside this range will be ignored by window.setInterval,
|
||||
// making it fire all the time.
|
||||
if (this.intervalValue <= 0 || this.intervalValue >= 2 ** 31) return;
|
||||
|
||||
this.interval = window.setInterval(this.ping, this.intervalValue);
|
||||
}
|
||||
|
||||
|
|
|
@ -259,6 +259,16 @@ WAGTAIL_AUTO_UPDATE_PREVIEW_INTERVAL = 500
|
|||
|
||||
The interval (in milliseconds) is to check for changes made in the page editor before updating the preview. The default value is `500`.
|
||||
|
||||
(wagtail_editing_session_ping_interval)=
|
||||
|
||||
### `WAGTAIL_EDITING_SESSION_PING_INTERVAL`
|
||||
|
||||
```python
|
||||
WAGTAIL_EDITING_SESSION_PING_INTERVAL = 10000
|
||||
```
|
||||
|
||||
The interval (in milliseconds) to ping the server during an editing session. This is used to indicate that the session is active, as well as to display the list of other sessions that are currently editing the same content. The default value is `10000` (10 seconds). In order to effectively display the sessions list, this value needs to be set to under 1 minute. If set to 0, the interval will be disabled.
|
||||
|
||||
(wagtailadmin_global_edit_lock)=
|
||||
|
||||
### `WAGTAILADMIN_GLOBAL_EDIT_LOCK`
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
data-w-swap-defer-value="true"
|
||||
data-w-action-continue-value="true"
|
||||
data-w-action-url-value="{{ release_url }}"
|
||||
data-w-session-interval-value="{{ ping_interval }}"
|
||||
data-w-session-w-dialog-outlet="[data-edit-form] [data-controller='w-dialog']#w-overwrite-changes-dialog"
|
||||
data-action="w-session:ping->w-swap#submit visibilitychange@document->w-session#dispatchVisibilityState w-session:visible->w-session#ping w-session:visible->w-session#addInterval w-session:hidden->w-session#clearInterval w-session:hidden->w-action#sendBeacon w-unsaved:add@document->w-session#setUnsavedChanges w-unsaved:clear@document->w-session#setUnsavedChanges w-swap:json->w-session#updateSessionData"
|
||||
>
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.admin.utils import quote
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
from django.test import TestCase, override_settings
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from freezegun import freeze_time
|
||||
|
||||
from wagtail.admin.models import EditingSession
|
||||
from wagtail.models import GroupPagePermission, Page
|
||||
from wagtail.test.testapp.models import Advert, SimplePage
|
||||
from wagtail.test.testapp.models import (
|
||||
Advert,
|
||||
AdvertWithCustomPrimaryKey,
|
||||
FullFeaturedSnippet,
|
||||
SimplePage,
|
||||
)
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
|
||||
if settings.USE_TZ:
|
||||
|
@ -1180,3 +1186,151 @@ class TestReleaseView(WagtailTestUtils, TestCase):
|
|||
self.assertTrue(
|
||||
EditingSession.objects.filter(id=self.other_session.id).exists()
|
||||
)
|
||||
|
||||
|
||||
class TestModuleInEditView(WagtailTestUtils, TestCase):
|
||||
url_name = "wagtailadmin_pages:edit"
|
||||
model = Page
|
||||
|
||||
def setUp(self):
|
||||
self.user = self.create_superuser(
|
||||
"bob", password="password", first_name="Bob", last_name="Testuser"
|
||||
)
|
||||
self.login(user=self.user)
|
||||
self.content_type = ContentType.objects.get_for_model(self.model)
|
||||
|
||||
self.object = self.create_object()
|
||||
|
||||
self.session = EditingSession.objects.create(
|
||||
user=self.user,
|
||||
content_type=self.content_type,
|
||||
object_id=self.object.pk,
|
||||
last_seen_at=TIMESTAMP_1,
|
||||
)
|
||||
self.old_session = EditingSession.objects.create(
|
||||
user=self.user,
|
||||
content_type=self.content_type,
|
||||
object_id=self.object.pk,
|
||||
last_seen_at=TIMESTAMP_PAST,
|
||||
)
|
||||
|
||||
def create_object(self):
|
||||
root_page = Page.get_first_root_node()
|
||||
page = SimplePage(title="Foo", slug="foo", content="bar")
|
||||
root_page.add_child(instance=page)
|
||||
page.save_revision()
|
||||
return page
|
||||
|
||||
def get(self):
|
||||
return self.client.get(reverse(self.url_name, args=(quote(self.object.pk),)))
|
||||
|
||||
def assertRevisionInput(self, soup):
|
||||
revision_input = soup.select_one('input[name="revision_id"]')
|
||||
self.assertIsNotNone(revision_input)
|
||||
self.assertEqual(revision_input.get("type"), "hidden")
|
||||
self.assertEqual(
|
||||
revision_input.get("value"),
|
||||
str(self.object.latest_revision.id),
|
||||
)
|
||||
|
||||
@freeze_time(TIMESTAMP_NOW)
|
||||
def test_edit_view_with_default_interval(self):
|
||||
self.assertEqual(EditingSession.objects.all().count(), 2)
|
||||
response = self.get()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Should perform a cleanup of the EditingSessions
|
||||
self.assertTrue(EditingSession.objects.filter(id=self.session.id).exists())
|
||||
self.assertFalse(EditingSession.objects.filter(id=self.old_session.id).exists())
|
||||
|
||||
# Should create a new EditingSession for the current user
|
||||
self.assertEqual(EditingSession.objects.all().count(), 2)
|
||||
new_session = EditingSession.objects.exclude(id=self.session.id).get(
|
||||
content_type=self.content_type,
|
||||
object_id=self.object.pk,
|
||||
)
|
||||
self.assertEqual(new_session.user, self.user)
|
||||
|
||||
# Should load the EditingSessionsModule with the default interval (10s)
|
||||
soup = self.get_soup(response.content)
|
||||
module = soup.select_one('form[data-controller~="w-session"]')
|
||||
self.assertIsNotNone(module)
|
||||
self.assertEqual(module.get("data-w-session-interval-value"), "10000")
|
||||
|
||||
# Should show the revision_id input
|
||||
self.assertRevisionInput(module)
|
||||
|
||||
@freeze_time(TIMESTAMP_NOW)
|
||||
@override_settings(WAGTAIL_EDITING_SESSION_PING_INTERVAL=30000)
|
||||
def test_edit_view_with_custom_interval(self):
|
||||
self.assertEqual(EditingSession.objects.all().count(), 2)
|
||||
response = self.get()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Should perform a cleanup of the EditingSessions
|
||||
self.assertTrue(EditingSession.objects.filter(id=self.session.id).exists())
|
||||
self.assertFalse(EditingSession.objects.filter(id=self.old_session.id).exists())
|
||||
|
||||
# Should create a new EditingSession for the current user
|
||||
self.assertEqual(EditingSession.objects.all().count(), 2)
|
||||
new_session = EditingSession.objects.exclude(id=self.session.id).get(
|
||||
content_type=self.content_type,
|
||||
object_id=self.object.pk,
|
||||
)
|
||||
self.assertEqual(new_session.user, self.user)
|
||||
|
||||
# Should load the EditingSessionsModule
|
||||
soup = self.get_soup(response.content)
|
||||
module = soup.select_one('form[data-controller~="w-session"]')
|
||||
self.assertIsNotNone(module)
|
||||
self.assertEqual(
|
||||
module.get("data-w-swap-src-value"),
|
||||
reverse(
|
||||
"wagtailadmin_editing_sessions:ping",
|
||||
args=(
|
||||
self.content_type.app_label,
|
||||
self.content_type.model,
|
||||
quote(self.object.pk),
|
||||
new_session.id,
|
||||
),
|
||||
),
|
||||
)
|
||||
self.assertEqual(
|
||||
module.get("data-w-action-url-value"),
|
||||
reverse(
|
||||
"wagtailadmin_editing_sessions:release",
|
||||
args=(new_session.id,),
|
||||
),
|
||||
)
|
||||
|
||||
# Should use the custom interval (30s)
|
||||
self.assertEqual(module.get("data-w-session-interval-value"), "30000")
|
||||
self.assertRevisionInput(module)
|
||||
|
||||
|
||||
class TestModuleInEditViewWithRevisableSnippet(TestModuleInEditView):
|
||||
model = FullFeaturedSnippet
|
||||
|
||||
@property
|
||||
def url_name(self):
|
||||
return self.model.snippet_viewset.get_url_name("edit")
|
||||
|
||||
def create_object(self):
|
||||
obj = self.model.objects.create(text="Shodan")
|
||||
obj.save_revision()
|
||||
return obj
|
||||
|
||||
|
||||
class TestModuleInEditViewWithNonRevisableSnippet(TestModuleInEditView):
|
||||
model = AdvertWithCustomPrimaryKey
|
||||
|
||||
@property
|
||||
def url_name(self):
|
||||
return self.model.snippet_viewset.get_url_name("edit")
|
||||
|
||||
def create_object(self):
|
||||
return self.model.objects.create(text="GLaDOS", advert_id="m0n5t3r!/#")
|
||||
|
||||
def assertRevisionInput(self, soup):
|
||||
revision_input = soup.select_one('input[name="revision_id"]')
|
||||
self.assertIsNone(revision_input)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from django.conf import settings
|
||||
|
||||
from wagtail.admin.ui.components import Component
|
||||
|
||||
|
||||
|
@ -23,10 +25,16 @@ class EditingSessionsModule(Component):
|
|||
self.revision_id = revision_id
|
||||
|
||||
def get_context_data(self, parent_context):
|
||||
ping_interval = getattr(
|
||||
settings,
|
||||
"WAGTAIL_EDITING_SESSION_PING_INTERVAL",
|
||||
10000,
|
||||
)
|
||||
return {
|
||||
"current_session": self.current_session,
|
||||
"ping_url": self.ping_url,
|
||||
"release_url": self.release_url,
|
||||
"ping_interval": str(ping_interval), # avoid the need to | unlocalize
|
||||
"sessions_list": self.sessions_list,
|
||||
"content_type": self.content_type,
|
||||
"revision_id": self.revision_id,
|
||||
|
|
Ładowanie…
Reference in New Issue