Commits

Stefan Scherfke committed cb2392d Merge

Merge

  • Participants
  • Parent commits e351854, a3502c9

Comments (0)

Files changed (10)

File docs/conf.py

 # built documents.
 #
 # The short X.Y version.
-version = '1.3.0'
+version = '1.3'
 # The full version, including alpha/beta/rc tags.
 release = '1.3.0'
 

File docs/quickstart.txt

     sphinx builder and the sphinxdoc updater. If not set, defaults to
     *_build*.
 
+``SPHINXDOC_PROTECTED_PROJECTS``
+    A mapping of project slugs to lists of permissions indicating that users
+    are required to log in and have the list of permissions to view the
+    documented project. An empty list will just require a log in.
+
 
 Add a project
 -------------

File docs/ref/decorators.txt

+.. _ref-admin:
+
+Decorators
+==========
+
+.. automodule:: sphinxdoc.decorators
+
+    .. autofunction:: user_allowed_for_project(view)
+

File docs/ref/index.txt

     :maxdepth: 1
     
     admin
+    decorators
     forms
     management
     models

File docs/ref/views.txt

 
     .. autoclass:: ProjectSearchView
         :members:
+
+    .. autoclass:: OverviewList
+        :members:

File sphinxdoc/decorators.py

+from functools import wraps
+
+from django.contrib.auth.views import redirect_to_login
+from django.core.exceptions import ImproperlyConfigured
+from django.core.exceptions import PermissionDenied
+from django.shortcuts import get_object_or_404
+from django.utils.decorators import available_attrs
+
+from sphinxdoc.models import Project
+
+
+def user_allowed_for_project(view_func):
+    """
+    Check that the user is allowed for the project.
+
+    If the user is not allowed, the view will be redirected to the standard
+    login page.
+    """
+    @wraps(view_func, assigned=available_attrs(view_func))
+    def _wrapped_view(request, *args, **kwargs):
+        try:
+            slug = kwargs['slug']
+        except IndexError:
+            raise ImproperlyConfigured
+        project = get_object_or_404(Project, slug=slug)
+        if project.is_allowed(request.user):
+            return view_func(request, *args, **kwargs)
+        if request.user.is_authenticated():
+            raise PermissionDenied
+        path = request.build_absolute_uri()
+        return redirect_to_login(path)
+    return _wrapped_view
+
+

File sphinxdoc/management/commands/updatedoc.py

         if update_all:
             for project in Project.objects.all():
                 self.update_project(project, options)
+
+            self.update_haystack()
+
         elif args:
             for slug in args:
                 try:
                     raise CommandError('Project "%s" does not exist' % slug)
                 else:
                     self.update_project(project, options)
+
+            self.update_haystack()
+
         else:
             raise CommandError('No project(s) specified.')
 
+        print 'Done'
+
     def update_project(self, project, options):
         """
         Updates (and optionally builds) the documenation for a given project.
         print 'Importing JSON files for "%s" ...' % project.slug
         self.import_files(project)
 
-        print 'Updating search index for "%s" ...' % project.slug
-        self.update_haystack()
-
-        print 'Done'
-
     def build(self, project, virtualenv=''):
         """Runs ``sphinx-build`` for ``project``. You can also specify a path
         to the bin-directory of a ``virtualenv``, if your project requires it.
 
     def update_haystack(self):
         """Updates Haystack's search index."""
+        print 'Updating search index for all projects ...'
         call_command('rebuild_index', interactive=False)

File sphinxdoc/models.py

 Models for django-sphinxdoc.
 
 """
+from django.conf import settings
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
 
     def __unicode__(self):
         return self.name
 
+    def is_allowed(self, user):
+        protected = getattr(settings, 'SPHINXDOC_PROTECTED_PROJECTS', {})
+        if not self.slug in protected:
+            # Project not protected, publicly visible
+            return True
+        if (not user.is_authenticated() or
+            not user.has_perms(protected[self.slug])):
+            return False
+        return True
+
     @models.permalink
     def get_absolute_url(self):
         return ('doc-index', (), {'slug': self.slug})

File sphinxdoc/urls.py

+# encoding: utf-8
 """
 URL conf for django-sphinxdoc.
 
 except ImportError:
     from django.conf.urls import patterns, url
 
-from django.views.generic import ListView
-
-from sphinxdoc import models
 from sphinxdoc.views import ProjectSearchView
+from sphinxdoc.views import OverviewList
 
 
 urlpatterns = patterns('sphinxdoc.views',
     url(
         r'^$',
-        ListView.as_view(
-            queryset=models.Project.objects.all().order_by('name')),
+        OverviewList.as_view(),
         name='docs-list',
     ),
     url(

File sphinxdoc/views.py

 import os.path
 
 from django.conf import settings
+from django.contrib.auth.views import redirect_to_login
 from django.core import urlresolvers
 from django.core.exceptions import ObjectDoesNotExist
+from django.core.exceptions import PermissionDenied
 from django.shortcuts import get_object_or_404, render_to_response
 from django.template import RequestContext
 from django.views.decorators.cache import cache_page
+from django.views.generic import ListView
 from django.views.static import serve
 from haystack.views import SearchView
 
+from sphinxdoc.decorators import user_allowed_for_project
 from sphinxdoc.forms import ProjectSearchForm
 from sphinxdoc.models import Project, Document
 
 CACHE_MINUTES = getattr(settings, 'SPHINXDOC_CACHE_MINUTES', 5)
 
 
+@user_allowed_for_project
 @cache_page(60 * CACHE_MINUTES)
 def documentation(request, slug, path):
     """Displays the contents of a :class:`sphinxdoc.models.Document`.
                               context_instance=RequestContext(request))
 
 
+@user_allowed_for_project
 @cache_page(60 * CACHE_MINUTES)
 def objects_inventory(request, slug):
     """Renders the ``objects.inv`` as plain text."""
     return response
 
 
+@user_allowed_for_project
 @cache_page(60 * CACHE_MINUTES)
 def sphinx_serve(request, slug, type_, path):
     """Serves sphinx static and other files."""
 
     def __call__(self, request, slug):
         self.slug = slug
-        return SearchView.__call__(self, request)
+        try:
+            return SearchView.__call__(self, request)
+        except PermissionDenied:
+            if request.user.is_authenticated():
+                raise
+            path = request.build_absolute_uri()
+            return redirect_to_login(path)
 
     def build_form(self):
         """Instantiates the form that should be used to process the search
 
         """
         project = Project.objects.get(slug=self.slug)
+        if not project.is_allowed(self.request.user):
+            raise PermissionDenied
 
         try:
             env = json.load(open(os.path.join(project.path, BUILDDIR,
             'env': env,
             'update_date': update_date,
         }
+
+
+class OverviewList(ListView):
+    """Listing of all projects available.
+    
+    Extends :class:`django.views.generic.list.ListView`.
+
+    If the user is not authenticated, then projects defined in
+    :data:`SPHINXDOC_PROTECTED_PROJECTS` will not be listed.
+    """
+    queryset = Project.objects.all().order_by('name')
+    template_name = 'sphinxdoc/project_list.html'
+    context_object_name = 'project_list'
+
+    def get_queryset(self):
+        qs = super(OverviewList, self).get_queryset()
+        qs = [project for project in qs if project.is_allowed(self.request.user)]
+        # Note: we are not actually returning a queryset, but a list. This has
+        # some repercussions if there are dependencies elsewhere in the class
+        # on this actually being a queryset.
+        return qs
+