[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