~widelands-dev/widelands-website/django_staticfiles

« back to all changes in this revision

Viewing changes to wiki/models.py

  • Committer: Holger Rapp
  • Date: 2009-02-19 15:31:42 UTC
  • Revision ID: sirver@h566336-20090219153142-dc8xuabldnw5t395
Initial commit of new widelands homepage

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
from datetime import datetime
2
 
from django.urls import reverse
3
 
 
4
 
# Google Diff Match Patch library
5
 
# http://code.google.com/p/google-diff-match-patch
6
 
from diff_match_patch import diff_match_patch
7
 
 
8
 
from django.db import models
9
 
from django.conf import settings
10
 
from django.contrib.auth.models import User
11
 
from django.utils.translation import ugettext_lazy as _
12
 
from django.contrib.contenttypes.models import ContentType
13
 
from django.contrib.contenttypes.fields import GenericRelation, GenericForeignKey
14
 
 
15
 
from tagging.fields import TagField
16
 
from tagging.models import Tag
17
 
from wlimages.models import Image
18
 
 
19
 
try:
20
 
    from notification import models as notification
21
 
    from django.db.models import signals
22
 
except ImportError:
23
 
    notification = None
24
 
 
25
 
# We dont need to create a new one everytime
26
 
dmp = diff_match_patch()
27
 
 
28
 
 
29
 
def diff(txt1, txt2):
30
 
    """Create a 'diff' from txt1 to txt2."""
31
 
    patch = dmp.patch_make(txt1, txt2)
32
 
    return dmp.patch_toText(patch)
33
 
 
34
 
try:
35
 
    markup_choices = settings.WIKI_MARKUP_CHOICES
36
 
except AttributeError:
37
 
    markup_choices = (
38
 
        ('crl', _(u'Creole')),
39
 
        ('rst', _(u'reStructuredText')),
40
 
        ('txl', _(u'Textile')),
41
 
        ('mrk', _(u'Markdown')),
42
 
    )
43
 
 
44
 
 
45
 
class Article(models.Model):
46
 
    """A wiki page reflecting the actual revision."""
47
 
    title = models.CharField(_(u"Title"), max_length=50, unique=True)
48
 
    content = models.TextField(_(u"Content"))
49
 
    summary = models.CharField(_(u"Summary"), max_length=150,
50
 
                               null=True, blank=True)
51
 
    markup = models.CharField(_(u"Content Markup"), max_length=3,
52
 
                              choices=markup_choices,
53
 
                              null=True, blank=True)
54
 
    creator = models.ForeignKey(User, verbose_name=_('Article Creator'),
55
 
                                null=True)
56
 
    created_at = models.DateTimeField(default=datetime.now)
57
 
    last_update = models.DateTimeField(blank=True, null=True)
58
 
 
59
 
    content_type = models.ForeignKey(ContentType, null=True)
60
 
    object_id = models.PositiveIntegerField(null=True)
61
 
    group = GenericForeignKey('content_type', 'object_id')
62
 
 
63
 
    images = GenericRelation(Image)
64
 
 
65
 
    tags = TagField()
66
 
 
67
 
    class Meta:
68
 
        verbose_name = _(u'Article')
69
 
        verbose_name_plural = _(u'Articles')
70
 
        app_label = 'wiki'
71
 
        default_permissions = ('change', 'add',)
72
 
        ordering = ['title']
73
 
 
74
 
    def get_absolute_url(self):
75
 
        if self.group is None:
76
 
            return reverse('wiki_article', args=(self.title,))
77
 
        return self.group.get_absolute_url() + 'wiki/' + self.title
78
 
 
79
 
    def save(self, *args, **kwargs):
80
 
        self.last_update = datetime.now()
81
 
        super(Article, self).save(*args, **kwargs)
82
 
 
83
 
    def latest_changeset(self):
84
 
        try:
85
 
            return self.changeset_set.filter(
86
 
                reverted=False).order_by('-revision')[0]
87
 
        except IndexError:
88
 
            return ChangeSet.objects.none()
89
 
 
90
 
    def all_images(self):
91
 
        return self.images.all()
92
 
 
93
 
    def new_revision(self, old_content, old_title, old_markup,
94
 
                     comment, editor):
95
 
        """Create a new ChangeSet with the old content."""
96
 
 
97
 
        content_diff = diff(self.content, old_content)
98
 
 
99
 
        cs = ChangeSet.objects.create(
100
 
            article=self,
101
 
            comment=comment,
102
 
            editor=editor,
103
 
            old_title=old_title,
104
 
            old_markup=old_markup,
105
 
            content_diff=content_diff)
106
 
 
107
 
        return cs
108
 
 
109
 
    def revert_to(self, revision, editor=None):
110
 
        """Revert the article to a previuos state, by revision number."""
111
 
        changeset = self.changeset_set.get(revision=revision)
112
 
        changeset.reapply(editor)
113
 
 
114
 
    def compare(self, from_revision, to_revision):
115
 
        """Compares to revisions of this article."""
116
 
        changeset = self.changeset_set.get(revision=to_revision)
117
 
        return changeset.compare_to(from_revision)
118
 
 
119
 
    def __unicode__(self):
120
 
        return self.title
121
 
 
122
 
 
123
 
class ChangeSetManager(models.Manager):
124
 
 
125
 
    def all_later(self, revision):
