[Python-modules-commits] [django-organizations] 01/03: Imported Upstream version 0.9.1

Scott Kitterman kitterman at moszumanska.debian.org
Tue Jun 20 21:45:53 UTC 2017


This is an automated email from the git hooks/post-receive script.

kitterman pushed a commit to branch debian/master
in repository django-organizations.

commit af15f77925c22896cd42ae3a1d5d461ff98f075b
Author: Scott Kitterman <scott at kitterman.com>
Date:   Tue Jun 20 17:34:03 2017 -0400

    Imported Upstream version 0.9.1
---
 HISTORY.rst                                        |  14 ++
 PKG-INFO                                           |  30 +++-
 README.rst                                         |  14 ++
 django_organizations.egg-info/PKG-INFO             |  30 +++-
 django_organizations.egg-info/SOURCES.txt          |   5 +
 django_organizations.egg-info/requires.txt         |   2 +-
 organizations/__init__.py                          |   2 +-
 organizations/{models.py => abstract.py}           |  80 ++++++---
 organizations/admin.py                             |  22 ++-
 organizations/backends/defaults.py                 |  24 ++-
 organizations/base.py                              |  36 +++-
 organizations/{admin.py => base_admin.py}          |  17 +-
 organizations/forms.py                             |   7 +
 organizations/migrations/0002_model_update.py      |  20 +++
 organizations/mixins.py                            |   2 +-
 organizations/models.py                            | 193 ++-------------------
 .../organizations/email/notification_body.html     |   7 +
 .../organizations/email/notification_subject.txt   |   1 +
 setup.cfg                                          |   1 -
 19 files changed, 264 insertions(+), 243 deletions(-)

diff --git a/HISTORY.rst b/HISTORY.rst
index 1a26966..f335196 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -3,6 +3,20 @@
 History
 =======
 
+0.9.1
+-----
+
+* Fixes missing migration. Migration was created due to non-schema changes in models
+
+0.9.0
+-----
+
+* Add notification to users when added to an organization
+* New abstract models create separation between 'plain' base models and abstract
+  models that include abstracted functionality previously included only in
+  concrete models 
+* Python 3.6 and Django 1.11 test support 
+
 0.8.2
 -----
 
diff --git a/PKG-INFO b/PKG-INFO
index 15937af..6c24cea 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: django-organizations
-Version: 0.8.2
+Version: 0.9.1
 Summary: Group accounts for Django
 Home-page: https://github.com/bennylope/django-organizations/
 Author: Ben Lopatin
@@ -218,6 +218,20 @@ Description: ====================
         
             tox
         
+        Fast testing
+        ------------
+        
+        Testing each change on all the environments takes some time, you may
+        want to test faster and avoid slowing down development by using pytest
+        against your current environment::
+        
+            pip install -r requirements-test.txt
+            py.test
+        
+        Supply the ``-x`` option for **failfast** mode::
+        
+            py.test -x
+        
         Submitting
         ----------
         
@@ -263,6 +277,20 @@ Description: ====================
         History
         =======
         
+        0.9.1
+        -----
+        
+        * Fixes missing migration. Migration was created due to non-schema changes in models
+        
+        0.9.0
+        -----
+        
+        * Add notification to users when added to an organization
+        * New abstract models create separation between 'plain' base models and abstract
+          models that include abstracted functionality previously included only in
+          concrete models 
+        * Python 3.6 and Django 1.11 test support 
+        
         0.8.2
         -----
         
