From 1b92f9284935dc085573b00e809f0ecc36e4b6ca Mon Sep 17 00:00:00 2001 From: Gustavo Andres Morero Date: Wed, 18 Jan 2017 15:08:39 -0300 Subject: [PATCH] adding Article.organization and changes to handle articles by organization. --- wiki/decorators.py | 14 ++++++++++-- wiki/migrations/0002_auto_20170118_0635.py | 21 ++++++++++++++++++ wiki/migrations/0003_article_organization.py | 22 +++++++++++++++++++ wiki/models/article.py | 4 ++++ wiki/models/urlpath.py | 21 ++++++++++++------ wiki/views/article.py | 23 +++++++++++++------- 6 files changed, 88 insertions(+), 17 deletions(-) create mode 100644 wiki/migrations/0002_auto_20170118_0635.py create mode 100644 wiki/migrations/0003_article_organization.py diff --git a/wiki/decorators.py b/wiki/decorators.py index c94e416b..0286b91a 100644 --- a/wiki/decorators.py +++ b/wiki/decorators.py @@ -84,13 +84,20 @@ def get_article(func=None, can_read=True, can_write=False, # noqa path = kwargs.pop('path', None) article_id = kwargs.pop('article_id', None) + org = request.organization urlpath = None # fetch by urlpath.path if path is not None: try: - urlpath = models.URLPath.get_by_path(path, select_related=True) + urlpath = models.URLPath.get_by_path( + path, org, select_related=True) + if urlpath.article.organization != org: + if urlpath.is_root_node(): + raise NoRootURL + else: + raise models.URLPath.DoesNotExist except NoRootURL: return redirect('wiki:root_create') except models.URLPath.DoesNotExist: @@ -101,7 +108,7 @@ def get_article(func=None, can_read=True, can_write=False, # noqa path.split("/"), )) path = "/".join(pathlist[:-1]) - parent = models.URLPath.get_by_path(path) + parent = models.URLPath.get_by_path(path, org) return HttpResponseRedirect( reverse( "wiki:create", kwargs={'path': parent.path, }) + @@ -153,6 +160,9 @@ def get_article(func=None, can_read=True, can_write=False, # noqa if article.current_revision and article.current_revision.deleted: return redirect('wiki:deleted', article_id=article.id) + if article.organization != org: + return response_forbidden(request, article, urlpath) + if article.current_revision.locked and not_locked: return response_forbidden(request, article, urlpath) diff --git a/wiki/migrations/0002_auto_20170118_0635.py b/wiki/migrations/0002_auto_20170118_0635.py new file mode 100644 index 00000000..b7d9cffb --- /dev/null +++ b/wiki/migrations/0002_auto_20170118_0635.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2017-01-18 14:35 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('wiki', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='urlpath', + name='article', + field=models.ForeignKey(help_text='This field is automatically updated, but you need to populate it when creating a new URL path.', on_delete=django.db.models.deletion.CASCADE, to='wiki.Article', verbose_name='article'), + ), + ] diff --git a/wiki/migrations/0003_article_organization.py b/wiki/migrations/0003_article_organization.py new file mode 100644 index 00000000..ff1716a4 --- /dev/null +++ b/wiki/migrations/0003_article_organization.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2017-01-18 14:38 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('grinch', '0016_team_organization'), + ('wiki', '0002_auto_20170118_0635'), + ] + + operations = [ + migrations.AddField( + model_name='article', + name='organization', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='articles', to='grinch.Organization'), + ), + ] diff --git a/wiki/models/article.py b/wiki/models/article.py index a8c52de2..d019383a 100644 --- a/wiki/models/article.py +++ b/wiki/models/article.py @@ -77,6 +77,10 @@ class Article(models.Model): default=True, verbose_name=_('others write access')) + organization = models.ForeignKey( + 'grinch.Organization', related_name='articles', + null=True, blank=True) + # PERMISSIONS def can_read(self, user): return permissions.can_read(self, user) diff --git a/wiki/models/urlpath.py b/wiki/models/urlpath.py index 3b78725c..d5e2f504 100644 --- a/wiki/models/urlpath.py +++ b/wiki/models/urlpath.py @@ -8,6 +8,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, transaction +from django.db.models import Q from django.db.models.signals import post_save, pre_delete # Django 1.6 transaction API, required for 1.8+ from django.utils.encoding import python_2_unicode_compatible @@ -167,11 +168,13 @@ class URLPath(MPTTModel): log.exception("Exception deleting article subtree.") @classmethod - def root(cls): + def root(cls, organization=None): site = Site.objects.get_current() - root_nodes = list( - cls.objects.root_nodes().filter(site=site).select_related_common() - ) + query = Q(site=site) + if organization: + query &= Q(article__organization=organization) + qs = cls.objects.root_nodes().filter(query).select_related_common() + root_nodes = list(qs) # We fetch the nodes as a list and use len(), not count() because we need # to get the result out anyway. This only takes one sql query no_paths = len(root_nodes) @@ -219,7 +222,7 @@ class URLPath(MPTTModel): super(URLPath, self).clean(*args, **kwargs) @classmethod - def get_by_path(cls, path, select_related=False): + def get_by_path(cls, path, organization, select_related=False): """ Strategy: Don't handle all kinds of weird cases. Be strict. Accepts paths both starting with and without '/' @@ -234,11 +237,11 @@ class URLPath(MPTTModel): # Root page requested if not path: - return cls.root() + return cls.root(organization) slugs = path.split('/') level = 1 - parent = cls.root() + parent = cls.root(organization) for slug in slugs: if settings.URL_CASE_SENSITIVE: child = parent.get_children().select_related_common().get( @@ -262,12 +265,16 @@ class URLPath(MPTTModel): if not site: site = Site.objects.get_current() root_nodes = cls.objects.root_nodes().filter(site=site) + if request: + root_nodes = root_nodes.filter( + article__organization=request.organization) if not root_nodes: # (get_or_create does not work for MPTT models??) article = Article() revision = ArticleRevision(title=title, **kwargs) if request: revision.set_from_request(request) + article.organization = request.organization article.add_revision(revision, save=True) article.save() root = cls.objects.create(site=site, article=article) diff --git a/wiki/views/article.py b/wiki/views/article.py index be4c84ec..c006c44b 100644 --- a/wiki/views/article.py +++ b/wiki/views/article.py @@ -82,9 +82,11 @@ class Create(FormView, ArticleMixin): def form_valid(self, form): user = None + organization = None ip_address = None if not self.request.user.is_anonymous(): user = self.request.user + organization = self.request.organization if settings.LOG_IPS_USERS: ip_address = self.request.META.get('REMOTE_ADDR', None) elif settings.LOG_IPS_ANONYMOUS: @@ -105,6 +107,7 @@ class Create(FormView, ArticleMixin): 'group_write': self.article.group_write, 'other_read': self.article.other_read, 'other_write': self.article.other_write, + 'organization': organization, }) messages.success( self.request, @@ -883,22 +886,26 @@ class CreateRootView(FormView): template_name = 'wiki/create_root.html' def dispatch(self, request, *args, **kwargs): - if not request.user.is_superuser: return redirect("wiki:root_missing") + org = request.organization try: - root = models.URLPath.root() + root = models.URLPath.root(org) + if request.method == "GET" and root.article.organization != org: + raise NoRootURL except NoRootURL: pass else: if root.article: - return redirect('wiki:get', path=root.path) - - # TODO: This is too dangerous... let's say there is no root.article and we end up here, - # then it might cascade to delete a lot of things on an existing - # installation.... / benjaoming - root.delete() + if root.article.organization == org: + return redirect('wiki:get', path=root.path) + else: + # TODO: This is too dangerous... + # let's say there is no root.article and we end up here, + # then it might cascade to delete a lot of things + # on an existing installation.... / benjaoming + root.delete() return super(CreateRootView, self).dispatch(request, *args, **kwargs) def form_valid(self, form): -- 2.45.2