~widelands-dev/widelands-website/trunk

22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
1
from datetime import datetime
274 by Holger Rapp
Disable wikiwords in the forums
2
from mainpage.templatetags.wl_markdown import do_wl_markdown
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
3
import os.path
254 by Holger Rapp
Fixed a few deprecation warnings and reparied cross site scripting fixes for AJAX requests
4
import hashlib
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
5
6
from django.db import models
7
from django.contrib.auth.models import User
512.1.12 by franku
cleanups
8
from django.contrib.auth.models import Group
489.1.13 by franku
django.conf.urlresolvers -> django.urls
9
from django.urls import reverse
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
10
from django.utils.html import strip_tags
11
from django.utils.translation import ugettext_lazy as _
12
from django.conf import settings
13
192 by Holger Rapp
Trying to fix 338756
14
from pybb.markups import mypostmarkup
513.4.1 by franku
removed: column moderators and corresponding view, model privatemessage, users view, memoize method
15
from pybb.util import urlize, unescape
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
16
from pybb import settings as pybb_settings
90 by Holger Rapp
Sphinx must now be enabled in local_settings.py, otherwise it won't work
17
18
from django.conf import settings
427.1.8 by franku
moved unhide_post to pybb.models
19
from notification.models import send
504.2.9 by franku
some refactoring
20
from check_input.models import SuspiciousInput
21
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
22
209.1.21 by Timo Wingender
Add notifications to forum
23
try:
24
    from notification import models as notification
25
    from django.db.models import signals
26
except ImportError:
27
    notification = None
28
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
29
MARKUP_CHOICES = (
30
    ('markdown', 'markdown'),
209.1.68 by Timo Wingender
add field to choose between markdown and bbcode as markup
31
    ('bbcode', 'bbcode'),
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
32
)
33
512.1.1 by franku
first working version for implementing an internal forum
34
class PybbExcludeInternal(models.Manager):
35
    def get_queryset(self):
512.1.3 by franku
call the added field internal, instead of official; some variable refactoring;better filtering
36
        return super(PybbExcludeInternal, self).get_queryset().exclude(internal=True)
512.1.1 by franku
first working version for implementing an internal forum
37
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
38
39
class Category(models.Model):
512.1.3 by franku
call the added field internal, instead of official; some variable refactoring;better filtering
40
    """The base model of pybb.
41
    
42
    If 'internal' is set to True, the category is only visible for superusers and
43
    users which have the permission 'can_access_internal'.
44
    """
45
    
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
46
    name = models.CharField(_('Name'), max_length=80)
47
    position = models.IntegerField(_('Position'), blank=True, default=0)
512.1.3 by franku
call the added field internal, instead of official; some variable refactoring;better filtering
48
    internal = models.BooleanField(
49
        default=False,
50
        verbose_name=_('Internal Category'),
51
        help_text=_('If set, this category is only visible for special users.')
52
        )
512.1.1 by franku
first working version for implementing an internal forum
53
54
    objects = models.Manager()
55
    exclude_internal = PybbExcludeInternal()
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
56
57
    class Meta:
58
        ordering = ['position']
59
        verbose_name = _('Category')
60
        verbose_name_plural = _('Categories')
512.1.14 by franku
small change
61
        # See also settings.INTERNAL_PERM
512.1.3 by franku
call the added field internal, instead of official; some variable refactoring;better filtering
62
        permissions = (("can_access_internal", "Can access Internal Forums"),)
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
63
64
    def __unicode__(self):
65
        return self.name
66
67
    def forum_count(self):
68
        return self.forums.all().count()
69
70
    def get_absolute_url(self):
71
        return reverse('pybb_category', args=[self.id])
72
73
    @property
74
    def topics(self):
75
        return Topic.objects.filter(forum__category=self).select_related()
192 by Holger Rapp
Trying to fix 338756
76
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
77
    @property
78
    def posts(self):
79
        return Post.objects.filter(topic__forum__category=self).select_related()