diff --git a/README.rst b/README.rst
index 833c66b..c6f5a0f 100644
--- a/README.rst
+++ b/README.rst
@@ -210,6 +210,20 @@ To run the tests against all target environments, install `tox
 
     tox
 
+Fast testing
+------------
+
+Testing each change on all the environments takes some time, you may
+want to test faster and avoid slowing down development by using pytest
+against your current environment::
+
+    pip install -r requirements-test.txt
+    py.test
+
+Supply the ``-x`` option for **failfast** mode::
+
+    py.test -x
+
 Submitting
 ----------
 
diff --git a/django_organizations.egg-info/PKG-INFO b/django_organizations.egg-info/PKG-INFO
index 15937af..6c24cea 100644
--- a/django_organizations.egg-info/PKG-INFO
+++ b/django_organizations.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: django-organizations
-Version: 0.8.2
+Version: 0.9.1
 Summary: Group accounts for Django
 Home-page: https://github.com/bennylope/django-organizations/
 Author: Ben Lopatin
@@ -218,6 +218,20 @@ Description: ====================
         
             tox
         
+        Fast testing
+        ------------
+        
+        Testing each change on all the environments takes some time, you may
+        want to test faster and avoid slowing down development by using pytest
+        against your current environment::
+        
+            pip install -r requirements-test.txt
+            py.test
+        
+        Supply the ``-x`` option for **failfast** mode::
+        
+            py.test -x
+        
         Submitting
         ----------
         
@@ -263,6 +277,20 @@ Description: ====================
         History
         =======
         
+        0.9.1
+        -----
+        
+        * Fixes missing migration. Migration was created due to non-schema changes in models
+        
+        0.9.0
+        -----
+        
+        * Add notification to users when added to an organization
+        * New abstract models create separation between 'plain' base models and abstract
+          models that include abstracted functionality previously included only in
+          concrete models 
+        * Python 3.6 and Django 1.11 test support 
+        
         0.8.2
         -----
         
diff --git a/django_organizations.egg-info/SOURCES.txt b/django_organizations.egg-info/SOURCES.txt
index 900f9c5..4e82974 100644
--- a/django_organizations.egg-info/SOURCES.txt
+++ b/django_organizations.egg-info/SOURCES.txt
@@ -11,10 +11,12 @@ django_organizations.egg-info/not-zip-safe
 django_organizations.egg-info/requires.txt
 django_organizations.egg-info/top_level.txt
 organizations/__init__.py
+organizations/abstract.py
 organizations/admin.py
 organizations/app_settings.py
 organizations/apps.py
 organizations/base.py
+organizations/base_admin.py
 organizations/exceptions.py
 organizations/fields.py
 organizations/forms.py
@@ -30,6 +32,7 @@ organizations/backends/defaults.py
 organizations/backends/forms.py
 organizations/backends/tokens.py
 organizations/migrations/0001_initial.py
+organizations/migrations/0002_model_update.py
 organizations/migrations/__init__.py
 organizations/templates/organizations_base.html
 organizations/templates/organizations/login.html
@@ -49,6 +52,8 @@ organizations/templates/organizations/email/activation_body.html
 organizations/templates/organizations/email/activation_subject.txt
 organizations/templates/organizations/email/invitation_body.html
 organizations/templates/organizations/email/invitation_subject.txt
+organizations/templates/organizations/email/notification_body.html
+organizations/templates/organizations/email/notification_subject.txt
 organizations/templates/organizations/email/reminder_body.html
 organizations/templates/organizations/email/reminder_subject.txt
 organizations/templatetags/__init__.py
diff --git a/django_organizations.egg-info/requires.txt b/django_organizations.egg-info/requires.txt
index 4c83297..f97e3c8 100644
--- a/django_organizations.egg-info/requires.txt
+++ b/django_organizations.egg-info/requires.txt
@@ -1 +1 @@
-Django>=1.8.0
\ No newline at end of file
+Django>=1.8.0
diff --git a/organizations/__init__.py b/organizations/__init__.py
index 7767276..9cbd29d 100644
--- a/organizations/__init__.py
+++ b/organizations/__init__.py
@@ -26,7 +26,7 @@
 
 __author__ = 'Ben Lopatin'
 __email__ = 'ben at wellfire.co'
-__version__ = '0.8.2'
+__version__ = '0.9.1'
 
 
 default_app_config = 'organizations.apps.OrganizationsConfig'
diff --git a/organizations/models.py b/organizations/abstract.py
similarity index 72%
copy from organizations/models.py
copy to organizations/abstract.py
index 5ee7208..2299510 100644
--- a/organizations/models.py
+++ b/organizations/abstract.py
@@ -29,8 +29,12 @@ from django.conf import settings
 from django.core.urlresolvers import reverse
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
+try:
+    import six
+except ImportError:
+    from django.utils import six
 
-from .base import OrganizationBase, OrganizationUserBase, OrganizationOwnerBase
+from .base import OrgMeta, AbstractBaseOrganization, AbstractBaseOrganizationUser, AbstractBaseOrganizationOwner
 from .fields import SlugField, AutoCreatedField, AutoLastModifiedField
 from .signals import user_added, user_removed, owner_changed
 
@@ -42,23 +46,43 @@ if ORGS_TIMESTAMPED_MODEL:
                   DeprecationWarning)
 
 
-class TimeStampedModel(models.Model):
+class SharedBaseModel(models.Model):
+    """
+    Adds fields ``created`` and ``modified`` and
+    two private methods that are used by the rest
+    of the abstract models.
+    """
     created = AutoCreatedField()
     modified = AutoLastModifiedField()
 
+    @property
+    def _org_user_model(self):
+        model = self.__class__.module_registry[self.__class__.__module__]['OrgUserModel']
+        if model is None:
+            model = self.__class__.module_registry['organizations.models']['OrgUserModel']
+        return model
+
+    @property
+    def _org_owner_model(self):
+        model = self.__class__.module_registry[self.__class__.__module__]['OrgOwnerModel']
+        if model is None:
+            model = self.__class__.module_registry['organizations.models']['OrgOwnerModel']
+        return model
+
     class Meta:
         abstract = True
 
 
-class Organization(OrganizationBase, TimeStampedModel):
+class AbstractOrganization(six.with_metaclass(OrgMeta, SharedBaseModel, AbstractBaseOrganization)):
     """
-    Default Organization model.
+    Abstract Organization model.
     """
     slug = SlugField(max_length=200, blank=False, editable=True,
             populate_from='name', unique=True,
             help_text=_("The name in all lowercase, suitable for URL identification"))
 
-    class Meta(OrganizationBase.Meta):
+    class Meta(AbstractBaseOrganization.Meta):
+        abstract = True
         verbose_name = _("organization")
         verbose_name_plural = _("organizations")
 
@@ -77,12 +101,13 @@ class Organization(OrganizationBase, TimeStampedModel):
         if users_count == 0:
             is_admin = True
         # TODO get specific org user?
-        org_user = OrganizationUser.objects.create(user=user,
-                organization=self, is_admin=is_admin)
+        org_user = self._org_user_model.objects.create(user=user,
+                                                       organization=self,
+                                                       is_admin=is_admin)
         if users_count == 0:
             # TODO get specific org user?
-            OrganizationOwner.objects.create(organization=self,
-                    organization_user=org_user)
+            self._org_owner_model.objects.create(organization=self,
+                                                 organization_user=org_user)
 
         # User added signal
         user_added.send(sender=self, user=user)
@@ -92,8 +117,8 @@ class Organization(OrganizationBase, TimeStampedModel):
         """
         Deletes a user from an organization.
         """
-        org_user = OrganizationUser.objects.get(user=user,
-                                                organization=self)
+        org_user = self._org_user_model.objects.get(user=user,
+                                                    organization=self)
         org_user.delete()
 
         # User removed signal
@@ -116,13 +141,13 @@ class Organization(OrganizationBase, TimeStampedModel):
         if users_count == 0:
             is_admin = True
 
-        org_user, created = OrganizationUser.objects.get_or_create(
-                organization=self, user=user, defaults={'is_admin': is_admin})
-
+        org_user, created = self._org_user_model.objects\
+                                .get_or_create(organization=self,
+                                               user=user,
+                                               defaults={'is_admin': is_admin})
         if users_count == 0:
-            OrganizationOwner.objects.create(organization=self,
-                    organization_user=org_user)
-
+            self._org_owner_model.objects\
+                .create(organization=self, organization_user=org_user)
         if created:
             # User added signal
             user_added.send(sender=self, user=user)
@@ -152,10 +177,14 @@ class Organization(OrganizationBase, TimeStampedModel):
         return self.owner.organization_user.user == user
 
 
-class OrganizationUser(OrganizationUserBase, TimeStampedModel):
+class AbstractOrganizationUser(six.with_metaclass(OrgMeta, SharedBaseModel, AbstractBaseOrganizationUser)):
+    """
+    Abstract OrganizationUser model
+    """
     is_admin = models.BooleanField(default=False)
 
-    class Meta(OrganizationUserBase.Meta):
+    class Meta(AbstractBaseOrganizationUser.Meta):
+        abstract = True
         verbose_name = _("organization user")
         verbose_name_plural = _("organization users")
 
@@ -176,18 +205,21 @@ class OrganizationUser(OrganizationUserBase, TimeStampedModel):
                 raise OwnershipRequired(_("Cannot delete organization owner "
                     "before organization or transferring ownership."))
         # TODO This line presumes that OrgOwner model can't be modified
-        except OrganizationOwner.DoesNotExist:
+        except self._org_owner_model.DoesNotExist:
             pass
-        super(OrganizationUserBase, self).delete(using=using)
+        super(AbstractBaseOrganizationUser, self).delete(using=using)
 
     def get_absolute_url(self):
         return reverse('organization_user_detail', kwargs={
             'organization_pk': self.organization.pk, 'user_pk': self.user.pk})
 
 
-class OrganizationOwner(OrganizationOwnerBase, TimeStampedModel):
-
+class AbstractOrganizationOwner(six.with_metaclass(OrgMeta, SharedBaseModel, AbstractBaseOrganizationOwner)):
+    """
+    Abstract OrganizationOwner model
+    """
     class Meta:
+        abstract = True
         verbose_name = _("organization owner")
         verbose_name_plural = _("organization owners")
 
@@ -206,4 +238,4 @@ class OrganizationOwner(OrganizationOwnerBase, TimeStampedModel):
         if self.organization_user.organization.pk != self.organization.pk:
             raise OrganizationMismatch
         else:
-            super(OrganizationOwnerBase, self).save(*args, **kwargs)
+            super(AbstractBaseOrganizationOwner, self).save(*args, **kwargs)
diff --git a/organizations/admin.py b/organizations/admin.py
index 0678481..08395eb 100644
--- a/organizations/admin.py
+++ b/organizations/admin.py
@@ -25,29 +25,27 @@
 
 from django.contrib import admin
 
+from .base_admin import (BaseOwnerInline,
+                         BaseOrganizationAdmin,
+                         BaseOrganizationUserAdmin,
+                         BaseOrganizationOwnerAdmin)
 from .models import Organization, OrganizationUser, OrganizationOwner
 
 
-class OwnerInline(admin.StackedInline):
+class OwnerInline(BaseOwnerInline):
     model = OrganizationOwner
-    raw_id_fields = ('organization_user',)
 
 
-class OrganizationAdmin(admin.ModelAdmin):
+class OrganizationAdmin(BaseOrganizationAdmin):
     inlines = [OwnerInline]
-    list_display = ['name', 'is_active']
-    prepopulated_fields = {"slug": ("name",)}
-    search_fields = ['name']
-    list_filter = ('is_active',)
 
 
-class OrganizationUserAdmin(admin.ModelAdmin):
-    list_display = ['user', 'organization', 'is_admin']
-    raw_id_fields = ('user', 'organization')
+class OrganizationUserAdmin(BaseOrganizationUserAdmin):
+    pass
 
 
-class OrganizationOwnerAdmin(admin.ModelAdmin):
-    raw_id_fields = ('organization_user', 'organization')
+class OrganizationOwnerAdmin(BaseOrganizationOwnerAdmin):
+    pass
 
 
 admin.site.register(Organization, OrganizationAdmin)
diff --git a/organizations/backends/defaults.py b/organizations/backends/defaults.py
index 77bb9a2..d0c2f63 100644
--- a/organizations/backends/defaults.py
+++ b/organizations/backends/defaults.py
@@ -36,7 +36,7 @@ from django.core.urlresolvers import reverse
 from django.core.mail import EmailMessage
 from django.http import Http404
 from django.shortcuts import render, redirect
-from django.template import Context, loader
+from django.template import loader
 from django.utils.translation import ugettext as _
 
 from ..utils import create_organization
@@ -149,12 +149,11 @@ class BaseBackend(object):
 
         headers = {'Reply-To': reply_to}
         kwargs.update({'sender': sender, 'user': user})
-        ctx = Context(kwargs, autoescape=False)
 
         subject_template = loader.get_template(subject_template)
         body_template = loader.get_template(body_template)
-        subject = subject_template.render(ctx).strip()  # Remove stray newline characters
-        body = body_template.render(ctx)
+        subject = subject_template.render(kwargs).strip()  # Remove stray newline characters
+        body = body_template.render(kwargs)
         return EmailMessage(subject, body, from_email, [user.email],
                 headers=headers).send()
 
@@ -244,6 +243,8 @@ class InvitationBackend(BaseBackend):
     A backend for inviting new users to join the site as members of an
     organization.
     """
+    notification_subject = 'organizations/email/notification_subject.txt'
+    notification_body = 'organizations/email/notification_body.html'
     invitation_subject = 'organizations/email/invitation_subject.txt'
     invitation_body = 'organizations/email/invitation_body.html'
     reminder_subject = 'organizations/email/reminder_subject.txt'
@@ -290,3 +291,18 @@ class InvitationBackend(BaseBackend):
         kwargs.update({'token': token})
         self._send_email(user, self.invitation_subject, self.invitation_body,
                 sender, **kwargs)
+
+    def send_notification(self, user, sender=None, **kwargs):
+        """
+        An intermediary function for sending an notification email informing
+        a pre-existing, active user that they have been added to a new
+        organization.
+        """
+        if not user.is_active:
+            return False
+        self._send_email(
+            user,
+            self.notification_subject,
+            self.notification_body,
+            sender,
+            **kwargs)
diff --git a/organizations/base.py b/organizations/base.py
index 095633e..8589c42 100644
--- a/organizations/base.py
+++ b/organizations/base.py
@@ -83,12 +83,15 @@ class OrgMeta(ModelBase):
                 'OrgOwnerModel': None,
             }
         for b in bases:
-            if b.__name__ == "OrganizationBase":
-                cls.module_registry[module]['OrgModel'] = model
-            elif b.__name__ == "OrganizationUserBase":
-                cls.module_registry[module]['OrgUserModel'] = model
-            elif b.__name__ == "OrganizationOwnerBase":
-                cls.module_registry[module]['OrgOwnerModel'] = model
+            key = None
+            if b.__name__ in ["AbstractOrganization", "OrganizationBase"]:
+                key = 'OrgModel'
+            elif b.__name__ in ["AbstractOrganizationUser", "OrganizationUserBase"]:
+                key = 'OrgUserModel'
+            elif b.__name__ in ["AbstractOrganizationOwner", "OrganizationOwnerBase"]:
+                key = 'OrgOwnerModel'
+            if key:
+                cls.module_registry[module][key] = model
 
         if all([cls.module_registry[module][klass] for klass in base_classes]):
             model.update_org(module)
@@ -143,7 +146,7 @@ class OrgMeta(ModelBase):
                         related_name="owner"))
 
 
-class OrganizationBase(six.with_metaclass(OrgMeta, UnicodeMixin, models.Model)):
+class AbstractBaseOrganization(UnicodeMixin, models.Model):
     """
     The umbrella object with which users can be associated.
 
@@ -180,7 +183,12 @@ class OrganizationBase(six.with_metaclass(OrgMeta, UnicodeMixin, models.Model)):
         return True if user in self.users.all() else False
 
 
-class OrganizationUserBase(six.with_metaclass(OrgMeta, UnicodeMixin, models.Model)):
+class OrganizationBase(six.with_metaclass(OrgMeta, AbstractBaseOrganization)):
+    class Meta(AbstractBaseOrganization.Meta):
+        abstract = True
+
+
+class AbstractBaseOrganizationUser(UnicodeMixin, models.Model):
     """
     ManyToMany through field relating Users to Organizations.
 
@@ -212,7 +220,12 @@ class OrganizationUserBase(six.with_metaclass(OrgMeta, UnicodeMixin, models.Mode
         return "{0}".format(self.user)
 
 
-class OrganizationOwnerBase(six.with_metaclass(OrgMeta, UnicodeMixin, models.Model)):
+class OrganizationUserBase(six.with_metaclass(OrgMeta, AbstractBaseOrganizationUser)):
+    class Meta(AbstractBaseOrganizationUser.Meta):
+        abstract = True
+
+
+class AbstractBaseOrganizationOwner(UnicodeMixin, models.Model):
     """
     Each organization must have one and only one organization owner.
     """
@@ -222,3 +235,8 @@ class OrganizationOwnerBase(six.with_metaclass(OrgMeta, UnicodeMixin, models.Mod
 
     def __unicode__(self):
         return u"{0}: {1}".format(self.organization, self.organization_user)
+
+
+class OrganizationOwnerBase(six.with_metaclass(OrgMeta, AbstractBaseOrganizationOwner)):
+    class Meta(AbstractBaseOrganizationOwner.Meta):
+        abstract = True
diff --git a/organizations/admin.py b/organizations/base_admin.py
similarity index 78%
copy from organizations/admin.py
copy to organizations/base_admin.py
index 0678481..368395b 100644
--- a/organizations/admin.py
+++ b/organizations/base_admin.py
@@ -25,31 +25,22 @@
 
 from django.contrib import admin
 
-from .models import Organization, OrganizationUser, OrganizationOwner
 
-
-class OwnerInline(admin.StackedInline):
-    model = OrganizationOwner
+class BaseOwnerInline(admin.StackedInline):
     raw_id_fields = ('organization_user',)
 
 
-class OrganizationAdmin(admin.ModelAdmin):
-    inlines = [OwnerInline]
+class BaseOrganizationAdmin(admin.ModelAdmin):
     list_display = ['name', 'is_active']
     prepopulated_fields = {"slug": ("name",)}
     search_fields = ['name']
     list_filter = ('is_active',)
 
 
-class OrganizationUserAdmin(admin.ModelAdmin):
+class BaseOrganizationUserAdmin(admin.ModelAdmin):
     list_display = ['user', 'organization', 'is_admin']
     raw_id_fields = ('user', 'organization')
 
 
-class OrganizationOwnerAdmin(admin.ModelAdmin):
+class BaseOrganizationOwnerAdmin(admin.ModelAdmin):
     raw_id_fields = ('organization_user', 'organization')
-
-
-admin.site.register(Organization, OrganizationAdmin)
-admin.site.register(OrganizationUser, OrganizationUserAdmin)
-admin.site.register(OrganizationOwner, OrganizationOwnerAdmin)
diff --git a/organizations/forms.py b/organizations/forms.py
index 44bdad8..8ffb445 100644
--- a/organizations/forms.py
+++ b/organizations/forms.py
@@ -105,6 +105,13 @@ class OrganizationUserAddForm(forms.ModelForm):
                     **{'domain': get_current_site(self.request),
                         'organization': self.organization,
                         'sender': self.request.user})
+        # Send a notification email to this user to inform them that they
+        # have been added to a new organization.
+        invitation_backend().send_notification(user, **{
+            'domain': get_current_site(self.request),
+            'organization': self.organization,
+            'sender': self.request.user,
+        })
         return OrganizationUser.objects.create(user=user,
                 organization=self.organization,
                 is_admin=self.cleaned_data['is_admin'])
diff --git a/organizations/migrations/0002_model_update.py b/organizations/migrations/0002_model_update.py
new file mode 100644
index 0000000..3f9fc35
--- /dev/null
+++ b/organizations/migrations/0002_model_update.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import organizations.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('organizations', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='organization',
+            name='slug',
+            field=organizations.fields.SlugField(help_text='The name in all lowercase, suitable for URL identification', unique=True, populate_from='name', max_length=200, editable=True),
+        ),
+    ]
diff --git a/organizations/mixins.py b/organizations/mixins.py
index 7d87263..f17dc16 100644
--- a/organizations/mixins.py
+++ b/organizations/mixins.py
@@ -76,7 +76,7 @@ class OrganizationUserMixin(OrganizationMixin):
         organization_pk = self.kwargs.get('organization_pk', None)
         user_pk = self.kwargs.get('user_pk', None)
         self.organization_user = get_object_or_404(
-                OrganizationUser.objects.select_related(),
+                self.get_user_model().objects.select_related(),
                 user__pk=user_pk, organization__pk=organization_pk)
         return self.organization_user
 
diff --git a/organizations/models.py b/organizations/models.py
index 5ee7208..b999412 100644
--- a/organizations/models.py
+++ b/organizations/models.py
@@ -23,187 +23,30 @@
 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-import warnings
+from .abstract import (AbstractOrganization,
+                       AbstractOrganizationUser,
+                       AbstractOrganizationOwner)
 
-from django.conf import settings
-from django.core.urlresolvers import reverse
-from django.db import models
-from django.utils.translation import ugettext_lazy as _
 
-from .base import OrganizationBase, OrganizationUserBase, OrganizationOwnerBase
-from .fields import SlugField, AutoCreatedField, AutoLastModifiedField
-from .signals import user_added, user_removed, owner_changed
-
-USER_MODEL = getattr(settings, 'AUTH_USER_MODEL', 'auth.User')
-ORGS_TIMESTAMPED_MODEL = getattr(settings, 'ORGS_TIMESTAMPED_MODEL', None)
-
-if ORGS_TIMESTAMPED_MODEL:
-    warnings.warn("Configured TimestampModel has been replaced and is now ignored.",
-                  DeprecationWarning)
-
-
-class TimeStampedModel(models.Model):
-    created = AutoCreatedField()
-    modified = AutoLastModifiedField()
-
-    class Meta:
-        abstract = True
-
-
-class Organization(OrganizationBase, TimeStampedModel):
+class Organization(AbstractOrganization):
     """
     Default Organization model.
     """
-    slug = SlugField(max_length=200, blank=False, editable=True,
-            populate_from='name', unique=True,
-            help_text=_("The name in all lowercase, suitable for URL identification"))
-
-    class Meta(OrganizationBase.Meta):
-        verbose_name = _("organization")
-        verbose_name_plural = _("organizations")
-
-    def __unicode__(self):
-        return self.name
-
-    def get_absolute_url(self):
-        return reverse('organization_detail', kwargs={'organization_pk': self.pk})
-
-    def add_user(self, user, is_admin=False):
-        """
-        Adds a new user and if the first user makes the user an admin and
-        the owner.
-        """
-        users_count = self.users.all().count()
-        if users_count == 0:
-            is_admin = True
-        # TODO get specific org user?
-        org_user = OrganizationUser.objects.create(user=user,
-                organization=self, is_admin=is_admin)
-        if users_count == 0:
-            # TODO get specific org user?
-            OrganizationOwner.objects.create(organization=self,
-                    organization_user=org_user)
-
-        # User added signal
-        user_added.send(sender=self, user=user)
-        return org_user
-
-    def remove_user(self, user):
-        """
-        Deletes a user from an organization.
-        """
-        org_user = OrganizationUser.objects.get(user=user,
-                                                organization=self)
-        org_user.delete()
-
-        # User removed signal
-        user_removed.send(sender=self, user=user)
-
-    def get_or_add_user(self, user, **kwargs):
-        """
-        Adds a new user to the organization, and if it's the first user makes
-        the user an admin and the owner. Uses the `get_or_create` method to
-        create or return the existing user.
-
-        `user` should be a user instance, e.g. `auth.User`.
-
-        Returns the same tuple as the `get_or_create` method, the
-        `OrganizationUser` and a boolean value indicating whether the
-        OrganizationUser was created or not.
-        """
-        is_admin = kwargs.pop('is_admin', False)
-        users_count = self.users.all().count()
-        if users_count == 0:
-            is_admin = True
-
-        org_user, created = OrganizationUser.objects.get_or_create(
-                organization=self, user=user, defaults={'is_admin': is_admin})
-
-        if users_count == 0:
-            OrganizationOwner.objects.create(organization=self,
-                    organization_user=org_user)
+    class Meta(AbstractOrganization.Meta):
+        abstract = False
 
-        if created:
-            # User added signal
-            user_added.send(sender=self, user=user)
-        return org_user, created
 
-    def change_owner(self, new_owner):
-        """
-        Changes ownership of an organization.
-        """
-        old_owner = self.owner.organization_user
-        self.owner.organization_user = new_owner
-        self.owner.save()
-
-        # Owner changed signal
-        owner_changed.send(sender=self, old=old_owner, new=new_owner)
-
-    def is_admin(self, user):
-        """
-        Returns True is user is an admin in the organization, otherwise false
-        """
-        return True if self.organization_users.filter(user=user, is_admin=True) else False
-
-    def is_owner(self, user):
-        """
-        Returns True is user is the organization's owner, otherwise false
-        """
-        return self.owner.organization_user.user == user
-
-
-class OrganizationUser(OrganizationUserBase, TimeStampedModel):
-    is_admin = models.BooleanField(default=False)
-
-    class Meta(OrganizationUserBase.Meta):
-        verbose_name = _("organization user")
-        verbose_name_plural = _("organization users")
-
-    def __unicode__(self):
-        return u"{0} ({1})".format(self.name if self.user.is_active else
-                self.user.email, self.organization.name)
-
-    def delete(self, using=None):
-        """
-        If the organization user is also the owner, this should not be deleted
-        unless it's part of a cascade from the Organization.
-
-        If there is no owner then the deletion should proceed.
-        """
-        from organizations.exceptions import OwnershipRequired
-        try:
-            if self.organization.owner.organization_user.id == self.id:
-                raise OwnershipRequired(_("Cannot delete organization owner "
-                    "before organization or transferring ownership."))
-        # TODO This line presumes that OrgOwner model can't be modified
-        except OrganizationOwner.DoesNotExist:
-            pass
-        super(OrganizationUserBase, self).delete(using=using)
-
-    def get_absolute_url(self):
-        return reverse('organization_user_detail', kwargs={
-            'organization_pk': self.organization.pk, 'user_pk': self.user.pk})
-
-
-class OrganizationOwner(OrganizationOwnerBase, TimeStampedModel):
-
-    class Meta:
-        verbose_name = _("organization owner")
-        verbose_name_plural = _("organization owners")
-
-    def save(self, *args, **kwargs):
-        """
-        Extends the default save method by verifying that the chosen
-        organization user is associated with the organization.
+class OrganizationUser(AbstractOrganizationUser):
+    """
+    Default OrganizationUser model.
+    """
+    class Meta(AbstractOrganizationUser.Meta):
+        abstract = False
 
-        Method validates against the primary key of the organization because
-        when validating an inherited model it may be checking an instance of
-        `Organization` against an instance of `CustomOrganization`. Mutli-table
-        inheritence means the database keys will be identical though.
 
-        """
-        from organizations.exceptions import OrganizationMismatch
-        if self.organization_user.organization.pk != self.organization.pk:
-            raise OrganizationMismatch
-        else:
-            super(OrganizationOwnerBase, self).save(*args, **kwargs)
+class OrganizationOwner(AbstractOrganizationOwner):
+    """
+    Default OrganizationOwner model.
+    """
+    class Meta(AbstractOrganizationOwner.Meta):
+        abstract = False
diff --git a/organizations/templates/organizations/email/notification_body.html b/organizations/templates/organizations/email/notification_body.html
new file mode 100644
index 0000000..c14281c
--- /dev/null
+++ b/organizations/templates/organizations/email/notification_body.html
@@ -0,0 +1,7 @@
+You've been added to the organization {{ organization|safe }} on {{ domain.name }} by {{ sender.full_name|safe }}.`
+
+Follow the link below to view this organization.
+
+http://{{ domain.domain }}{% url "organization_detail" organization.pk %}
+
+If you are unsure about this link please contact the sender.
diff --git a/organizations/templates/organizations/email/notification_subject.txt b/organizations/templates/organizations/email/notification_subject.txt
new file mode 100644
index 0000000..f97d2f8
--- /dev/null
+++ b/organizations/templates/organizations/email/notification_subject.txt
@@ -0,0 +1 @@
+{% spaceless %}You've been added to an organization{% endspaceless %}
diff --git a/setup.cfg b/setup.cfg
index 6f08d0e..adf5ed7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -4,5 +4,4 @@ universal = 1
 [egg_info]
 tag_build = 
 tag_date = 0
-tag_svn_revision = 0
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-organizations.git



More information about the Python-modules-commits mailing list