Fix bug with pagination usage.
[django-migdal.git] / migdal / models.py
index 54091e4..7cef388 100644 (file)
@@ -2,54 +2,70 @@
 # This file is part of PrawoKultury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 # This file is part of PrawoKultury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
+import re
 from datetime import datetime
 from django.conf import settings
 from django.contrib.sites.models import Site
 from django.core.exceptions import ValidationError
 from datetime import datetime
 from django.conf import settings
 from django.contrib.sites.models import Site
 from django.core.exceptions import ValidationError
-from django.core.mail import send_mail
+from django.core.mail import mail_managers, send_mail
 from django.db import models
 from django.template import loader, Context
 from django.db import models
 from django.template import loader, Context
-from django.utils.translation import get_language, ugettext_lazy as _, ugettext
+from django.utils.translation import ugettext_lazy as _, ugettext
 from django_comments_xtd.models import XtdComment
 from django_comments_xtd.models import XtdComment
-from markupfield.fields import MarkupField
+from fnpdjango.utils.fields import TextileField
+from fnpdjango.utils.models.translation import add_translatable, tQ
 from migdal import app_settings
 from migdal import app_settings
-from fnpdjango.utils.models.translation import add_translatable
 from migdal.fields import SlugNullField
 
 from migdal.fields import SlugNullField
 
+
 class Category(models.Model):
 class Category(models.Model):
-    taxonomy = models.CharField(_('taxonomy'), max_length=32,
-                    choices=app_settings.TAXONOMIES)
+    taxonomy = models.CharField(_('taxonomy'), max_length=32, choices=app_settings.TAXONOMIES)
 
     class Meta:
         verbose_name = _('category')
         verbose_name_plural = _('categories')
 
     def __unicode__(self):
 
     class Meta:
         verbose_name = _('category')
         verbose_name_plural = _('categories')
 
     def __unicode__(self):
-        return self.title
+        return self.title or u""
 
     @models.permalink
     def get_absolute_url(self):
 
     @models.permalink
     def get_absolute_url(self):
-        return ('migdal_category', [self.slug])
+        return 'migdal_category', [self.slug]
 
 
 
 
-add_translatable(Category, {
+add_translatable(Category, languages=app_settings.LANGUAGES, fields={
     'title': models.CharField(max_length=64, unique=True, db_index=True),
     'slug': models.SlugField(unique=True, db_index=True),
 })
 
 
     'title': models.CharField(max_length=64, unique=True, db_index=True),
     'slug': models.SlugField(unique=True, db_index=True),
 })
 
 
+class PublishedEntryManager(models.Manager):
+    def get_queryset(self):
+        return super(PublishedEntryManager, self).get_queryset().filter(
+                tQ(published=True)
+            )
+
+
 class Entry(models.Model):
 class Entry(models.Model):
-    type = models.CharField(max_length=16,
-            choices=((t.db, t.slug) for t in app_settings.TYPES),
-            db_index=True)
+    type = models.CharField(
+        max_length=16,
+        choices=((t.db, t.slug) for t in app_settings.TYPES),
+        db_index=True)
     date = models.DateTimeField(_('created at'), auto_now_add=True, db_index=True)
     changed_at = models.DateTimeField(_('changed at'), auto_now=True, db_index=True)
     author = models.CharField(_('author'), max_length=128)
     date = models.DateTimeField(_('created at'), auto_now_add=True, db_index=True)
     changed_at = models.DateTimeField(_('changed at'), auto_now=True, db_index=True)
     author = models.CharField(_('author'), max_length=128)
-    author_email = models.EmailField(_('author email'), max_length=128, null=True, blank=True,
-            help_text=_('Used only to display gravatar and send notifications.'))
+    author_email = models.EmailField(
+        _('author email'), max_length=128, null=True, blank=True,
+        help_text=_('Used only to display gravatar and send notifications.'))
     image = models.ImageField(_('image'), upload_to='entry/image/', null=True, blank=True)
     promo = models.BooleanField(_('promoted'), default=False)
     image = models.ImageField(_('image'), upload_to='entry/image/', null=True, blank=True)
     promo = models.BooleanField(_('promoted'), default=False)