80
81
82
class Forum(models.Model):
438.1.6 by franku
run the script
83
    category = models.ForeignKey(
84
        Category, related_name='forums', verbose_name=_('Category'))
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
85
    name = models.CharField(_('Name'), max_length=80)
86
    position = models.IntegerField(_('Position'), blank=True, default=0)
87
    description = models.TextField(_('Description'), blank=True, default='')
88
    updated = models.DateTimeField(_('Updated'), null=True)
512.1.3 by franku
call the added field internal, instead of official; some variable refactoring;better filtering
89
    moderator_group = models.ForeignKey(
90
        Group,
91
        on_delete=models.CASCADE,
92
        blank=True,
93
        null=True,
94
        default=None,
95
        help_text='Users in this Group will have administrative permissions in this Forum.',
96
    )
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
97
98
    class Meta:
99
        ordering = ['position']
100
        verbose_name = _('Forum')
101
        verbose_name_plural = _('Forums')
102
103
    def __unicode__(self):
104
        return self.name
105
106
    def topic_count(self):
107
        return self.topics.all().count()
108
109
    def get_absolute_url(self):
110
        return reverse('pybb_forum', args=[self.id])
192 by Holger Rapp
Trying to fix 338756
111
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
112
    @property
113
    def posts(self):
504.2.8 by franku
try to increase performance; removed unused template contexts
114
        return Post.objects.filter(topic__forum=self).exclude(hidden=True).select_related()
115
116
    @property
117
    def post_count(self):
118
        return Post.objects.filter(topic__forum=self).exclude(hidden=True).count()
119
120
    @property
121
    def last_post(self):
504.2.13 by franku
addressed code review
122
        # This has better performance than using the posts manager hidden_topics
504.2.12 by franku
performance boost
123
        # We search only for the last 10 topics
124
        topics = self.topics.order_by('-updated')[:10]
512.1.1 by franku
first working version for implementing an internal forum
125
        posts = []
504.2.12 by franku
performance boost
126
        for topic in topics:
127
            if topic.is_hidden:
128
                continue
129
            posts = topic.posts.exclude(hidden=True).order_by(
438.1.6 by franku
run the script
130
            '-created').select_related()
504.2.12 by franku
performance boost
131
            break
132
422.1.1 by franku
add hided field to topic and post; hide topics; added akisemt python library
133
        try:
134
            return posts[0]
135
        except IndexError:
136
            return None
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
137
438.1.6 by franku
run the script
138
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
139
class Topic(models.Model):
438.1.6 by franku
run the script
140
    forum = models.ForeignKey(
141
        Forum, related_name='topics', verbose_name=_('Forum'))
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
142
    name = models.CharField(_('Subject'), max_length=255)
143
    created = models.DateTimeField(_('Created'), null=True)
144
    updated = models.DateTimeField(_('Updated'), null=True)
145
    user = models.ForeignKey(User, verbose_name=_('User'))
146
    views = models.IntegerField(_('Views count'), blank=True, default=0)
147
    sticky = models.BooleanField(_('Sticky'), blank=True, default=False)
148
    closed = models.BooleanField(_('Closed'), blank=True, default=False)
438.1.6 by franku
run the script
149
    subscribers = models.ManyToManyField(
150
        User, related_name='subscriptions', verbose_name=_('Subscribers'), blank=True)
192 by Holger Rapp
Trying to fix 338756
151
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
152
    class Meta:
192 by Holger Rapp
Trying to fix 338756
153
        ordering = ['-updated']
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
154
        verbose_name = _('Topic')
155
        verbose_name_plural = _('Topics')
156
157
    def __unicode__(self):
158
        return self.name
192 by Holger Rapp
Trying to fix 338756
159
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
160
    @property
161
    def head(self):
504.2.11 by franku
prevent errors for admin page pybb topics if there is a topic without a post
162
        try:
163
            return self.posts.all().order_by('created').select_related()[0]
164
        except:
165
            return None
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
166
167
    @property
168
    def last_post(self):
504.2.8 by franku
try to increase performance; removed unused template contexts
169
        return self.posts.exclude(hidden=True).order_by('-created').select_related()[0]
