Commit ab7dc9a0 by benjaoming

Adding migrations for URLPath.article cache field

Adding dependency on sorl-thumbnail
parent 3e7ddf6a
......@@ -115,6 +115,7 @@ So far the dependencies are:
* [Markdown>=2.2.0](https://github.com/waylan/Python-Markdown)
* [django-mptt>=0.5](https://github.com/django-mptt/django-mptt)
* [django-sekizai](https://github.com/ojii/django-sekizai/)
* [sorl-thumbnail](https://github.com/sorl/sorl-thumbnail)
Development
------------
......
......@@ -3,3 +3,4 @@ South<0.8
Markdown<2.3.0
django-sekizai<0.7
django-mptt>=0.5
sorl-thumbnail
\ No newline at end of file
......@@ -6,6 +6,17 @@ that you have checked out the root of the Git repository.
It comes with a prepopulated SQLite database.
Setup
-----
You should link your wiki and django_notify folders like so
ln -s ../wiki .
ln -s ../django_notify .
Login
-----
Django admin:
Username: admin
......
......@@ -104,6 +104,7 @@ INSTALLED_APPS = (
'south',
'sekizai',
'django_notify',
'sorl.thumbnail',
'wiki',
'wiki.plugins.images',
'wiki.plugins.attachments',
......
......@@ -3,10 +3,12 @@ from django.conf import settings as django_settings
from django.core.urlresolvers import reverse
from django.shortcuts import redirect, get_object_or_404, render_to_response
from django.template.context import RequestContext
from django.http import HttpResponse, HttpResponseNotFound
from django.http import HttpResponse, HttpResponseNotFound,\
HttpResponseForbidden
from django.utils import simplejson as json
from wiki.core.exceptions import NoRootURL
from django.template.loader import render_to_string
def json_view(func):
def wrap(request, *args, **kwargs):
......@@ -79,7 +81,7 @@ def get_article(func=None, can_read=True, can_write=False, deleted_contents=Fals
# fetch by article.id
if article_id:
elif article_id:
article = get_object_or_404(articles, id=article_id)
try:
urlpath = models.URLPath.objects.get(articles__article=article)
......@@ -96,14 +98,14 @@ def get_article(func=None, can_read=True, can_write=False, deleted_contents=Fals
return redirect(django_settings.LOGIN_URL)
else:
c = RequestContext(request, {'urlpath' : urlpath})
return render_to_response("wiki/permission_denied.html", context_instance=c)
return HttpResponseForbidden(render_to_string("wiki/permission_denied.html", c))
if can_write and not article.can_write(request.user):
if request.user.is_anonymous():
return redirect(django_settings.LOGIN_URL)
else:
c = RequestContext(request, {'urlpath' : urlpath})
return render_to_response("wiki/permission_denied.html", context_instance=c)
return HttpResponseForbidden(render_to_string("wiki/permission_denied.html", c))
# If the article has been deleted, show a special page.
if not deleted_contents and article.current_revision and article.current_revision.deleted:
......
......@@ -4,7 +4,7 @@ from django.contrib.sites.models import Site
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models.signals import pre_delete
from django.db.models.signals import pre_delete, post_save
from django.utils.translation import ugettext_lazy as _, ugettext
from mptt.fields import TreeForeignKey
......@@ -13,6 +13,7 @@ from mptt.models import MPTTModel
from wiki.conf import settings
from wiki.core.exceptions import NoRootURL, MultipleRootURLs
from wiki.models.article import ArticleRevision, ArticleForObject, Article
from django.contrib.contenttypes.models import ContentType
class URLPath(MPTTModel):
"""
......@@ -25,6 +26,11 @@ class URLPath(MPTTModel):
INHERIT_PERMISSIONS = True
articles = generic.GenericRelation(ArticleForObject)
# Do NOT modify this field - it is updated with signals whenever ArticleForObject is changed.
article = models.ForeignKey(Article, on_delete=models.CASCADE, editable=False,
verbose_name=_(u'Cache lookup value for articles'))
slug = models.SlugField(verbose_name=_(u'slug'), null=True, blank=True)
site = models.ForeignKey(Site)
parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
......@@ -85,6 +91,11 @@ class URLPath(MPTTModel):
Strategy: Don't handle all kinds of weird cases. Be strict.
Accepts paths both starting with and without '/'
"""
# TODO: Save paths directly in the model for constant time lookups?
# Or: Save the parents in a lazy property because the parents are
# always fetched anyways so it's fine to fetch them here.
path = path.lstrip("/")
path = path.rstrip("/")
......@@ -127,25 +138,28 @@ class URLPath(MPTTModel):
"""Utility function:
Create a new urlpath with an article and a new revision for the article"""
if not site: site = Site.objects.get_current()
newpath = cls.objects.create(site=site, parent=parent, slug=slug)
article = Article(**article_kwargs)
article.add_revision(ArticleRevision(title=title, **kwargs),
save=True)
article.save()
newpath = cls.objects.create(site=site, parent=parent, slug=slug, article=article)
article.add_object_relation(newpath)
return newpath
@property
def article(self):
try:
return self.articles.all()[0].article
except IndexError:
return None
######################################################
# SIGNAL HANDLERS
######################################################
# Just get this once
urlpath_content_type = ContentType.objects.get_for_model(URLPath)
def on_article_relation_save(instance, *args, **kwargs):
if instance.content_type == urlpath_content_type:
URLPath.objects.filter(id=instance.object_id).update(article=instance.article)
post_save.connect(on_article_relation_save, ArticleForObject)
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.
......@@ -168,12 +182,13 @@ def on_article_delete(instance, *args, **kwargs):
content=_(u'Articles who lost their parents'
'==============================='),
title=_(u"Lost and found")))
for urlpath in URLPath.objects.filter(articles__article=instance, site=site):
# Delete the children
for child in urlpath.get_children():
child.move_to(lost_and_found)
# ...and finally delete the path itself
# TODO: This should be unnecessary because of URLPath.article(...ondelete=models.CASCADE)
urlpath.delete()
pre_delete.connect(on_article_delete, Article)
......@@ -61,7 +61,6 @@ class Migration(SchemaMigration):
},
'images.image': {
'Meta': {'object_name': 'Image', '_ormbases': ['wiki.RevisionPlugin']},
'caption': ('django.db.models.fields.CharField', [], {'max_length': '2056', 'null': 'True', 'blank': 'True'}),
'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
'revisionplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['wiki.RevisionPlugin']", 'unique': 'True', 'primary_key': 'True'})
},
......
from django.conf import settings as django_settings
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.utils.translation import ugettext_lazy as _
......@@ -5,9 +7,13 @@ import settings
from wiki.models.pluginbase import RevisionPlugin
if not "sorl.thumbnail" in django_settings.INSTALLED_APPS:
raise ImproperlyConfigured('wiki.plugins.images: needs sorl.thumbnail in INSTALLED_APPS')
class Image(RevisionPlugin):
image = models.ImageField(upload_to=settings.IMAGE_PATH)
image = models.ImageField(upload_to=settings.IMAGE_PATH,
max_length=2000)
def get_filename(self):
if self.image:
......
......@@ -17,6 +17,7 @@
{% endfor %}
</table>
{% if article|can_write:user %}
<hr />
<h4>{% trans "Add new image" %}</h4>
......@@ -55,3 +56,5 @@
{% trans "Add image" %}
</button>
</p>
{% endif %}
from wiki.views.mixins import ArticleMixin
from django.views.generic.base import TemplateView
from django.utils.decorators import method_decorator
from wiki.decorators import get_article
from wiki.views.mixins import ArticleMixin
class ImageView(ArticleMixin, TemplateView):
......
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