-    categories = models.ManyToManyField(Category, null=True, blank=True, verbose_name=_('categories'))
+    in_stream = models.BooleanField(_('in stream'), default=True)
+    categories = models.ManyToManyField(Category, blank=True, verbose_name=_('categories'))
+    first_published_at = models.DateTimeField(_('published at'), null=True, blank=True)
+    canonical_url = models.URLField(_('canonical link'), null=True, blank=True)
+
+    objects = models.Manager()
+    published_objects = PublishedEntryManager()
 
     class Meta:
         verbose_name = _('entry')
 
     class Meta:
         verbose_name = _('entry')
@@ -60,20 +76,21 @@ class Entry(models.Model):
         return self.title
 
     def save(self, *args, **kwargs):
         return self.title
 
     def save(self, *args, **kwargs):
-        if self.pk is not None:
-            orig = type(self).objects.get(pk=self.pk)
-            published_now = False
-            for lc, ln in settings.LANGUAGES:
-                if (getattr(self, "published_%s" % lc)
-                        and getattr(self, "published_at_%s" % lc) is None):
-                    setattr(self, "published_at_%s" % lc, datetime.now())
+        published_now = False
+        for lc, ln in app_settings.LANGUAGES:
+            if (getattr(self, "published_%s" % lc)
+                    and getattr(self, "published_at_%s" % lc) is None):
+                now = datetime.now()
+                setattr(self, "published_at_%s" % lc, now)
+                if self.first_published_at is None:
+                    self.first_published_at = now
                     published_now = True
                     published_now = True
-            if published_now:
-                self.notify_author_published()
         super(Entry, self).save(*args, **kwargs)
         super(Entry, self).save(*args, **kwargs)
+        if published_now and self.pk is not None:
+            self.notify_author_published()
 
     def clean(self):
 
     def clean(self):
-        for lc, ln in settings.LANGUAGES:
+        for lc, ln in app_settings.LANGUAGES:
             if (getattr(self, "published_%s" % lc) and
                     not getattr(self, "slug_%s" % lc)):
                 raise ValidationError(
             if (getattr(self, "published_%s" % lc) and
                     not getattr(self, "slug_%s" % lc)):
                 raise ValidationError(
@@ -81,7 +98,7 @@ class Entry(models.Model):
 
     @models.permalink
     def get_absolute_url(self):
 
     @models.permalink
     def get_absolute_url(self):
-        return ('migdal_entry_%s' % self.type, [self.slug])
+        return 'migdal_entry_%s' % self.type, [self.slug]
 
     def get_type(self):
         return dict(app_settings.TYPES_DICT)[self.type]
 
     def get_type(self):
         return dict(app_settings.TYPES_DICT)[self.type]
@@ -100,6 +117,12 @@ class Entry(models.Model):
             mail_text, settings.SERVER_EMAIL, [self.author_email]
         )
 
             mail_text, settings.SERVER_EMAIL, [self.author_email]
         )
 