422.1.9 by Holger Rapp
Minor nits.
170
422.1.5 by franku
completed hiding of spam to normal users
171
    @property
422.1.8 by GunChleoc
Proofreading.
172
    def is_hidden(self):
504.2.11 by franku
prevent errors for admin page pybb topics if there is a topic without a post
173
        # If the first post of this topic is hidden, the topic is hidden
174
        try:
175
            return self.posts.first().hidden
176
        except:
177
            return False
422.1.9 by Holger Rapp
Minor nits.
178
422.1.5 by franku
completed hiding of spam to normal users
179
    @property
323 by Holger Rapp
Let post count be calculated automatically instead of keeping track of it manually. Let's see how this affects performance
180
    def post_count(self):
429.1.2 by franku
reverted changes to pybb views; instead provide only non hidden adat for them; added django command
181
        return Post.objects.filter(topic=self).exclude(hidden=True).count()
323 by Holger Rapp
Let post count be calculated automatically instead of keeping track of it manually. Let's see how this affects performance
182
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
183
    def get_absolute_url(self):
184
        return reverse('pybb_topic', args=[self.id])
185
186
    def save(self, *args, **kwargs):
209.1.21 by Timo Wingender
Add notifications to forum
187
        new = self.id is None
188
        if new:
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
189
            self.created = datetime.now()
190
        super(Topic, self).save(*args, **kwargs)
191
192
    def update_read(self, user):
193
        read, new = Read.objects.get_or_create(user=user, topic=self)
194
        if not new:
195
            read.time = datetime.now()
196
            read.save()
197
438.1.6 by franku
run the script
198
    # def has_unreads(self, user):
199
        # try:
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
200
            #read = Read.objects.get(user=user, topic=self)
438.1.6 by franku
run the script
201
        # except Read.DoesNotExist:
202
            # return True
203
        # else:
204
            # return self.updated > read.time
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
205
206
207
class RenderableItem(models.Model):
438.1.6 by franku
run the script
208
    """Base class for models that has markup, body, body_text and body_html
209
    fields."""
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
210
211
    class Meta:
212
        abstract = True
213
214
    def render(self):
215
        if self.markup == 'bbcode':
216
            self.body_html = mypostmarkup.markup(self.body, auto_urls=False)
217
        elif self.markup == 'markdown':
438.1.6 by franku
run the script
218
            self.body_html = unicode(do_wl_markdown(
449.2.4 by franku
adjusted backlinks; cleanup
219
                self.body, 'bleachit'))
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
220
        else:
221
            raise Exception('Invalid markup property: %s' % self.markup)
222
223
        # Remove tags which was generated with the markup processor
224
        text = strip_tags(self.body_html)
225
226
        # Unescape entities which was generated with the markup processor
227
        self.body_text = unescape(text)
228
229
        self.body_html = urlize(self.body_html)
230
438.1.6 by franku
run the script
231
504.2.4 by franku
Do not show hidden topics in feeds
232
class HiddenTopicsManager(models.Manager):
233
    """Find all hidden topics by posts.
234
235
    A whole topic is hidden, if the first post is hidden.
236
    This manager returns the hidden topics and can be used to filter them out
237
    like so:
238
504.2.9 by franku
some refactoring
239
    Post.objects.exclude(topic__in=Post.hidden_topics.all()).filter(...)
504.2.5 by franku
implement hide/unhide toggling of topics
240
504.2.13 by franku
addressed code review
241
    Use this with caution, because it affects performance, see:
504.2.8 by franku
try to increase performance; removed unused template contexts
242
    https://docs.djangoproject.com/en/dev/ref/models/querysets/#in
504.2.4 by franku
Do not show hidden topics in feeds
243
    """
244
245
    def get_queryset(self, *args, **kwargs):
246
        qs = super(HiddenTopicsManager,
247
                   self).get_queryset().filter(hidden=True)
248
504.2.5 by franku
implement hide/unhide toggling of topics
249
        hidden_topics = []
508.1.6 by franku
fixed issues if the website get set up from scratch
250
        try:
