Commits

Kai Diefenbach committed baf371b

Initial import

Comments (0)

Files changed (19)

+syntax: glob
+.installed.cfg
+*.pyc
+*.pyo
+*.DS_Store 
+*develop-eggs/
+*.egg-info/
+*eggs/
+*parts/
+*bin/
+*downloads/
+*docs/_build
+include README.txt
+recursive-include permissions/locale *
+What is it?
+===========
+
+LFC is a Content Manangement System based on Django
+
+Features:
+=========
+
+- Variable templates to display the content
+- Variable portlets
+- Multi languages (without pain)
+- Bulk upload of images + automatic scaling
+- WYSIWYG-Editor
+- Tagging
+- Commenting
+- RSS Feeds
+- Pluggable (Add own content types and portlets see http://pypi.python.org/pypi/lfc-blog/
+
+Installation
+=============
+
+0. Install mercurial
+
+   $ easy_install mercurial"
+
+1. Get the buildout
+
+   hg clone http://bitbucket.org/diefenbach/lfc-buildout-development/
+
+2. Execute the buildout
+
+   a. $ cd lfc-buildout-development
+   b. $ python boostrap
+   c. $ bin/buildout -v
+
+3. Start the server
+
+   $ bin/django runserver
+
+4. Login
+
+   http://localhost:8000/login/ (admin/admin)
+
+5. Go to the management interface
+
+   http://localhost:8000/manage/
+
+Changes
+=======
+
+1.0 alpha 2 (2010-01-15)
+------------------------
+
+* A lot of cleanups and bugfixes
+* Added middleware to traverse objects
+* Added custom object manager to handle permissions (very simple yet)
+* Added simple form to manage registered content types
+* Added global images
+* Added some docs
+* Improved multi languages handling
+* Improved object actions
+* Improved search
+* Updated german translations
+
+1.0 alpha 1 (2010-01-10)
+------------------------
+
+* Initial public release
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html      to make standalone HTML files"
+	@echo "  dirhtml   to make HTML files named index.html in directories"
+	@echo "  pickle    to make pickle files"
+	@echo "  json      to make JSON files"
+	@echo "  htmlhelp  to make HTML files and a HTML help project"
+	@echo "  qthelp    to make HTML files and a qthelp project"
+	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes   to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck to check all external links for integrity"
+	@echo "  doctest   to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-permissions.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-permissions.qhc"
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+	      "run these through (pdf)latex."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
+===
+API
+===
+
+.. warning::
+
+    django-permissions is in alpha state. Please consider the API as supposed 
+    to be changed until it reaches beta state.
+
+.. automodule:: permissions.utils
+
+Utils
+=====
+
+Manage permissions
+------------------
+
+  .. autofunction:: grant_permission
+  .. autofunction:: remove_permission
+  .. autofunction:: has_permission
+
+
+Manage inheritance
+------------------
+
+  .. autofunction:: add_inheritance_block
+  .. autofunction:: remove_inheritance_block
+  .. autofunction:: is_inherited
+
+Registration
+------------
+
+Register permissions
+^^^^^^^^^^^^^^^^^^^^
+
+  .. autofunction:: register_permission
+  .. autofunction:: unregister_permission
+
+Register groups
+^^^^^^^^^^^^^^^
+
+  .. autofunction:: register_group
+  .. autofunction:: unregister_group
+
+Models
+======
+
+This models can be found in ``permissions.models``.
+
+.. autoclass:: permissions.models.Permission
+.. autoclass:: permissions.models.ObjectPermission
+.. autoclass:: permissions.models.ObjectPermissionInheritanceBlock
+# -*- coding: utf-8 -*-
+#
+# django-permissions documentation build configuration file, created by
+# sphinx-quickstart on Thu Mar 11 07:25:51 2010.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.append(os.path.abspath('.'))
+
+sys.path.append(os.path.abspath("../../../lfc_project"))
+sys.path.append(os.path.abspath("../../../parts/lfc"))
+sys.path.append(os.path.abspath("../../../parts/django"))
+sys.path.append(os.path.abspath("../../../parts/tagging"))
+sys.path.append(os.path.abspath("../../../parts/portlets"))
+sys.path.append(os.path.abspath("../../../parts/permissions"))
+
+# -- General configuration -----------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'django-permissions'
+copyright = u'2010, Kai Diefenbach'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '1.0a1'
+# The full version, including alpha/beta/rc tags.
+release = '1.0a1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'django-permissionsdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'django-permissions.tex', u'django-permissions Documentation',
+   u'Kai Diefenbach', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
+django-permissions
+==================
+
+Contents:
+
+.. toctree::
+   :maxdepth: 1
+   
+   overview.rst
+   api.rst
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+

docs/overview.rst

+========
+Overview
+========
+
+.. warning::
+
+    django-permissions is in alpha state. Please consider the API as supposed 
+    to be changed until it reaches beta state.
+
+What is it?
+===========
+
+django-permissions provides generic per-object permissions for Django.
+
+How does it work?
+=================
+
+Create a new permission
+-----------------------
+
+.. code-block:: python
+
+    from permissions.utils import register_permission
+    permission = register_permission("View", "view")
+
+Create a new group
+------------------
+
+.. code-block:: python
+
+    from permissions.utils import register_group
+    brights = register_group("Brights")
+    
+This will create a default Django group.
+
+Create a FlatPage
+-----------------
+
+.. code-block:: python
+
+    from django.contrib.flatpages.models import FlatPage
+    content = FlatPage.objects.create(title="Example", url="example")
+
+Grant permission
+----------------
+
+.. code-block:: python
+
+    from permission.utils import grant_permission
+    grant_permission("view", brights, content)
+
+Now all users which are member of the group "Brights" have the permission to
+view object "content". You can also grant permission to single users.
+
+Check permission
+----------------
+
+.. code-block:: python
+
+    from permission.utils import has_permission
+    result = has_permission("view", request.user, content)
+
+    if result == False:
+        print "Alert!"
+
+This will check whether the current user has the permission "View" for the 
+FlatPage "content"

permissions/README.txt

+What is it?
+===========
+
+django-permissions provides generic per-object permissions for Django.
+
+Information
+===========
+
Add a comment to this file

permissions/__init__.py

Empty file added.

permissions/admin.py

+from django.contrib import admin
+
+from permissions.models import ObjectPermission
+admin.site.register(ObjectPermission)
+
+from permissions.models import Permission
+admin.site.register(Permission)

permissions/backend.py

+# permissions import
+import permissions.utils
+
+class ObjectPermissionsBackend(object):
+    """Django backend for object permissions. Needs Django 1.2.
+
+
+    Use it together with the default ModelBackend like so::
+
+        AUTHENTICATION_BACKENDS = (
+            'django.contrib.auth.backends.ModelBackend',
+            'permissions.backend.ObjectPermissionsBackend',
+        )
+    """
+    supports_object_permissions = True
+    supports_anonymous_user = True
+
+    def authenticate(self, username, password):
+        return None
+
+    def has_permission(self, permission_codename, user, obj=None):
+        """Checks whether the passed user has passed permission for passed
+        object (obj).
+
+        This should be the primary method to check wether a user has a certain
+        permission.
+
+        Parameters
+        ==========
+
+        permission
+            The permission's codename which should be checked.
+
+        user
+            The user for which the permission should be checked.
+
+        obj
+            The object for which the permission should be checked.
+        """
+        return permissions.utils.has_permission(permission_codename, user, obj)

permissions/fixtures/initial.xml

+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+    <object pk="1" model="permissions.permission">
+        <field type="CharField" name="name">View</field>
+        <field type="CharField" name="codename">view</field>
+    </object>
+    <object pk="2" model="permissions.permission">
+        <field type="CharField" name="name">Edit</field>
+        <field type="CharField" name="codename">edit</field>
+    </object>
+    <object pk="3" model="permissions.permission">
+        <field type="CharField" name="name">Delete</field>
+        <field type="CharField" name="codename">delete</field>
+    </object>
+    <object pk="4" model="permissions.permission">
+        <field type="CharField" name="name">Cut</field>
+        <field type="CharField" name="codename">cut</field>
+    </object>
+    <object pk="5" model="permissions.permission">
+        <field type="CharField" name="name">Copy</field>
+        <field type="CharField" name="codename">copy</field>
+    </object>
+</django-objects>

permissions/models.py

+# django imports
+from django.db import models
+from django.contrib.auth.models import User
+from django.contrib.auth.models import Group
+from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.models import ContentType
+from django.utils.translation import ugettext_lazy as _
+
+class Permission(models.Model):
+    """A permission which can be granted to users/groups and objects.
+
+    **Attributes:**
+
+        name
+            The unique name of the permission. This is displayed to users.
+        codename
+            The unique codename of the permission. This is used internal to
+            identify a permission.
+    """
+    name = models.CharField(_(u"Name"), max_length=100, unique=True)
+    codename = models.CharField(_(u"Codename"), max_length=100, unique=True)
+
+    def __unicode__(self):
+        return "%s (%s)" % (self.name, self.codename)
+
+class ObjectPermission(models.Model):
+    """Grants permission for specific user/group and object.
+
+    **Attributes:**
+
+        group
+            The group for which the permission is granted. Either this xor the
+            user must be given.
+        user
+            The user for which the permission is granted. Either this xor the
+            user must be given.
+        permission
+            The permission which is granted.
+        content
+            The object for which the permission is granted.
+    """
+    group = models.ForeignKey(Group, verbose_name=_(u"Group"), blank=True, null=True)
+    user = models.ForeignKey(User, verbose_name=_(u"User"), blank=True, null=True)
+    permission = models.ForeignKey(Permission, verbose_name=_(u"Permission"))
+
+    content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"))
+    content_id = models.PositiveIntegerField(verbose_name=_(u"Content id"))
+    content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id")
+
+    def __unicode__(self):
+        if self.group:
+            principal = self.group
+        else:
+            principal = self.user
+
+        return "%s / %s / %s - %s" % (self.permission.name, principal, self.content_type, self.content_id)
+
+class ObjectPermissionInheritanceBlock(models.Model):
+    """Blocks the inheritance for specific permission and object.
+
+    **Attributes:**
+
+        permission
+            The permission for which inheritance is blocked.
+        content
+            The object for which the inheritance is blocked.
+    """
+    permission = models.ForeignKey(Permission, verbose_name=_(u"Permission"))
+
+    content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"))
+    content_id = models.PositiveIntegerField(verbose_name=_(u"Content id"))
+    content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id")
+
+    def __unicode__(self):
+        return "%s / %s - %s" % (self.permission, self.content_type, self.content_id)
Add a comment to this file

permissions/templatetags/__init__.py

Empty file added.

permissions/templatetags/permissions_tags.py

+# django imports
+from django import template
+from django.core.exceptions import ImproperlyConfigured
+from django.contrib.auth.models import User, AnonymousUser
+
+import permissions.utils
+register = template.Library()
+
+class PermissionComparisonNode(template.Node):
+    """Implements a node to provide an if current user has passed permission 
+    for current object.
+    """
+    @classmethod
+    def handle_token(cls, parser, token):
+        bits = token.contents.split()
+        if len(bits) != 2:
+            raise template.TemplateSyntaxError(
+                "'%s' tag takes one argument" % bits[0])
+        end_tag = 'endifhasperm'
+        nodelist_true = parser.parse(('else', end_tag))
+        token = parser.next_token()
+        if token.contents == 'else': # there is an 'else' clause in the tag
+            nodelist_false = parser.parse((end_tag,))
+            parser.delete_first_token()
+        else:
+            nodelist_false = ""
+
+        return cls(bits[1], nodelist_true, nodelist_false)
+
+    def __init__(self, permission, nodelist_true, nodelist_false):
+        self.permission = permission
+        self.nodelist_true = nodelist_true
+        self.nodelist_false = nodelist_false
+
+    def render(self, context):
+        obj = context.get("obj")
+        request = context.get("request")
+        if permissions.utils.has_permission(self.permission, request.user, obj):
+            return self.nodelist_true.render(context)
+        else:
+            return self.nodelist_false
+
+@register.tag
+def ifhasperm(parser, token):
+    """
+    This function provides functionality for the 'ifhasperm' template tag
+
+    Syntax::
+
+        {% ifhasperm PERMISSION_LABEL.CHECK_NAME USER *OBJS %}
+            lalala
+        {% else %}
+            meh
+        {% endifhasperm %}
+
+        {% if hasperm "poll_permission.change_poll" request.user %}
+            lalala
+        {% else %}
+            meh
+        {% endifhasperm %}
+
+    """
+    return PermissionComparisonNode.handle_token(parser, token)
+

permissions/tests.py

+# django imports
+from django.contrib.flatpages.models import FlatPage
+from django.contrib.auth.models import Group
+from django.contrib.auth.models import User
+from django.core.urlresolvers import reverse
+from django.test import TestCase
+from django.test.client import Client
+
+# permissions imports
+from permissions.models import Permission
+import permissions.utils
+
+class PermissionTestCase(TestCase):
+    """
+    """
+    def setUp(self):
+        """
+        """
+        self.group_1 = permissions.utils.register_group("Group 1")
+        self.group_2 = permissions.utils.register_group("Group 2")
+
+        self.user = User.objects.create(username="doe")
+        self.user.groups.add(self.group_1)
+        self.user.groups.add(self.group_2)
+        self.user.save()
+
+        self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1")
+        self.page_2 = FlatPage.objects.create(url="/page-1/", title="Page 2")
+
+        self.permission = permissions.utils.register_permission("View", "view")
+
+    def test_has_permission(self):
+        """
+        """
+        result = permissions.utils.has_permission("view", self.user, self.page_1)
+        self.assertEqual(result, False)
+
+        result = permissions.utils.grant_permission(self.permission, self.group_1, self.page_1)
+        self.assertEqual(result, True)
+
+        result = permissions.utils.has_permission("view", self.user, self.page_1)
+        self.assertEqual(result, True)
+
+        result = permissions.utils.remove_permission("view", self.user, self.page_1)
+        self.assertEqual(result, True)
+
+        result = permissions.utils.has_permission("view", self.user, self.page_1)
+        self.assertEqual(result, False)
+        
+    def test_ineritance(self):
+        """
+        """
+        result = permissions.utils.is_inherited("view", self.page_1)
+        self.assertEqual(result, True)
+
+        permissions.utils.add_inheritance_block(self.permission, self.page_1)
+
+        result = permissions.utils.is_inherited("view", self.page_1)
+        self.assertEqual(result, False)
+
+class RegistrationTestCase(TestCase):
+    """Tests the registration of different components.
+    """
+    def test_group(self):
+        """Tests registering/unregistering of a group.
+        """
+        # Register a group
+        result = permissions.utils.register_group("Brights")
+        self.failUnless(isinstance(result, Group))
+
+        # It's there
+        group = Group.objects.get(name="Brights")
+        self.assertEqual(group.name, "Brights")
+
+        # Trying to register another group with same name
+        result = permissions.utils.register_group("Brights")
+        self.assertEqual(result, False)
+
+        group = Group.objects.get(name="Brights")
+        self.assertEqual(group.name, "Brights")
+
+        # Unregister the group
+        result = permissions.utils.unregister_group("Brights")
+        self.assertEqual(result, True)
+
+        # It's not there anymore
+        self.assertRaises(Group.DoesNotExist, Group.objects.get, name="Brights")
+
+        # Trying to unregister the group again
+        result = permissions.utils.unregister_group("Brights")
+        self.assertEqual(result, False)
+
+    def test_permission(self):
+        """Tests registering/unregistering of a permission.
+        """
+        # Register a permission
+        result = permissions.utils.register_permission("Change", "change")
+        self.failUnless(isinstance(result, Permission))
+
+        # Is it there?
+        p = Permission.objects.get(codename="change")
+        self.assertEqual(p.name, "Change")
+
+        # Register a permission with the same codename
+        result = permissions.utils.register_permission("Change2", "change")
+        self.assertEqual(result, False)
+
+        # Is it there?
+        p = Permission.objects.get(codename="change")
+        self.assertEqual(p.name, "Change")
+
+        # Register a permission with the same name
+        result = permissions.utils.register_permission("Change", "change2")
+        self.assertEqual(result, False)
+
+        # Is it there?
+        p = Permission.objects.get(codename="change")
+        self.assertEqual(p.name, "Change")
+
+        # Unregister the permission
+        result = permissions.utils.unregister_permission("change")
+        self.assertEqual(result, True)
+
+        # Is it not there anymore?
+        self.assertRaises(Permission.DoesNotExist, Permission.objects.get, codename="change")
+
+        # Unregister the permission again
+        result = permissions.utils.unregister_permission("change")
+        self.assertEqual(result, False)
+
+
+# django imports
+from django.core.handlers.wsgi import WSGIRequest
+from django.contrib.auth.models import User
+from django.contrib.sessions.backends.file import SessionStore
+from django.test.client import Client
+
+# Taken from "http://www.djangosnippets.org/snippets/963/"
+class RequestFactory(Client):
+    """
+    Class that lets you create mock Request objects for use in testing.
+
+    Usage:
+
+    rf = RequestFactory()
+    get_request = rf.get('/hello/')
+    post_request = rf.post('/submit/', {'foo': 'bar'})
+
+    This class re-uses the django.test.client.Client interface, docs here:
+    http://www.djangoproject.com/documentation/testing/#the-test-client
+
+    Once you have a request object you can pass it to any view function,
+    just as if that view had been hooked up using a URLconf.
+
+    """
+    def request(self, **request):
+        """
+        Similar to parent class, but returns the request object as soon as it
+        has created it.
+        """
+        environ = {
+            'HTTP_COOKIE': self.cookies,
+            'PATH_INFO': '/',
+            'QUERY_STRING': '',
+            'REQUEST_METHOD': 'GET',
+            'SCRIPT_NAME': '',
+            'SERVER_NAME': 'testserver',
+            'SERVER_PORT': 80,
+            'SERVER_PROTOCOL': 'HTTP/1.1',
+        }
+        environ.update(self.defaults)
+        environ.update(request)
+        return WSGIRequest(environ)
+
+def create_request():
+    """
+    """
+    rf = RequestFactory()
+    request = rf.get('/')
+    request.session = SessionStore()
+
+    user = User()
+    user.is_superuser = True
+    user.save()
+    request.user = user
+
+    return request

permissions/utils.py

+# django imports
+from django.db import IntegrityError
+from django.contrib.auth.models import User
+from django.contrib.auth.models import Group
+from django.contrib.contenttypes.models import ContentType
+from django.core.exceptions import ObjectDoesNotExist
+
+# permissions imports
+from permissions.models import ObjectPermission
+from permissions.models import ObjectPermissionInheritanceBlock
+from permissions.models import Permission
+
+# Permission #################################################################
+
+def grant_permission(permission, group, obj):
+    """Adds passed permission to passed group and object. Returns True if the
+    permission was able to be added, otherwise False.
+
+    **Parameters:**
+
+        permission
+            The permission for which should be removed. Either a permission 
+            object or the codename of a permission.
+        obj
+            The content object for which an inheritance should be added.
+    """
+    if not isinstance(permission, Permission):
+        try:
+            permission = Permission.objects.get(codename = permission)
+        except Permission.DoesNotExist:
+            return False
+    
+    ct = ContentType.objects.get_for_model(obj)
+    try:
+        ObjectPermission.objects.get(group=group, content_type = ct, content_id=obj.id, permission=permission)
+    except ObjectPermission.DoesNotExist:
+        try:
+            result = ObjectPermission.objects.create(group=group, content=obj, permission=permission)
+        except IntegrityError:
+            return False
+    return True
+
+def remove_permission(permission, group, obj):
+    """Removes passed permission from passed group and object. Returns True if
+    the permission has been removed.
+
+    **Parameters:**
+
+        permission
+            The permission for which should be removed. Either a permission 
+            object or the codename of a permission.
+        group
+            The group for which a permission should be removed.
+        obj
+            The content object for which a permission should be removed.
+    """
+    if not isinstance(permission, Permission):
+        try:
+            permission = Permission.objects.get(codename = permission)
+        except Permission.DoesNotExist:
+            return False
+
+    ct = ContentType.objects.get_for_model(obj)
+    try:
+        op = ObjectPermission.objects.get(group=group, content_type = ct, content_id=obj.id, permission = permission)
+    except ObjectPermission.DoesNotExist:
+        return False
+
+    op.delete()
+    return True
+
+def has_permission(codename, user, obj=None):
+    """Checks whether the passed user has passed permission for passed object.
+
+    **Parameters:**
+
+    codename
+        The permission's codename which should be checked.
+    user
+        The user for which the permission should be checked.
+    obj
+        The object for which the permission should be checked.
+    """
+    if user.is_superuser:
+        return True
+
+    if not user.is_authenticated():
+        user = User.objects.get(username="anonymous")
+
+    if obj is None:
+        return False
+
+    groups = Group.objects.filter(user=user)
+    ct = ContentType.objects.get_for_model(obj)
+
+    while obj is not None:
+        p = ObjectPermission.objects.filter(
+            content_type=ct, content_id=obj.id, group__in=groups, permission__codename = codename)
+
+        if p.exists():
+            return True
+
+        if is_inherited(codename, obj):
+            return False
+
+        try:
+            obj = obj.get_parent_for_permissions()
+            ct = ContentType.objects.get_for_model(obj)
+        except AttributeError:
+            return False
+
+    return False
+
+# Inheritance ################################################################
+
+def add_inheritance_block(permission, obj):
+    """Adds an inheritance for the passed permission on the passed obj.
+
+    **Parameters:**
+
+        permission
+            The permission for which an inheritance block should be added. 
+            Either a permission object or the codename of a permission.
+        obj
+            The content object for which an inheritance block should be added.
+    """
+    if not isinstance(permission, Permission):
+        try:
+            permission = Permission.objects.get(codename = permission)
+        except Permission.DoesNotExist:
+            return False
+
+    ct = ContentType.objects.get_for_model(obj)
+    try:
+        ObjectPermissionInheritanceBlock.objects.get(content_type = ct, content_id=obj.id, permission=permission)
+    except ObjectPermissionInheritanceBlock.DoesNotExist:
+        try:
+            result = ObjectPermissionInheritanceBlock.objects.create(content=obj, permission=permission)
+        except IntegrityError:
+            return False
+    return True
+
+def remove_inheritance_block(permission, obj):
+    """Removes a inheritance block for the passed permission from the passed 
+    object.
+
+    **Parameters:**
+
+        permission
+            The permission for which an inheritance block should be removed.
+            Either a permission object or the codename of a permission.
+        obj
+            The content object for which an inheritance block should be added.
+    """
+    if not isinstance(permission, Permission):
+        try:
+            permission = Permission.objects.get(codename = permission)
+        except Permission.DoesNotExist:
+            return False
+
+    ct = ContentType.objects.get_for_model(obj)
+    try:
+        opi = ObjectPermissionInheritanceBlock.objects.get(content_type = ct, content_id=obj.id, permission=permission)
+    except ObjectPermissionInheritanceBlock.DoesNotExist:
+        return False
+
+    opi.delete()
+    return True
+
+def is_inherited(codename, obj):
+    """Returns True if the passed permission is inherited for passed object.
+
+    **Parameters:**
+
+        codename
+            The permission which should be checked. Must be the codename of
+            the permission.
+        obj
+            The content object for which the permission should be checked.
+    """
+    ct = ContentType.objects.get_for_model(obj)
+    try:
+        ObjectPermissionInheritanceBlock.objects.get(
+            content_type=ct, content_id=obj.id, permission__codename = codename)
+    except ObjectDoesNotExist:
+        return True
+    else:
+        return False
+
+# Registering ################################################################
+
+def register_permission(name, codename):
+    """Registers a permission to the framework. Returns the permission if the
+    registration was successfully, otherwise False.
+
+    **Parameters:**
+
+        name
+            The unique name of the permission. This is displayed to the
+            customer.
+
+        codename
+            The unique codename of the permission. This is used internally to
+            identify the permission.
+    """
+    try:
+        p = Permission.objects.create(name=name, codename=codename)
+    except IntegrityError:
+        return False
+    return p
+
+def unregister_permission(codename):
+    """Unregisters a permission from the framework
+
+    **Parameters:**
+
+        codename
+            The unique codename of the permission.
+    """
+    try:
+        permission = Permission.objects.get(codename=codename)
+    except Permission.DoesNotExist:
+        return False
+    permission.delete()
+    return True
+
+def register_group(name):
+    """Registers a group with passed name to the framework. Creates a Django 
+    default group. Returns the new group if the registration was successfully, 
+    otherwise False.
+
+    **Parameters:**
+
+        name
+            The unique group name.
+    """
+    try:
+        group = Group.objects.create(name=name)
+    except IntegrityError:
+        return False
+    return group
+
+def unregister_group(name):
+    """Unregisters the group with passed name. This will remove a Django
+    default group with passed name.
+
+    **Parameters:**
+
+        name
+            The unique group name.
+    """
+    try:
+        group = Group.objects.get(name=name)
+    except Group.DoesNotExist:
+        return False
+
+    group.delete()
+    return True
+from setuptools import setup, find_packages
+import os
+
+version = '1.0a1'
+
+here = os.path.abspath(os.path.dirname(__file__))
+README = open(os.path.join(here, 'README.txt')).read()
+
+setup(name='django-permissions',
+      version=version,
+      description='Generic object permissions framework for django',
+      long_description=README,
+      classifiers=[
+          'Development Status :: 3 - Alpha',
+          'Environment :: Web Environment',
+          'Framework :: Django',
+          'License :: OSI Approved :: BSD License',
+          'Operating System :: OS Independent',
+          'Programming Language :: Python',
+      ],
+      keywords='django authorization permissions',
+      author='Kai Diefenbach',
+      author_email='kai.diefenbach@iqpp.de',
+      url='http://www.iqpp.com',
+      license='BSD',
+      packages=find_packages(exclude=['ez_setup']),
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=[
+        'setuptools',
+      ],
+      )
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.