126
 
        """Return all changes later to the given revision.
127
 
 
128
 
        Util when we want to revert to the given revision.
129
 
 
130
 
        """
131
 
        return self.filter(revision__gt=int(revision))
132
 
 
133
 
 
134
 
class ChangeSet(models.Model):
135
 
    """A report of an older version of some Article."""
136
 
 
137
 
    article = models.ForeignKey(Article, verbose_name=_(u"Article"))
138
 
 
139
 
    # Editor identification -- logged
140
 
    editor = models.ForeignKey(User, verbose_name=_(u'Editor'),
141
 
                               null=True)
142
 
 
143
 
    # Revision number, starting from 1
144
 
    revision = models.IntegerField(_(u"Revision Number"))
145
 
 
146
 
    # How to recreate this version
147
 
    old_title = models.CharField(_(u"Old Title"), max_length=50, blank=True)
148
 
    old_markup = models.CharField(_(u"Article Content Markup"), max_length=3,
149
 
                                  choices=markup_choices,
150
 
                                  null=True, blank=True)
151
 
    content_diff = models.TextField(_(u"Content Patch"), blank=True)
152
 
 
153
 
    comment = models.TextField(_(u"Editor comment"), blank=True)
154
 
    modified = models.DateTimeField(_(u"Modified at"), default=datetime.now)
155
 
    reverted = models.BooleanField(_(u"Reverted Revision"), default=False)
156
 
 
157
 
    objects = ChangeSetManager()
158
 
 
159
 
    class Meta:
160
 
        verbose_name = _(u'Change set')
161
 
        verbose_name_plural = _(u'Change sets')
162
 
        get_latest_by = 'modified'
163
 
        ordering = ('-revision',)
164
 
        app_label = 'wiki'
165
 
 
166
 
    def __unicode__(self):
167
 
        return u'#%s' % self.revision
168
 
 
169
 
    def get_absolute_url(self):
170
 
        if self.article.group is None:
171
 
            return reverse('wiki_changeset', kwargs={
172
 
                'title': self.article.title,
173
 
                'revision': self.revision
174
 
            })
175
 
        return reverse('wiki_changeset', kwargs={
176
 
            'group_slug': self.article.group.slug,
177
 
            'title': self.article.title,
178
 
            'revision': self.revision,
179
 
        })
180
 
 
181
 
    def is_anonymous_change(self):
182
 
        return self.editor is None
183
 
 
184
 
    def reapply(self, editor):
185
 
        """Return the Article to this revision."""
186
 
 
187
 
        # XXX Would be better to exclude reverted revisions
188
 
        #     and revisions previous/next to reverted ones
189
 
        next_changes = self.article.changeset_set.filter(
190
 
            revision__gt=self.revision).order_by('-revision')
191
 
 
192
 
        article = self.article
193
 
 
194
 
        content = None
195
 
        for changeset in next_changes:
196
 
            if content is None:
197
 
                content = article.content
198
 
            patch = dmp.patch_fromText(changeset.content_diff)
199
 
            content = dmp.patch_apply(patch, content)[0]
200
 
 
201
 
            changeset.reverted = True
202
 
            changeset.save()
203
 
 
204
 
        old_content = article.content
205
 
        old_title = article.title
206
 
        old_markup = article.markup
207
 
 
208
 
        article.content = content
209
 
        article.title = changeset.old_title
210
 
        article.markup = changeset.old_markup
211
 
        article.save()
212
 
 
213
 
        article.new_revision(
214
 
            old_content=old_content, old_title=old_title,
215
 
            old_markup=old_markup,
216
 
            comment='Reverted to revision #%s' % self.revision,
217
 
            editor=editor
218
 
            )
219
 
 
220
 
        self.save()
221
 
 
222
 
        if None not in (notification, self.editor):
223
 
            notification.send([self.editor], 'wiki_revision_reverted',
224
 
                              {'revision': self, 'article': self.article})
225
 
 
226
 
    def save(self, *args, **kwargs):
227
 
        """Saves the article with a new revision."""
228
 
        if self.id is None:
229
 
            try:
230
 
                self.revision = ChangeSet.objects.filter(
231
 
                    article=self.article).latest().revision + 1
232
 
            except self.DoesNotExist:
233
 
                self.revision = 1
234
 
 
235
 
        super(ChangeSet, self).save(*args, **kwargs)
236
 
 
237
 
    def get_content(self):
238
 
        """Returns the content of this revision."""
239
 
        content = self.article.content
240
 
        newer_changesets = ChangeSet.objects.filter(
241
 
            article=self.article, revision__gt=self.revision).order_by('-revision')
242
 
        for changeset in newer_changesets:
243
 
            patches = dmp.patch_fromText(changeset.content_diff)
244
 
            content = dmp.patch_apply(patches, content)[0]
245
 
        return content
246
 
 
247
 
    def compare_to(self, revision_from):
248
 
        other_content = u""
249
 
        if revision_from > 0:
250
 
            other_content = ChangeSet.objects.filter(
251
 
                article=self.article, revision__lte=revision_from).order_by('-revision')[0].get_content()
252
 
        diffs = dmp.diff_main(other_content, self.get_content())
253
 
        dmp.diff_cleanupSemantic(diffs)
254
 
        return dmp.diff_prettyHtml(diffs)