251
            for post in qs:
252
                if post.topic.is_hidden:
253
                    hidden_topics.append(post.topic)
254
            return hidden_topics
255
        except:
256
            return []
504.2.4 by franku
Do not show hidden topics in feeds
257
528.1.5 by franku
refactored name of manager; fixed ordering
258
class PublicPostsManager(models.Manager):
528.1.1 by franku
Unify queriýing official posts; Performance optimizations
259
528.1.5 by franku
refactored name of manager; fixed ordering
260
    def public(self, limit=None, date_from=None):
261
        """Get public posts.
528.1.1 by franku
Unify queriýing official posts; Performance optimizations
262
528.1.6 by kaputtnik
wording, spelling
263
        Filters out all posts which shouldn't be visible to
264
        normal visitors. The result is always orderd by the
265
        posts creation time, Descending. Optional arguments:
528.1.3 by franku
cleanups
266
528.1.1 by franku
Unify queriýing official posts; Performance optimizations
267
        limit:     Slice the QuerySet [:limit].
268
        date_from: Gathers all posts from this day until today.
269
        """
270
271
        qs = self.get_queryset().filter(
272
            topic__forum__category__internal=False, hidden=False).exclude(
273
            topic__in=Post.hidden_topics.all()).order_by(
274
            '-created')
275
        
276
        if date_from:
277
            qs = qs.filter(created__gte=date_from)
278
        if limit:
279
            qs = qs[:limit]
280
281
        return qs
282
504.2.4 by franku
Do not show hidden topics in feeds
283
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
284
class Post(RenderableItem):
438.1.6 by franku
run the script
285
    topic = models.ForeignKey(
286
        Topic, related_name='posts', verbose_name=_('Topic'))
287
    user = models.ForeignKey(
288
        User, related_name='posts', verbose_name=_('User'))
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
289
    created = models.DateTimeField(_('Created'), blank=True)
290
    updated = models.DateTimeField(_('Updated'), blank=True, null=True)
438.1.6 by franku
run the script
291
    markup = models.CharField(_('Markup'), max_length=15,
292
                              default=pybb_settings.DEFAULT_MARKUP, choices=MARKUP_CHOICES)
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
293
    body = models.TextField(_('Message'))
294
    body_html = models.TextField(_('HTML version'))
295
    body_text = models.TextField(_('Text version'))
422.1.8 by GunChleoc
Proofreading.
296
    hidden = models.BooleanField(_('Hidden'), blank=True, default=False)
192 by Holger Rapp
Trying to fix 338756
297
528.1.6 by kaputtnik
wording, spelling
298
    objects = PublicPostsManager()  # Normal manager, extended
528.1.5 by franku
refactored name of manager; fixed ordering
299
    hidden_topics = HiddenTopicsManager()  # Custom manager
504.2.4 by franku
Do not show hidden topics in feeds
300
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
301
    class Meta:
302
        ordering = ['created']
303
        verbose_name = _('Post')
304
        verbose_name_plural = _('Posts')
305
306
    def summary(self):
307
        LIMIT = 50
192 by Holger Rapp
Trying to fix 338756
308
        tail = len(self.body) > LIMIT and '...' or ''
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
309
        return self.body[:LIMIT] + tail
310
311
    __unicode__ = summary
312
313
    def save(self, *args, **kwargs):
314
        if self.created is None:
315
            self.created = datetime.now()
209.1.21 by Timo Wingender
Add notifications to forum
316
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
317
        self.render()
318
319
        new = self.id is None
320
321
        if new:
322
            self.topic.updated = datetime.now()
323
            self.topic.save()
324
            self.topic.forum.updated = self.topic.updated
325
            self.topic.forum.save()
326
327
        super(Post, self).save(*args, **kwargs)
328
329
    def get_absolute_url(self):
330
        return reverse('pybb_post', args=[self.id])
331
427.1.8 by franku
moved unhide_post to pybb.models
332
    def unhide_post(self):
438.1.6 by franku
run the script
333
        """Unhide post(s) and inform subscribers."""