+    def inline_html(self):
+        for att in self.attachment_set.all():
+            if att.file.name.endswith(".html"):
+                with open(att.file.path) as f:
+                    yield f.read()
+
 
 add_translatable(Entry, languages=app_settings.OPTIONAL_LANGUAGES, fields={
     'needed': models.CharField(_('needed'), max_length=1, db_index=True, choices=(
 
 add_translatable(Entry, languages=app_settings.OPTIONAL_LANGUAGES, fields={
     'needed': models.CharField(_('needed'), max_length=1, db_index=True, choices=(
@@ -107,13 +130,17 @@ add_translatable(Entry, languages=app_settings.OPTIONAL_LANGUAGES, fields={
                 default='n'),
 })
 
                 default='n'),
 })
 
-add_translatable(Entry, {
+TEXTILE_HELP = _('Use <a href="https://txstyle.org/article/44/an-overview-of-the-textile-syntax">Textile</a> syntax.')
+
+add_translatable(Entry, languages=app_settings.LANGUAGES, fields={
     'slug': SlugNullField(unique=True, db_index=True, null=True, blank=True),
     'title': models.CharField(_('title'), max_length=255, null=True, blank=True),
     'slug': SlugNullField(unique=True, db_index=True, null=True, blank=True),
     'title': models.CharField(_('title'), max_length=255, null=True, blank=True),
-    'lead': MarkupField(_('lead'), markup_type='textile_pl', null=True, blank=True,
-                help_text=_('Use <a href="http://textile.thresholdstate.com/">Textile</a> syntax.')),
-    'body': MarkupField(_('body'), markup_type='textile_pl', null=True, blank=True,
-                help_text=_('Use <a href="http://textile.thresholdstate.com/">Textile</a> syntax.')),
+    'lead': TextileField(
+        _('lead'), markup_type='textile_pl', null=True, blank=True, help_text=TEXTILE_HELP),
+    'body': TextileField(
+        _('body'), markup_type='textile_pl', null=True, blank=True, help_text=TEXTILE_HELP),
+    'place': models.CharField(_('place'), null=True, blank=True, max_length=256),
+    'time': models.CharField(_('time'), null=True, blank=True, max_length=256),
     'published': models.BooleanField(_('published'), default=False),
     'published_at': models.DateTimeField(_('published at'), null=True, blank=True),
 })
     'published': models.BooleanField(_('published'), default=False),
     'published_at': models.DateTimeField(_('published at'), null=True, blank=True),
 })
@@ -127,10 +154,16 @@ class Attachment(models.Model):
         return self.file.url if self.file else ''
 
 
         return self.file.url if self.file else ''
 
 
+class Photo(models.Model):
+    image = models.ImageField(_('image'), upload_to='entry/photo/')
+    entry = models.ForeignKey(Entry)
+
+    def url(self):
+        return self.image.url if self.image else ''
+
 
 def notify_new_comment(sender, instance, created, **kwargs):
 
 def notify_new_comment(sender, instance, created, **kwargs):
-    if (created and isinstance(instance.content_object, Entry) and
-                instance.content_object.author_email):
+    if created and isinstance(instance.content_object, Entry) and instance.content_object.author_email:
         site = Site.objects.get_current()
         mail_text = loader.get_template('migdal/mail/new_comment.txt').render(
             Context({
         site = Site.objects.get_current()
         mail_text = loader.get_template('migdal/mail/new_comment.txt').render(
             Context({
@@ -142,4 +175,21 @@ def notify_new_comment(sender, instance, created, **kwargs):
             mail_text, settings.SERVER_EMAIL, 
             [instance.content_object.author_email]
         )
             mail_text, settings.SERVER_EMAIL, 
             [instance.content_object.author_email]
         )
-models.signals.post_save.connect(notify_new_comment, sender=XtdComment)
\ No newline at end of file
+models.signals.post_save.connect(notify_new_comment, sender=XtdComment)
+
+
+def spamfilter(sender, comment, **kwargs):
+    """Very simple spam filter. Just don't let any HTML links go through."""
+    if re.search(r"<a\s+href=", comment.comment):
+        fields = (
+            comment.user, comment.user_name, comment.user_email,
+            comment.user_url, comment.submit_date, comment.ip_address,
+            comment.followup, comment.comment)
+        mail_managers(
+            u"Spam filter report",
+            (u"""This comment was turned down as SPAM: \n""" +
+             """\n%s""" * len(fields) +
+             """\n\nYou don't have to do anything.""") % fields)
+        return False
+    return True
+# comment_will_be_posted.connect(spamfilter)