diff --git a/publisher/managers.py b/publisher/managers.py index 814505b..2e13155 100644 --- a/publisher/managers.py +++ b/publisher/managers.py @@ -1,4 +1,6 @@ from django.db import models +from django.db.models import Q +from django.utils import timezone from .signals import publisher_pre_delete from .middleware import get_draft_status @@ -16,7 +18,12 @@ def drafts(self): def published(self): from .models import PublisherModelBase - return self.filter(publisher_is_draft=PublisherModelBase.STATE_PUBLISHED) + + return self.filter( + Q(publication_start_date__isnull=True) | Q(publication_start_date__lte=timezone.now()), + Q(publication_end_date__isnull=True) | Q(publication_end_date__gt=timezone.now()), + publisher_is_draft=PublisherModelBase.STATE_PUBLISHED, + ) def current(self): if get_draft_status(): diff --git a/publisher/models.py b/publisher/models.py index 0d7967a..96c4077 100644 --- a/publisher/models.py +++ b/publisher/models.py @@ -1,6 +1,8 @@ from django.utils import timezone from django.db import models from django.core.exceptions import ObjectDoesNotExist +from django.utils.translation import ugettext_lazy as _ +from django.utils import timezone from .managers import PublisherManager from .utils import assert_draft @@ -33,6 +35,23 @@ class PublisherModelBase(models.Model): publisher_published_at = models.DateTimeField(null=True, editable=False) + publication_start_date = models.DateTimeField( + _("publication start date"), + null=True, blank=True, db_index=True, + help_text=_( + "Published content will only be visible from this point in time." + " Leave blank if always visible." + ) + ) + publication_end_date = models.DateTimeField( + _("publication end date"), + null=True, blank=True, db_index=True, + help_text=_( + "When to expire the published version." + " Leave empty to never expire." + ), + ) + publisher_fields = ( 'publisher_linked', 'publisher_is_draft', @@ -60,6 +79,22 @@ def is_draft(self): def is_published(self): return self.publisher_is_draft == self.STATE_PUBLISHED + @property + def hidden_by_end_date(self): + if not self.publication_end_date: + return False + return self.publication_end_date <= timezone.now() + + @property + def hidden_by_start_date(self): + if not self.publication_start_date: + return False + return self.publication_start_date >= timezone.now() + + @property + def is_visible(self): + return self.is_published and (not self.hidden_by_end_date) and (not self.hidden_by_start_date) + @property def is_dirty(self): if not self.is_draft: diff --git a/tests/myapp/tests.py b/tests/myapp/tests.py index eacb9bf..8ff1ca6 100644 --- a/tests/myapp/tests.py +++ b/tests/myapp/tests.py @@ -2,7 +2,7 @@ from django import test from django.utils import timezone - +from django.utils.timezone import now as tz_now from mock import MagicMock from publisher.utils import NotDraftException @@ -11,7 +11,6 @@ from myapp.models import PublisherTestModel - class PublisherTest(test.TestCase): def test_creating_model_creates_only_one_record(self): @@ -316,3 +315,115 @@ def test_middleware_forgets_current_draft_status_after_request(self): PublisherMiddleware.process_response(None, None) self.assertFalse(get_draft_status()) + + def test_publication_start_date(self): + yesterday = tz_now() - datetime.timedelta(days=1) + tomorrow = tz_now() + datetime.timedelta(days=1) + + instance = PublisherTestModel.publisher_manager.create(title='Test model') + instance.publish() + + # No publication_start_date set: + + published = PublisherTestModel.publisher_manager.published() + self.assertEqual(published.count(), 1) + # Check model instance + obj = published[0] + self.assertEqual(obj.publication_start_date, None) + self.assertEqual(obj.publication_end_date, None) + self.assertEqual(obj.is_published, True) + self.assertEqual(obj.hidden_by_end_date, False) + self.assertEqual(obj.hidden_by_start_date, False) + self.assertEqual(obj.is_visible, True) + + # Hidden, because publication_start_date is in the future: + + instance.publication_start_date = tomorrow + instance.save() + instance.publish() + published = PublisherTestModel.publisher_manager.published() + self.assertEqual(published.count(), 0) + count = PublisherTestModel.publisher_manager.all().count() + self.assertEqual(count, 2) # draft + published + draft = PublisherTestModel.publisher_manager.drafts()[0] + self.assertEqual(draft.publication_start_date, tomorrow) + # Check model instance + obj = PublisherTestModel.objects.filter(publisher_is_draft=PublisherTestModel.STATE_PUBLISHED)[0] + self.assertEqual(obj.publication_start_date, tomorrow) + self.assertEqual(obj.publication_end_date, None) + self.assertEqual(obj.is_published, True) + self.assertEqual(obj.hidden_by_end_date, False) + self.assertEqual(obj.hidden_by_start_date, True) + self.assertEqual(obj.is_visible, False) + + # Visible, because publication_start_date is in the past: + + instance.publication_start_date = yesterday + instance.save() + instance.publish() + published = PublisherTestModel.publisher_manager.published() + self.assertEqual(published.count(), 1) + # Check model instance + obj = published[0] + self.assertEqual(obj.publication_start_date, yesterday) + self.assertEqual(obj.publication_end_date, None) + self.assertEqual(obj.is_published, True) + self.assertEqual(obj.hidden_by_end_date, False) + self.assertEqual(obj.hidden_by_start_date, False) + self.assertEqual(obj.is_visible, True) + + + def test_publication_end_date(self): + yesterday = tz_now() - datetime.timedelta(days=1) + tomorrow = tz_now() + datetime.timedelta(days=1) + + instance = PublisherTestModel.publisher_manager.create(title='Test model') + instance.publish() + + # No publication_end_date set: + published = PublisherTestModel.publisher_manager.published() + self.assertEqual(published.count(), 1) + # Check model instance + obj = published[0] + self.assertEqual(obj.publication_start_date, None) + self.assertEqual(obj.publication_end_date, None) + self.assertEqual(obj.is_published, True) + self.assertEqual(obj.hidden_by_end_date, False) + self.assertEqual(obj.hidden_by_start_date, False) + self.assertEqual(obj.is_visible, True) + + # Hidden, because publication_end_date is in the past: + instance.publication_end_date = yesterday + instance.save() + instance.publish() + published = PublisherTestModel.publisher_manager.published() + self.assertEqual(published.count(), 0) + count = PublisherTestModel.publisher_manager.all().count() + self.assertEqual(count, 2) # draft + published + draft = PublisherTestModel.publisher_manager.drafts()[0] + self.assertEqual(draft.publication_start_date, None) + self.assertEqual(draft.publication_end_date, yesterday) + # Check model instance + obj = PublisherTestModel.objects.filter(publisher_is_draft=PublisherTestModel.STATE_PUBLISHED)[0] + self.assertEqual(obj.publication_start_date, None) + self.assertEqual(obj.publication_end_date, yesterday) + self.assertEqual(obj.is_published, True) + self.assertEqual(obj.hidden_by_end_date, True) + self.assertEqual(obj.hidden_by_start_date, False) + self.assertEqual(obj.is_visible, False) + + # Visible, because publication_end_date is in the future: + instance.publication_end_date = tomorrow + instance.save() + instance.publish() + published = PublisherTestModel.publisher_manager.published() + self.assertEqual(published.count(), 1) + # Check model instance + obj = published[0] + self.assertEqual(obj.publication_start_date, None) + self.assertEqual(obj.publication_end_date, tomorrow) + self.assertEqual(obj.is_published, True) + self.assertEqual(obj.hidden_by_end_date, False) + self.assertEqual(obj.hidden_by_start_date, False) + self.assertEqual(obj.is_visible, True) +