427.1.8 by franku
moved unhide_post to pybb.models
334
        self.hidden = False
335
        self.save()
336
        if self.topic.post_count == 1:
337
            # The topic is new
438.1.6 by franku
run the script
338
            send(User.objects.all(), 'forum_new_topic',
339
                 {'topic': self.topic, 'post': self, 'user': self.topic.user})
427.1.8 by franku
moved unhide_post to pybb.models
340
        else:
341
            # Inform topic subscribers
438.1.6 by franku
run the script
342
            send(self.topic.subscribers.all(), 'forum_new_post',
343
                 {'post': self, 'topic': self.topic, 'user': self.user})
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
344
345
    def delete(self, *args, **kwargs):
346
        self_id = self.id
347
        head_post_id = self.topic.posts.order_by('created')[0].id
348
        super(Post, self).delete(*args, **kwargs)
349
350
        self.topic.save()
351
        self.topic.forum.save()
352
353
        if self_id == head_post_id:
354
            self.topic.delete()
355
504.2.9 by franku
some refactoring
356
    def is_spam(self):
357
        try:
358
            SuspiciousInput.objects.get(object_id = self.pk)
359
            return True
360
        except:
361
            pass
362
        return False
363
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
364
365
class Read(models.Model):
438.1.6 by franku
run the script
366
    """For each topic that user has entered the time is logged to this
367
    model."""
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
368
369
    user = models.ForeignKey(User, verbose_name=_('User'))
370
    topic = models.ForeignKey(Topic, verbose_name=_('Topic'))
371
    time = models.DateTimeField(_('Time'), blank=True)
372
373
    class Meta:
374
        unique_together = ['user', 'topic']
375
        verbose_name = _('Read')
376
        verbose_name_plural = _('Reads')
377
378
    def save(self, *args, **kwargs):
379
        if self.time is None:
380
            self.time = datetime.now()
381
        super(Read, self).save(*args, **kwargs)
382
383
    def __unicode__(self):
384
        return u'T[%d], U[%d]: %s' % (self.topic.id, self.user.id, unicode(self.time))
385
386
387
class Attachment(models.Model):
438.1.6 by franku
run the script
388
    post = models.ForeignKey(Post, verbose_name=_(
389
        'Post'), related_name='attachments')
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
390
    size = models.IntegerField(_('Size'))
391
    content_type = models.CharField(_('Content type'), max_length=255)
392
    path = models.CharField(_('Path'), max_length=255)
393
    name = models.TextField(_('Name'))
438.1.6 by franku
run the script
394
    hash = models.CharField(_('Hash'), max_length=40,
395
                            blank=True, default='', db_index=True)
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
396
397
    def save(self, *args, **kwargs):
398
        super(Attachment, self).save(*args, **kwargs)
399
        if not self.hash:
438.1.6 by franku
run the script
400
            self.hash = hashlib.sha1(
401
                str(self.id) + settings.SECRET_KEY).hexdigest()
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
402
        super(Attachment, self).save(*args, **kwargs)
403
404
    def __unicode__(self):
405
        return self.name
406
407
    def get_absolute_url(self):
408
        return reverse('pybb_attachment', args=[self.hash])
409
410
    def size_display(self):
411
        size = self.size
412
        if size < 1024:
413
            return '%b' % size
414
        elif size < 1024 * 1024:
415
            return '%dKb' % int(size / 1024)
416
        else:
417
            return '%.2fMb' % (size / float(1024 * 1024))
418
419
    def get_absolute_path(self):
420
        return os.path.join(settings.MEDIA_ROOT, pybb_settings.ATTACHMENT_UPLOAD_TO,
421
                            self.path)
422
423
404.2.25 by franku
added old notification app to widelands; deactivated notification feed
424
if notification is not None:
425
    signals.post_save.connect(notification.handle_observations, sender=Post)
209.1.21 by Timo Wingender
Add notifications to forum
426
22 by Holger Rapp
- Added my hacked version of pybb. Remerging new versions is very difficult at this point :(
427
from pybb import signals
428
signals.setup_signals()