Commit e56a78a7 by paul

python_2_unicode_compatible decorateur

parent 0d10395f
......@@ -69,7 +69,7 @@ class URLPathAdmin(MPTTModelAdmin):
list_filter = ('site', 'articles__article__current_revision__deleted',
'articles__article__created',
'articles__article__modified')
list_display = ('__unicode__', 'article', 'get_created')
list_display = ('__str__', 'article', 'get_created')
def get_created(self, instance):
return instance.article.created
......
......@@ -7,6 +7,7 @@ from django.contrib.auth.models import Group
from django.core.cache import cache
from django.db import models
from django.db.models.signals import post_save, pre_delete
from django.utils.encoding import python_2_unicode_compatible
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
......@@ -17,6 +18,7 @@ from wiki import managers
from mptt.models import MPTTModel
from django.core.urlresolvers import reverse
@python_2_unicode_compatible
class Article(models.Model):
objects = managers.ArticleManager()
......@@ -139,7 +141,7 @@ class Article(models.Model):
def get_for_object(cls, obj):
return ArticleForObject.objects.get(object_id=obj.id, content_type=ContentType.objects.get_for_model(obj)).article
def __unicode__(self):
def __str__(self):
if self.current_revision:
return self.current_revision.title
obj_name = _('Article without content (%(id)d)') % {'id': self.id}
......@@ -184,17 +186,17 @@ class Article(models.Model):
else:
return reverse('wiki:get', kwargs={'article_id': self.id})
class ArticleForObject(models.Model):
objects = managers.ArticleFkManager()
article = models.ForeignKey('Article', on_delete=models.CASCADE)
# Same as django.contrib.comments
content_type = models.ForeignKey(ContentType,
verbose_name=_('content type'),
related_name="content_type_set_for_%(class)s")
object_id = models.PositiveIntegerField(_('object ID'))
content_type = models.ForeignKey(ContentType,
verbose_name=_('content type'),
related_name="content_type_set_for_%(class)s")
object_id = models.PositiveIntegerField(_('object ID'))
content_object = generic.GenericForeignKey("content_type", "object_id")
is_mptt = models.BooleanField(default=False, editable=False)
......@@ -206,6 +208,7 @@ class ArticleForObject(models.Model):
# Do not allow several objects
unique_together = ('content_type', 'object_id')
class BaseRevisionMixin(models.Model):
"""This is an abstract model used as a mixin: Do not override any of the
core model methods but respect the inheritor's freedom to do so itself."""
......@@ -215,10 +218,10 @@ class BaseRevisionMixin(models.Model):
user_message = models.TextField(blank=True,)
automatic_log = models.TextField(blank=True, editable=False,)
ip_address = models.IPAddressField(_('IP address'), blank=True, null=True, editable=False)
user = models.ForeignKey(compat.USER_MODEL, verbose_name=_('user'),
blank=True, null=True,
on_delete=models.SET_NULL)
ip_address = models.IPAddressField(_('IP address'), blank=True, null=True, editable=False)
user = models.ForeignKey(compat.USER_MODEL, verbose_name=_('user'),
blank=True, null=True,
on_delete=models.SET_NULL)
modified = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
......@@ -234,7 +237,7 @@ class BaseRevisionMixin(models.Model):
verbose_name=_('deleted'),
default=False,
)
locked = models.BooleanField(
locked = models.BooleanField(
verbose_name=_('locked'),
default=False,
)
......@@ -249,7 +252,9 @@ class BaseRevisionMixin(models.Model):
class Meta:
abstract = True
@python_2_unicode_compatible
class ArticleRevision(BaseRevisionMixin, models.Model):
"""This is where main revision data is stored. To make it easier to
copy, do NEVER create m2m relationships."""
......@@ -273,7 +278,7 @@ class ArticleRevision(BaseRevisionMixin, models.Model):
# help_text=_('If set, the article will redirect to the contents of another article.'),
# related_name='redirect_set')
def __unicode__(self):
def __str__(self):
return "%s (%d)" % (self.title, self.revision_number)
def inherit_predecessor(self, article):
......@@ -328,10 +333,12 @@ def _clear_ancestor_cache(article):
for ancestor in article.ancestor_objects():
ancestor.article.clear_cache()
def on_article_save_clear_cache(instance, **kwargs):
on_article_delete_clear_cache(instance, **kwargs)
post_save.connect(on_article_save_clear_cache, Article)
def on_article_delete_clear_cache(instance, **kwargs):
_clear_ancestor_cache(instance)
instance.clear_cache()
......
......@@ -29,6 +29,7 @@ There are three kinds of plugin base models:
from .article import ArticleRevision, BaseRevisionMixin
from wiki.conf import settings
class ArticlePlugin(models.Model):
"""This is the mother of all plugins. Extending from it means a deletion
of an article will CASCADE to your plugin, and the database will be kept
......@@ -45,10 +46,13 @@ class ArticlePlugin(models.Model):
# Permission methods - you should override these, if they don't fit your logic.
def can_read(self, user):
return self.article.can_read(user)
def can_write(self, user):
return self.article.can_write(user)
def can_delete(self, user):
return self.article.can_delete(user)
def can_moderate(self, user):
return self.article.can_moderate(user)
......@@ -60,7 +64,8 @@ class ArticlePlugin(models.Model):
# Override this setting with app_label = '' in your extended model
# if it lives outside the wiki app.
app_label = settings.APP_LABEL
class ReusablePlugin(ArticlePlugin):
"""Extend from this model if you have a plugin that may be related to many
articles. Please note that the ArticlePlugin.article ForeignKey STAYS! This
......@@ -75,9 +80,9 @@ class ReusablePlugin(ArticlePlugin):
"""
# The article on which the plugin was originally created.
# Used to apply permissions.
ArticlePlugin.article.on_delete=models.SET_NULL
ArticlePlugin.article.verbose_name=_('original article')
ArticlePlugin.article.help_text=_('Permissions are inherited from this article')
ArticlePlugin.article.on_delete = models.SET_NULL
ArticlePlugin.article.verbose_name = _('original article')
ArticlePlugin.article.help_text = _('Permissions are inherited from this article')
ArticlePlugin.article.null = True
ArticlePlugin.article.blank = True
......@@ -87,10 +92,13 @@ class ReusablePlugin(ArticlePlugin):
# before handling permissions....
def can_read(self, user):
return self.article.can_read(user) if self.article else False
def can_write(self, user):
return self.article.can_write(user) if self.article else False
def can_delete(self, user):
return self.article.can_delete(user) if self.article else False
def can_moderate(self, user):
return self.article.can_moderate(user) if self.article else False
......@@ -109,8 +117,10 @@ class ReusablePlugin(ArticlePlugin):
# if it lives outside the wiki app.
app_label = settings.APP_LABEL
class SimplePluginCreateError(Exception): pass
class SimplePlugin(ArticlePlugin):
"""
Inherit from this model and make sure to specify an article when
......@@ -162,6 +172,7 @@ class SimplePlugin(ArticlePlugin):
# if it lives outside the wiki app.
app_label = settings.APP_LABEL
class RevisionPlugin(ArticlePlugin):
"""
If you want your plugin to maintain revisions, extend from this one,
......@@ -253,6 +264,7 @@ class RevisionPluginRevision(BaseRevisionMixin, models.Model):
# It's my art, when I disguise my body in the shape of a plane.
# (Shellac, 1993)
def update_simple_plugins(**kwargs):
"""Every time a new article revision is created, we update all active
plugins to match this article revision"""
......@@ -262,15 +274,18 @@ def update_simple_plugins(**kwargs):
# TODO: This was breaking things. SimplePlugin doesn't have a revision?
p_revisions.update(article_revision=instance)
def on_article_plugin_post_save(**kwargs):
articleplugin = kwargs['instance']
articleplugin.article.clear_cache()
def on_reusable_plugin_post_save(**kwargs):
reusableplugin = kwargs['instance']
for article in reusableplugin.articles.all():
article.clear_cache()
def on_revision_plugin_revision_post_save(**kwargs):
revision = kwargs['instance']
revision.plugin.article.clear_cache()
......
......@@ -14,6 +14,8 @@ from django.db import models, transaction
from six.moves import filter
#Django 1.6 transaction API, required for 1.8+
from django.utils.encoding import python_2_unicode_compatible
try:
notrans=transaction.non_atomic_requests
except:
......@@ -33,6 +35,8 @@ from wiki.models.article import ArticleRevision, ArticleForObject, Article
log = logging.getLogger(__name__)
@python_2_unicode_compatible
class URLPath(MPTTModel):
"""
Strategy: Very few fields go here, as most has to be managed through an
......@@ -155,7 +159,7 @@ class URLPath(MPTTModel):
class MPTTMeta:
pass
def __unicode__(self):
def __str__(self):
path = self.path
return path if path else ugettext("(root)")
......@@ -260,6 +264,7 @@ class URLPath(MPTTModel):
# Just get this once
urlpath_content_type = None
def on_article_relation_save(**kwargs):
global urlpath_content_type
instance = kwargs['instance']
......@@ -270,12 +275,14 @@ def on_article_relation_save(**kwargs):
post_save.connect(on_article_relation_save, ArticleForObject)
class Namespace:
# An instance of Namespace simulates "nonlocal variable_name" declaration
# in any nested function, that is possible in Python 3. It allows assigning
# to non local variable without rebinding it local. See PEP 3104.
pass
def on_article_delete(instance, *args, **kwargs):
# If an article is deleted, then throw out its URLPaths
# But move all descendants to a lost-and-found node.
......@@ -286,18 +293,19 @@ def on_article_delete(instance, *args, **kwargs):
# that the lost-and-found article can be deleted without being recreated!
ns = Namespace() # nonlocal namespace backported to Python 2.x
ns.lost_and_found = None
def get_lost_and_found():
if ns.lost_and_found:
return ns.lost_and_found
try:
ns.lost_and_found = URLPath.objects.get(slug=settings.LOST_AND_FOUND_SLUG,
parent=URLPath.root(),
site=site)
parent=URLPath.root(),
site=site)
except URLPath.DoesNotExist:
article = Article(group_read = True,
group_write = False,
other_read = False,
other_write = False)
article = Article(group_read=True,
group_write=False,
other_read=False,
other_write=False)
article.add_revision(ArticleRevision(
content=_('Articles who lost their parents\n'
'===============================\n\n'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment