Permalink
Browse files

Merge pull request #2753 from koirikivi/develop

Adding support for custom user models (with fixes)
  • Loading branch information...
2 parents c83ea12 + 1d361ad commit 168d276e3384b3e2872757c95ef825ff5af2007d @digi604 digi604 committed Feb 28, 2014
Showing with 2,109 additions and 831 deletions.
  1. +1 −0 .travis.yml
  2. +1 −0 AUTHORS
  3. +2 −1 CHANGELOG.txt
  4. +11 −2 cms/admin/forms.py
  5. +3 −3 cms/admin/permissionadmin.py
  6. +21 −9 cms/admin/useradmin.py
  7. +22 −6 cms/api.py
  8. +3 −1 cms/cache/permissions.py
  9. +4 −1 cms/cms_toolbar.py
  10. +29 −0 cms/compat.py
  11. +36 −0 cms/compat_forms.py
  12. +2 −2 cms/management/commands/publisher_publish.py
  13. +8 −3 cms/menu.py
  14. +28 −17 cms/migrations/0001_initial.py
  15. +21 −10 cms/migrations/0002_auto_start.py
  16. +21 −10 cms/migrations/0003_remove_placeholder.py
  17. +21 −10 cms/migrations/0004_textobjects.py
  18. +21 −10 cms/migrations/0005_mptt_added_to_plugins.py
  19. +21 −10 cms/migrations/0006_apphook.py
  20. +21 −10 cms/migrations/0007_apphook_longer.py
  21. +21 −10 cms/migrations/0008_redirects.py
  22. +21 −10 cms/migrations/0009_added_meta_fields.py
  23. +21 −10 cms/migrations/0010_5char_language.py
  24. +21 −10 cms/migrations/0011_title_overwrites.py
  25. +21 −10 cms/migrations/0012_publisher.py
  26. +21 −10 cms/migrations/0013_site_copy.py
  27. +21 −10 cms/migrations/0014_sites_removed.py
  28. +21 −10 cms/migrations/0015_modified_by_added.py
  29. +21 −10 cms/migrations/0016_author_copy.py
  30. +21 −10 cms/migrations/0017_author_removed.py
  31. +21 −10 cms/migrations/0018_site_permissions.py
  32. +21 −10 cms/migrations/0019_public_table_renames.py
  33. +21 −10 cms/migrations/0020_advanced_permissions.py
  34. +21 −10 cms/migrations/0021_publisher2.py
  35. +21 −10 cms/migrations/0022_login_required_added.py
  36. +21 −10 cms/migrations/0023_plugin_table_naming_function_changed.py
  37. +21 −10 cms/migrations/0024_added_placeholder_model.py
  38. +21 −10 cms/migrations/0025_placeholder_migration.py
  39. +21 −10 cms/migrations/0026_finish_placeholder_migration.py
  40. +21 −10 cms/migrations/0027_added_width_to_placeholder.py
  41. +21 −10 cms/migrations/0028_limit_visibility_in_menu_step1of3.py
  42. +21 −10 cms/migrations/0029_limit_visibility_in_menu_step2of3_data.py
  43. +21 −10 cms/migrations/0030_limit_visibility_in_menu_step3of3.py
  44. +21 −10 cms/migrations/0031_improved_language_code_support.py
  45. +21 −10 cms/migrations/0032_auto__del_field_cmsplugin_publisher_public__del_field_cmsplugin_publis.py
  46. +21 −10 cms/migrations/0033_auto__del_field_title_publisher_is_draft__del_field_title_publisher_st.py
  47. +21 −10 cms/migrations/0034_auto__chg_field_title_language__chg_field_cmsplugin_language__add_fiel.py
  48. +21 −10 cms/migrations/0035_auto__add_field_globalpagepermission_can_view__add_field_pagepermissio.py
  49. +21 −10 cms/migrations/0036_auto__add_field_cmsplugin_changed_date.py
  50. +21 −10 cms/migrations/0037_auto__del_pagemoderator__del_field_globalpagepermission_can_moderate__.py
  51. +20 −9 cms/migrations/0038_publish_page_permission.py
  52. +20 −9 cms/migrations/0039_auto__del_field_page_moderator_state.py
  53. +20 −9 cms/migrations/0040_auto__del_field_title_meta_keywords__chg_field_title_meta_description.py
  54. +22 −11 cms/migrations/0041_auto__add_usersettings.py
  55. +21 −10 cms/migrations/0042_auto__del_field_title_meta_keywords__chg_field_title_meta_description_.py
  56. +21 −10 cms/migrations/0043_auto__add_field_usersettings_clipboard.py
  57. +21 −10 cms/migrations/0044_killsettings.py
  58. +21 −10 cms/migrations/0045_auto__add_field_page_application_urls.py
  59. +21 −10 cms/migrations/0046_move_apphooks.py
  60. +21 −10 cms/migrations/0047_auto__del_field_title_application_urls.py
  61. +21 −10 cms/migrations/0048_auto__add_field_page_application_namespace__add_unique_page_publisher_.py
  62. +6 −2 cms/models/managers.py
  63. +5 −2 cms/models/pagemodel.py
  64. +25 −6 cms/models/permissionmodels.py
  65. +2 −2 cms/models/settingmodels.py
  66. +1 −1 cms/plugin_base.py
  67. +5 −2 cms/signals/permissions.py
  68. +3 −3 cms/templates/cms/toolbar/plugin.html
  69. +8 −1 cms/test_utils/cli.py
  70. +1 −0 cms/test_utils/project/customuserapp/__init__.py
  71. +12 −0 cms/test_utils/project/customuserapp/admin.py
  72. +8 −0 cms/test_utils/project/customuserapp/models.py
  73. +1 −0 cms/test_utils/project/emailuserapp/__init__.py
  74. +41 −0 cms/test_utils/project/emailuserapp/admin.py
  75. +112 −0 cms/test_utils/project/emailuserapp/forms.py
  76. +154 −0 cms/test_utils/project/emailuserapp/models.py
  77. +24 −6 cms/test_utils/testcases.py
  78. +3 −2 cms/test_utils/util/context_managers.py
  79. +110 −30 cms/tests/admin.py
  80. +16 −10 cms/tests/api.py
  81. +12 −9 cms/tests/apphooks.py
  82. +10 −2 cms/tests/extensions.py
  83. +26 −8 cms/tests/forms.py
  84. +17 −20 cms/tests/frontend.py
  85. +2 −3 cms/tests/mail.py
  86. +22 −13 cms/tests/menu.py
  87. +51 −21 cms/tests/menu_page_viewperm.py
  88. +60 −12 cms/tests/menu_page_viewperm_staff.py
  89. +10 −3 cms/tests/multilingual.py
  90. +5 −5 cms/tests/no_i18n.py
  91. +3 −4 cms/tests/nonroot.py
  92. +36 −32 cms/tests/permmod.py
  93. +12 −7 cms/tests/placeholder.py
  94. +9 −17 cms/tests/plugins.py
  95. +5 −6 cms/tests/publisher.py
  96. +3 −4 cms/tests/rendering.py
  97. +15 −16 cms/tests/reversion_tests.py
  98. +13 −6 cms/tests/security.py
  99. +3 −3 cms/tests/signals.py
  100. +3 −4 cms/tests/site.py
  101. +2 −4 cms/tests/static_placeholder.py
  102. +13 −7 cms/tests/templatetags.py
  103. +10 −28 cms/tests/toolbar.py
  104. +7 −2 cms/tests/views.py
  105. +3 −2 cms/utils/check.py
  106. +9 −5 cms/utils/permissions.py
  107. +30 −5 develop.py
  108. +4 −0 docs/contributing/testing.rst
  109. +16 −0 docs/getting_started/configuration.rst
  110. +4 −2 docs/upgrade/3.0.rst
  111. +1 −1 setup.py
View
@@ -21,6 +21,7 @@ env:
- DJANGO=trunk DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
- DJANGO=trunk DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
- DJANGO=trunk DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=0
+ - DJANGO=1.6 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='emailuserapp.EmailUser'
before_script:
- sh -c "if [ '$DATABASE_URL' = 'postgres://postgres@127.0.0.1/djangocms_test' ];
then psql -c 'DROP DATABASE IF EXISTS djangocms_test;' -U postgres; fi"
View
@@ -259,6 +259,7 @@ Contributors (in alphabetical order):
* Thomas Parslow
* Thomas Woolford
* timesong
+* Tim Anderegg
* Tim Davies
* Tim Graham
* Tino de Bruijn
View
@@ -215,4 +215,5 @@ Please see Install/2.4 release notes *before* attempting to upgrade to version 2
- Removed the plugin DB-name magic and added a compatibility layer
- urls_need_reloading signal added when an apphook change is detected.
- CMS_PAGE_CACHE, CMS_PLACEHOLDER_CACHE and CMS_PLUGIN_CACHE settings and functionality added. Default is True
-- Detect admin object creation and changes via toolbar and redirect to them.
+- Detect admin object creation and changes via toolbar and redirect to them.
+- Added support for custom user models
View
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
import sys
from cms.apphook_pool import apphook_pool
+from cms.compat import get_user_model
+from cms.compat_forms import UserCreationForm
from cms.forms.widgets import UserSelectAdminWidget
from cms.models import Page, PagePermission, PageUser, ACCESS_PAGE, PageUserGroup, titlemodels
from cms.utils.conf import get_cms_setting
@@ -11,8 +13,7 @@
from cms.utils.permissions import get_current_user, get_subordinate_users, get_subordinate_groups, \
get_user_permission_level
from django import forms
-from django.contrib.auth.forms import UserCreationForm
-from django.contrib.auth.models import Permission, User
+from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site
from django.core.exceptions import ValidationError
@@ -25,6 +26,8 @@
def get_permission_acessor(obj):
+ User = get_user_model()
+
if isinstance(obj, (PageUser, User,)):
rel_name = 'user_permissions'
else:
@@ -452,6 +455,12 @@ def clean_username(self):
return self.cleaned_data['username']
return super(PageUserForm, self).clean_username()
+ # required if the User model's USERNAME_FIELD is the email field
+ def clean_email(self):
+ if self.instance:
+ return self.cleaned_data['email']
+ return super(PageUserForm, self).clean_email()
+
def clean_password2(self):
if self.instance and self.cleaned_data['password1'] == '' and self.cleaned_data['password2'] == '':
self._password_change = False
@@ -7,11 +7,11 @@
from cms.utils.conf import get_cms_setting
from cms.utils.helpers import classproperty
from cms.utils.permissions import get_user_permission_level
+from cms.compat import get_user_model
from django.contrib import admin
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
-
PERMISSION_ADMIN_INLINES = []
@@ -31,7 +31,7 @@ class PagePermissionInlineAdmin(TabularInline):
def raw_id_fields(cls):
# Dynamically set raw_id_fields based on settings
threshold = get_cms_setting('RAW_ID_USERS')
- if threshold and User.objects.count() > threshold:
+ if threshold and get_user_model().objects.count() > threshold:
return ['user']
return []
@@ -117,7 +117,7 @@ class GlobalPagePermissionAdmin(admin.ModelAdmin):
form = GlobalPagePermissionAdminForm
- search_fields = ('user__username', 'user__first_name', 'user__last_name', 'group__name')
+ search_fields = ('user__'+get_user_model().USERNAME_FIELD, 'user__first_name', 'user__last_name', 'group__name')
exclude = []
View
@@ -1,38 +1,50 @@
# -*- coding: utf-8 -*-
from cms.utils.conf import get_cms_setting
from django.utils.translation import ugettext as _
-
from django.contrib import admin
-from django.contrib.auth.admin import UserAdmin
from cms.admin.forms import PageUserForm, PageUserGroupForm
from cms.admin.permissionadmin import GenericCmsPermissionAdmin
from cms.exceptions import NoPermissionsException
from cms.models import PageUser, PageUserGroup
from cms.utils.permissions import get_subordinate_users
-
+from cms.compat import get_user_model
+from cms.compat_forms import UserAdmin
class PageUserAdmin(UserAdmin, GenericCmsPermissionAdmin):
form = PageUserForm
add_form = PageUserForm
model = PageUser
- list_display = ('username', 'email', 'first_name', 'last_name', 'created_by')
+ list_display = ('email', 'first_name', 'last_name', 'created_by')
+
+ if get_user_model().USERNAME_FIELD != 'email':
+ list_display += (get_user_model().USERNAME_FIELD,)
# get_fieldsets method may add fieldsets depending on user
fieldsets = [
- (None, {'fields': ('username', ('password1', 'password2'), 'notify_user')}),
- (_('User details'), {'fields': (('first_name', 'last_name'), 'email')}),
- (_('Groups'), {'fields': ('groups',)}),
+ (None, {'fields': (get_user_model().USERNAME_FIELD, ('password1', 'password2'), 'notify_user')}),
]
+
+ if get_user_model().USERNAME_FIELD != 'email':
+ fieldsets.append((_('User details'), {'fields': (('first_name', 'last_name'), 'email')}))
+ else:
+ fieldsets.append((_('User details'), {'fields': (('first_name', 'last_name'))}))
+
+ fieldsets.append((_('Groups'), {'fields': ('groups',)}))
add_fieldsets = fieldsets
+
+ ordering = ('last_name', 'first_name', 'email')
+
+ if get_user_model().USERNAME_FIELD != 'email':
+ ordering = (get_user_model().USERNAME_FIELD,) + ordering
def get_fieldsets(self, request, obj=None):
fieldsets = self.update_permission_fieldsets(request, obj)
if not '/add' in request.path:
- fieldsets[0] = (None, {'fields': ('username', 'notify_user')})
+ fieldsets[0] = (None, {'fields': (get_user_model().USERNAME_FIELD, 'notify_user')})
fieldsets.append((_('Password'), {'fields': ('password1', 'password2'), 'classes': ('collapse',)}))
return fieldsets
@@ -45,7 +57,7 @@ def queryset(self, request):
return self.model.objects.get_empty_query_set()
def add_view(self, request):
- return super(UserAdmin, self).add_view(request)
+ return super(UserAdmin, self).add_view(request)
class PageUserGroupAdmin(admin.ModelAdmin, GenericCmsPermissionAdmin):
form = PageUserGroupForm
View
@@ -13,22 +13,37 @@
from django.core.exceptions import PermissionDenied, ValidationError, FieldError
from cms.utils.i18n import get_language_list
-from django.contrib.auth.models import User
+from cms.compat import get_user_model
from django.contrib.sites.models import Site
from django.template.defaultfilters import slugify
from menus.menu_pool import menu_pool
from cms.admin.forms import save_permissions
from cms.app_base import CMSApp
from cms.apphook_pool import apphook_pool
+from cms.compat import get_user_model
from cms.models.pagemodel import Page
-from cms.models.permissionmodels import PageUser, PagePermission, GlobalPagePermission, ACCESS_PAGE_AND_DESCENDANTS
+from cms.models.permissionmodels import PageUser, PagePermission, \
+ GlobalPagePermission, ACCESS_PAGE_AND_DESCENDANTS
from cms.models.placeholdermodel import Placeholder
from cms.models.pluginmodel import CMSPlugin
from cms.models.titlemodels import Title
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
+from cms.utils.compat.type_checks import string_types
+from cms.utils.conf import get_cms_setting
+from cms.utils.i18n import get_language_list
from cms.utils.permissions import _thread_locals
+from django.conf import settings
+from django.contrib.sites.models import Site
+from django.core.exceptions import PermissionDenied, ValidationError
+from django.db.models import Max
+from django.template.defaultfilters import slugify
+from menus.menu_pool import menu_pool
+import datetime
+
+
+
#===============================================================================
@@ -122,9 +137,10 @@ def create_page(title, template, language, menu_title=None, slug=None,
See docs/extending_cms/api_reference.rst for more info
"""
# ugly permissions hack
- if created_by and isinstance(created_by, User):
+ if created_by and isinstance(created_by, get_user_model()):
_thread_locals.user = created_by
- created_by = created_by.username
+
+ created_by = getattr(created_by, get_user_model().USERNAME_FIELD)
else:
_thread_locals.user = None
@@ -317,7 +333,7 @@ def create_page_user(created_by, user,
True, True, True, True, True, True, True)
# validate created_by
- assert isinstance(created_by, User)
+ assert isinstance(created_by, get_user_model())
data = {
'can_add_page': can_add_page,
@@ -335,7 +351,7 @@ def create_page_user(created_by, user,
user.is_staff = True
user.is_active = True
page_user = PageUser(created_by=created_by)
- for field in [f.name for f in User._meta.local_fields]:
+ for field in [f.name for f in get_user_model()._meta.local_fields]:
setattr(page_user, field, getattr(user, field))
user.save()
page_user.save()
View
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
+from cms.compat import get_user_model
from cms.utils import get_cms_setting
@@ -10,8 +11,9 @@
def get_cache_key(user, key):
+ username = getattr(user, get_user_model().USERNAME_FIELD)
return "%s:permission:%s:%s" % (
- get_cms_setting('CACHE_PREFIX'), user.username, key)
+ get_cms_setting('CACHE_PREFIX'), username, key)
def get_cache_version_key():
return "%s:permission:version" % (get_cms_setting('CACHE_PREFIX'),)
View
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
+import urllib
+from cms.compat import user_model_label
from cms.api import get_page_draft
from cms.constants import TEMPLATE_INHERITANCE_MAGIC, RIGHT
from cms.exceptions import LanguageError
@@ -88,7 +90,7 @@ def populate(self):
def add_admin_menu(self):
admin_menu = self.toolbar.get_or_create_menu(ADMIN_MENU_IDENTIFIER, self.current_site.name)
if self.request.user.has_perm('user.change_user') and User in admin.site._registry:
- admin_menu.add_sideframe_item(_('Users'), url=reverse("admin:auth_user_changelist"))
+ admin_menu.add_sideframe_item(_('Users'), url=reverse("admin:"+user_model_label.replace('.','_').lower()+"_changelist"))
# sites menu
if get_cms_setting('PERMISSION'):
sites_queryset = get_user_sites_queryset(self.request.user)
@@ -254,6 +256,7 @@ def change_admin_menu(self):
url += "&page_id=%s" % self.page.pk
admin_menu.add_sideframe_item(_('Pages'), url=url, position=0)
+
def add_page_menu(self):
# menu for current page
not_edit_mode = not self.toolbar.edit_mode
View
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+from django.conf import settings
+import django
+
+__all__ = ['User', 'get_user_model', 'user_model_label', 'user_related_name',
+ 'user_related_query_name']
+
+# Django 1.5+ compatibility
+if django.VERSION >= (1, 5):
+ from django.contrib.auth import get_user_model
+ from django.contrib.auth.models import User as OriginalUser
+ is_user_swapped = bool(OriginalUser._meta.swapped)
+else:
+ from django.contrib.auth.models import User
+ User.USERNAME_FIELD = 'username'
+ get_user_model = lambda: User
+ is_user_swapped = False
+
+user_model_label = getattr(settings, 'AUTH_USER_MODEL', 'auth.User')
+
+# With a custom user model named "EmailUser", Django 1.5 creates
+# Group.emailuser_set but Django 1.6 creates Group.user_set.
+# See https://code.djangoproject.com/ticket/20244
+if (1, 5) <= django.VERSION < (1, 6):
+ user_related_query_name = user_model_label.split('.')[1].lower()
+ user_related_name = user_related_query_name + '_set'
+else:
+ user_related_query_name = "user"
+ user_related_name = "user_set"
View
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+from django.utils import importlib
+from django.db import models
+
+from cms.compat import is_user_swapped, user_model_label
+
+# Some custom user models may require a custom UserAdmin class and associated forms,
+# so check if they exist and import them
+from django.contrib.auth.forms import UserCreationForm, UserChangeForm
+from django.contrib.auth.admin import UserAdmin
+
+# overide with custom classes if they exist
+if is_user_swapped:
+ # UserAdmin class
+ user_app_name = user_model_label.split('.')[0]
+ app = models.get_app(user_app_name)
+
+ try:
+ custom_admin = importlib.import_module(app.__name__[:-6] + "admin")
+
+ if hasattr(custom_admin, 'UserAdmin'):
+ UserAdmin = custom_admin.UserAdmin
+ except ImportError:
+ pass
+
+ # user form classes
+ try:
+ custom_forms = importlib.import_module(app.__name__[:-6] + "forms")
+
+ if hasattr(custom_forms, 'UserCreationForm'):
+ UserCreationForm = custom_forms.UserCreationForm
+
+ if hasattr(custom_forms, 'UserChangeForm'):
+ UserChangeForm = custom_forms.UserChangeForm
+ except ImportError:
+ pass
@@ -11,15 +11,15 @@ def handle_noargs(self, **options):
self.publish_pages()
def publish_pages(self):
- from django.contrib.auth.models import User
+ from cms.compat import get_user_model
from cms.models import Page
from cms.utils.permissions import set_current_user
# thread locals middleware needs to know, who are we - login as a first
# super user
try:
- user = User.objects.filter(is_active=True, is_staff=True, is_superuser=True)[0]
+ user = get_user_model().objects.filter(is_active=True, is_staff=True, is_superuser=True)[0]
except IndexError:
raise CommandError("No super user found, create one using `manage.py createsuperuser`.")
Oops, something went wrong.

0 comments on commit 168d276

Please sign in to comment.