From d8516a20c7cda5f7f004a2aaa91cca22854272e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 1 Oct 2013 13:17:02 +0200 Subject: [PATCH] Refactor: greenmine.base --- greenmine/base/api.py | 156 +----------------- greenmine/base/models.py | 48 ------ greenmine/base/routers.py | 11 +- greenmine/base/searches/__init__.py | 3 + greenmine/base/searches/api.py | 41 +++++ greenmine/base/searches/models.py | 3 + greenmine/base/searches/serializers.py | 23 +++ greenmine/base/tests.py | 121 -------------- greenmine/base/urls.py | 13 -- greenmine/base/users/__init__.py | 1 + greenmine/base/{ => users}/admin.py | 10 +- greenmine/base/users/api.py | 134 +++++++++++++++ greenmine/base/{ => users}/auth.py | 2 + .../{ => users}/fixtures/initial_role.json | 14 +- .../{ => users}/fixtures/initial_user.json | 2 +- greenmine/base/{ => users}/forms.py | 9 +- greenmine/base/users/models.py | 46 ++++++ greenmine/base/{ => users}/serializers.py | 22 +-- .../base/{utils/auth.py => users/utils.py} | 0 greenmine/documents/models.py | 3 +- greenmine/documents/urls.py | 11 -- greenmine/questions/models.py | 7 +- greenmine/questions/urls.py | 13 -- greenmine/scrum/api.py | 2 +- .../scrum/management/commands/sample_data.py | 2 +- greenmine/scrum/models.py | 36 ++-- greenmine/scrum/serializers.py | 2 +- greenmine/scrum/sigdispatch.py | 2 +- greenmine/scrum/urls.py | 57 ------- greenmine/settings/common.py | 8 +- greenmine/urls.py | 58 ++++++- greenmine/wiki/models.py | 7 +- greenmine/wiki/urls.py | 13 -- 33 files changed, 372 insertions(+), 508 deletions(-) create mode 100644 greenmine/base/searches/__init__.py create mode 100644 greenmine/base/searches/api.py create mode 100644 greenmine/base/searches/models.py create mode 100644 greenmine/base/searches/serializers.py delete mode 100644 greenmine/base/tests.py delete mode 100644 greenmine/base/urls.py create mode 100644 greenmine/base/users/__init__.py rename greenmine/base/{ => users}/admin.py (93%) create mode 100644 greenmine/base/users/api.py rename greenmine/base/{ => users}/auth.py (99%) rename greenmine/base/{ => users}/fixtures/initial_role.json (98%) rename greenmine/base/{ => users}/fixtures/initial_user.json (96%) rename greenmine/base/{ => users}/forms.py (80%) create mode 100644 greenmine/base/users/models.py rename greenmine/base/{ => users}/serializers.py (80%) rename greenmine/base/{utils/auth.py => users/utils.py} (100%) delete mode 100644 greenmine/documents/urls.py delete mode 100644 greenmine/questions/urls.py delete mode 100644 greenmine/scrum/urls.py delete mode 100644 greenmine/wiki/urls.py diff --git a/greenmine/base/api.py b/greenmine/base/api.py index 6e16a9ce..f11c0170 100644 --- a/greenmine/base/api.py +++ b/greenmine/base/api.py @@ -1,161 +1,7 @@ # -*- coding: utf-8 -*- -import uuid -from django.contrib.auth import logout, login, authenticate -from django.contrib.auth.views import login as auth_login, logout as auth_logout -from django.conf import settings -from django.db.models import Q -from django import http - -from rest_framework.decorators import action from rest_framework.response import Response -from rest_framework.permissions import IsAuthenticated, AllowAny -from rest_framework import status, generics, viewsets, views - -from haystack import query, inputs -from djmail.template_mail import MagicMailBuilder - -from greenmine.base.serializers import (LoginSerializer, UserLogged, - UserSerializer, RoleSerializer, - SearchSerializer) - -from greenmine.base.models import User, Role -from greenmine.base import exceptions as excp -from greenmine.scrum import models - - -class RolesViewSet(viewsets.ViewSet): - permission_classes = (IsAuthenticated,) - serializer_class = RoleSerializer - - def list(self, request, pk=None): - queryset = Role.objects.all() - serializer = self.serializer_class(queryset, many=True) - return Response(serializer.data) - - def retrieve(self, request, pk=None): - try: - role = Role.objects.get(pk=pk) - except Role.DoesNotExist: - raise excp.NotFound() - - serializer = self.serializer_class(role) - return Response(serializer.data) - - -class UsersViewSet(viewsets.ViewSet): - permission_classes = (IsAuthenticated,) - - def get_list_queryset(self): - own_projects = (models.Project.objects - .filter(members=self.request.user)) - - project = self.request.QUERY_PARAMS.get('project', None) - if project is not None: - own_projects = own_projects.filter(pk=project) - - queryset = (User.objects.filter(projects__in=own_projects) - .order_by('username').distinct()) - - return queryset - - def list(self, request, pk=None): - queryset = self.get_list_queryset() - serializer = UserSerializer(queryset, many=True) - return Response(serializer.data) - - def retrieve(self, request, pk=None): - return Response({}) - - @action(methods=["POST"], permission_classes=[]) - def password_recovery(self, request, pk=None): - username_or_email = request.DATA.get('username', None) - - if not username_or_email: - return Response({"detail": "Invalid username or password"}, status.HTTP_400_BAD_REQUEST) - - try: - queryset = User.objects.all() - user = queryset.get(Q(username=username_or_email) | - Q(email=username_or_email)) - except User.DoesNotExist: - return Response({"detail": "Invalid username or password"}, status.HTTP_400_BAD_REQUEST) - - user.token = str(uuid.uuid1()) - user.save(update_fields=["token"]) - - mbuilder = MagicMailBuilder() - email = mbuilder.password_recovery(user.email, {"user": user}) - - return Response({"detail": "Mail sended successful!"}) - - -class Login(viewsets.ViewSet): - permission_classes = (AllowAny,) - - def create(self, request, **kwargs): - username = request.DATA.get('username', None) - password = request.DATA.get('password', None) - - try: - user = User.objects.get(username=username) - except User.DoesNotExist: - return Response({"detail": "Invalid username or password"}, - status.HTTP_400_BAD_REQUEST) - - if not user.check_password(password): - return Response({"detail": "Invalid username or password"}, - status.HTTP_400_BAD_REQUEST) - - user = authenticate(username=username, password=password) - login(request, user) - - serializer = UserSerializer(user) - response_data = serializer.data - response_data["token"] = request.session.session_key - - return Response(response_data) - - -class Logout(viewsets.ViewSet): - permission_classes = (IsAuthenticated,) - - def list(self, request, **kwargs): - return self.logout(request) - - def create(self, request, **kwargs): - return self.logout(request) - - def logout(self, request): - logout(request) - return Response({}) - - -class Search(viewsets.ViewSet): - def list(self, request, **kwargs): - text = request.QUERY_PARAMS.get('text', "") - project_id = request.QUERY_PARAMS.get('project', None) - - try: - project = self._get_project(project_id) - except (models.Project.DoesNotExist, TypeError): - raise excp.PermissionDenied({"detail": "Wrong project id"}) - - #if not text: - # raise excp.BadRequest("text parameter must be contains text") - - queryset = query.SearchQuerySet() - queryset = queryset.filter(text=inputs.AutoQuery(text)) - queryset = queryset.filter(project_id=project_id) - - return_data = SearchSerializer(queryset) - return Response(return_data.data) - - def _get_project(self, project_id): - own_projects = (models.Project.objects - .filter(members=self.request.user)) - - return own_projects.get(pk=project_id) +from rest_framework import views class ApiRoot(views.APIView): diff --git a/greenmine/base/models.py b/greenmine/base/models.py index 521d6745..8fc527af 100644 --- a/greenmine/base/models.py +++ b/greenmine/base/models.py @@ -1,17 +1,8 @@ # -*- coding: utf-8 -*- -import uuid - from django.db.models import signals -from django.db import models -from django.db.models.signals import post_save, m2m_changed from django.utils.timezone import now from django.dispatch import receiver -from django.utils.translation import ugettext_lazy as _ -from django.contrib.auth.models import UserManager, AbstractUser, Group - -from greenmine.scrum.models import Project, UserStory, Task -from greenmine.base.notifications.models import WatcherMixin import uuid @@ -30,45 +21,6 @@ def attach_uuid(sender, instance, **kwargs): instance.uuid = unicode(uuid.uuid1()) -class User(WatcherMixin, AbstractUser): - color = models.CharField(max_length=9, null=False, blank=False, default="#669933", - verbose_name=_('color')) - description = models.TextField(null=False, blank=True, - verbose_name=_('description')) - photo = models.FileField(upload_to='files/msg', max_length=500, null=True, blank=True, - verbose_name=_('photo')) - default_language = models.CharField(max_length=20, null=False, blank=True, default='', - verbose_name=_('default language')) - default_timezone = models.CharField(max_length=20, null=False, blank=True, default='', - verbose_name=_('default timezone')) - token = models.CharField(max_length=200, null=False, blank=True, default='', - verbose_name=_('token')) - colorize_tags = models.BooleanField(null=False, blank=True, default=False, - verbose_name=_('colorize tags')) - objects = UserManager() - - class Meta: - ordering = ["username"] - - -class Role(models.Model): - name = models.CharField(max_length=200, null=False, blank=False, - verbose_name=_('name')) - slug = models.SlugField(max_length=250, unique=True, null=False, blank=True, - verbose_name=_('slug')) - permissions = models.ManyToManyField('auth.Permission', - related_name='roles', - verbose_name=_('permissions')) - - class Meta: - verbose_name = u'role' - verbose_name_plural = u'roles' - ordering = ['slug'] - - def __unicode__(self): - return self.name - - # Patch api view for correctly return 401 responses on # request is authenticated instead of 403 from .monkey import patch_api_view; patch_api_view() diff --git a/greenmine/base/routers.py b/greenmine/base/routers.py index 69e94af4..5abf8611 100644 --- a/greenmine/base/routers.py +++ b/greenmine/base/routers.py @@ -18,13 +18,4 @@ class DefaultRouter(routers.DefaultRouter): ] -class SimpleRouter(routers.SimpleRouter): - routes = [ - routers.SimpleRouter.routes[0], - actions_router, - routers.SimpleRouter.routes[2], - routers.SimpleRouter.routes[1] - ] - - -__all__ = ["DefaultRouter", "SimpleRouter"] +__all__ = ["DefaultRouter"] diff --git a/greenmine/base/searches/__init__.py b/greenmine/base/searches/__init__.py new file mode 100644 index 00000000..faaaf799 --- /dev/null +++ b/greenmine/base/searches/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + + diff --git a/greenmine/base/searches/api.py b/greenmine/base/searches/api.py new file mode 100644 index 00000000..40b63986 --- /dev/null +++ b/greenmine/base/searches/api.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +from django.db.models.loading import get_model + +from rest_framework.response import Response +from rest_framework import viewsets + +from haystack import query, inputs + +from greenmine.base import exceptions as excp + +from .serializers import SearchSerializer + + +class SearchViewSet(viewsets.ViewSet): + def list(self, request, **kwargs): + project_model = get_model("scrum", "Project") + text = request.QUERY_PARAMS.get('text', "") + project_id = request.QUERY_PARAMS.get('project', None) + + try: + project = self._get_project(project_id) + except (project_model.DoesNotExist, TypeError): + raise excp.PermissionDenied({"detail": "Wrong project id"}) + + #if not text: + # raise excp.BadRequest("text parameter must be contains text") + + queryset = query.SearchQuerySet() + queryset = queryset.filter(text=inputs.AutoQuery(text)) + queryset = queryset.filter(project_id=project_id) + + return_data = SearchSerializer(queryset) + return Response(return_data.data) + + def _get_project(self, project_id): + project_model = get_model("scrum", "Project") + own_projects = (project_model.objects + .filter(members=self.request.user)) + + return own_projects.get(pk=project_id) diff --git a/greenmine/base/searches/models.py b/greenmine/base/searches/models.py new file mode 100644 index 00000000..faaaf799 --- /dev/null +++ b/greenmine/base/searches/models.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + + diff --git a/greenmine/base/searches/serializers.py b/greenmine/base/searches/serializers.py new file mode 100644 index 00000000..4b1da067 --- /dev/null +++ b/greenmine/base/searches/serializers.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +from rest_framework import serializers + + +class SearchSerializer(serializers.Serializer): + id = serializers.CharField(max_length=255) + model_name = serializers.CharField(max_length=255) + pk = serializers.IntegerField() + score = serializers.FloatField() + stored_fields = serializers.SerializerMethodField('get_stored_fields') + + def get_stored_fields(self, obj): + return obj.get_stored_fields() + + def restore_object(self, attrs, instance=None): + """ + Given a dictionary of deserialized field values, either update + an existing model instance, or create a new model instance. + """ + if instance is not None: + return instance + return attrs diff --git a/greenmine/base/tests.py b/greenmine/base/tests.py deleted file mode 100644 index 5bd80057..00000000 --- a/greenmine/base/tests.py +++ /dev/null @@ -1,121 +0,0 @@ -# -*- coding: utf-8 -*- - -import json - -from django.test import TestCase -from django.core import mail -from django.core.urlresolvers import reverse - -from django.contrib.auth.models import User - -from greenqueue import send_task - - -class LowLevelEmailTests(TestCase): - def setUp(self): - mail.outbox = [] - - def test_send_one_mail(self): - send_task("send-mail", args=["subject", "template", ["hola@niwi.be"]]) - self.assertEqual(len(mail.outbox), 1) - - def test_send_bulk_mail(self): - send_task("send-bulk-mail", args=[[ - ('s1', 't1', ['hola@niwi.be']), - ('s2', 't2', ['hola@niwi.be']), - ]]) - - self.assertEqual(len(mail.outbox), 2) - - -class UserMailTests(TestCase): - def setUp(self): - self.user1 = User.objects.create( - username='test1', - email='test1@test.com', - is_active=True, - is_staff=True, - is_superuser=True, - ) - - self.user2 = User.objects.create( - username='test2', - email='test2@test.com', - is_active=True, - is_staff=False, - is_superuser=False, - ) - - self.user1.set_password("test") - self.user2.set_password("test") - - self.user1.save() - self.user2.save() - - mail.outbox = [] - - def test_remember_password(self): - url = reverse("remember-password") - - post_params = {'email': 'test2@test.com'} - response = self.client.post(url, post_params, follow=True) - self.assertEqual(response.status_code, 200) - self.assertEqual(len(mail.outbox), 1) - - jdata = json.loads(response.content) - self.assertIn("valid", jdata) - self.assertTrue(jdata['valid']) - - def test_remember_password_not_exists(self): - url = reverse("remember-password") - - post_params = {'email': 'test2@testa.com'} - response = self.client.post(url, post_params, follow=True) - self.assertEqual(response.status_code, 200) - self.assertEqual(len(mail.outbox), 0) - - jdata = json.loads(response.content) - self.assertIn("valid", jdata) - self.assertFalse(jdata['valid']) - - def test_send_recovery_password_by_staff(self): - url = reverse("users-recovery-password", args=[self.user2.pk]) - - ok = self.client.login(username="test1", password="test") - self.assertTrue(ok) - - # pre test - self.assertTrue(self.user2.is_active) - self.assertEqual(self.user2.token, None) - - response = self.client.get(url, follow=True) - self.assertEqual(response.status_code, 200) - - # expected redirect - self.assertEqual(response.redirect_chain, [('http://testserver/users/2/edit/', 302)]) - - # test mail sending - self.assertEqual(len(mail.outbox), 1) - self.assertEqual(mail.outbox[0].subject, "Greenmine: password recovery.") - - # test user model modification - self.user2 = User.objects.get(pk=self.user2.pk) - self.assertTrue(self.user2.is_active) - self.assertFalse(self.user2.has_usable_password()) - self.assertNotEqual(self.user2.token, None) - - url = reverse('password-recovery', args=[self.user2.token]) - - post_params = { - 'password': '123123', - 'password2': '123123', - } - response = self.client.post(url, post_params, follow=True) - - self.assertEqual(response.status_code, 200) - - # expected redirect - self.assertEqual(response.redirect_chain, [('http://testserver/login/', 302)]) - - ok = self.client.login(username="test2", password="123123") - self.assertTrue(ok) diff --git a/greenmine/base/urls.py b/greenmine/base/urls.py deleted file mode 100644 index 5f163b1d..00000000 --- a/greenmine/base/urls.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- - -from . import api, routers - - -router = routers.SimpleRouter(trailing_slash=False) -router.register(r"users", api.UsersViewSet, base_name="users") -router.register(r"roles", api.RolesViewSet, base_name="roles") -router.register(r"search", api.Search, base_name="search") -router.register(r"auth/login", api.Login, base_name="auth-login") -router.register(r"auth/logout", api.Logout, base_name="auth-logout") - -urlpatterns = router.urls diff --git a/greenmine/base/users/__init__.py b/greenmine/base/users/__init__.py new file mode 100644 index 00000000..40a96afc --- /dev/null +++ b/greenmine/base/users/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/greenmine/base/admin.py b/greenmine/base/users/admin.py similarity index 93% rename from greenmine/base/admin.py rename to greenmine/base/users/admin.py index 8fc6d2ea..b143ac1e 100644 --- a/greenmine/base/admin.py +++ b/greenmine/base/users/admin.py @@ -1,14 +1,18 @@ +# -*- coding: utf-8 -*- + from django.utils.translation import ugettext_lazy as _ from django.contrib import admin from django.contrib.auth.models import Group, Permission from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin -from greenmine.base.models import Role, User -from greenmine.base.forms import UserChangeForm, UserCreationForm +from .models import Role, User +from .forms import UserChangeForm, UserCreationForm + admin.site.unregister(Group) + class RoleAdmin(admin.ModelAdmin): list_display = ["name"] filter_horizontal = ('permissions',) @@ -22,8 +26,10 @@ class RoleAdmin(admin.ModelAdmin): return super(RoleAdmin, self).formfield_for_manytomany( db_field, request=request, **kwargs) + admin.site.register(Role, RoleAdmin) + class UserAdmin(DjangoUserAdmin): fieldsets = ( (None, {'fields': ('username', 'password')}), diff --git a/greenmine/base/users/api.py b/greenmine/base/users/api.py new file mode 100644 index 00000000..afb7ecf3 --- /dev/null +++ b/greenmine/base/users/api.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- + +from django.db.models.loading import get_model +from django.contrib.auth import logout, login, authenticate + +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework.permissions import IsAuthenticated, AllowAny +from rest_framework import status, viewsets + +from djmail.template_mail import MagicMailBuilder + +from greenmine.base import exceptions as excp + +from .serializers import ( + LoginSerializer, + UserLogged, + UserSerializer, + RoleSerializer, +) + +from .models import User, Role + +import uuid + + +class RolesViewSet(viewsets.ViewSet): + permission_classes = (IsAuthenticated,) + serializer_class = RoleSerializer + + def list(self, request, pk=None): + queryset = Role.objects.all() + serializer = self.serializer_class(queryset, many=True) + return Response(serializer.data) + + def retrieve(self, request, pk=None): + try: + role = Role.objects.get(pk=pk) + except Role.DoesNotExist: + raise excp.NotFound() + + serializer = self.serializer_class(role) + return Response(serializer.data) + + +class UsersViewSet(viewsets.ViewSet): + permission_classes = (IsAuthenticated,) + + def get_list_queryset(self): + project_model = get_model("scrum", "Project") + own_projects = (project_model.objects + .filter(members=self.request.user)) + + project = self.request.QUERY_PARAMS.get('project', None) + if project is not None: + own_projects = own_projects.filter(pk=project) + + queryset = (User.objects.filter(projects__in=own_projects) + .order_by('username').distinct()) + + return queryset + + def list(self, request, pk=None): + queryset = self.get_list_queryset() + serializer = UserSerializer(queryset, many=True) + return Response(serializer.data) + + def retrieve(self, request, pk=None): + return Response({}) + + @action(methods=["POST"], permission_classes=[]) + def password_recovery(self, request, pk=None): + username_or_email = request.DATA.get('username', None) + + if not username_or_email: + return Response({"detail": "Invalid username or password"}, + status.HTTP_400_BAD_REQUEST) + + try: + queryset = User.objects.all() + user = queryset.get(Q(username=username_or_email) | + Q(email=username_or_email)) + except User.DoesNotExist: + return Response({"detail": "Invalid username or password"}, + status.HTTP_400_BAD_REQUEST) + + user.token = str(uuid.uuid1()) + user.save(update_fields=["token"]) + + mbuilder = MagicMailBuilder() + email = mbuilder.password_recovery(user.email, {"user": user}) + + return Response({"detail": "Mail sended successful!"}) + + +class LoginViewSet(viewsets.ViewSet): + permission_classes = (AllowAny,) + + def create(self, request, **kwargs): + username = request.DATA.get('username', None) + password = request.DATA.get('password', None) + + try: + user = User.objects.get(username=username) + except User.DoesNotExist: + return Response({"detail": "Invalid username or password"}, + status.HTTP_400_BAD_REQUEST) + + if not user.check_password(password): + return Response({"detail": "Invalid username or password"}, + status.HTTP_400_BAD_REQUEST) + + user = authenticate(username=username, password=password) + login(request, user) + + serializer = UserSerializer(user) + response_data = serializer.data + response_data["token"] = request.session.session_key + + return Response(response_data) + + +class LogoutViewSet(viewsets.ViewSet): + permission_classes = (IsAuthenticated,) + + def list(self, request, **kwargs): + return self.logout(request) + + def create(self, request, **kwargs): + return self.logout(request) + + def logout(self, request): + logout(request) + return Response({}) diff --git a/greenmine/base/auth.py b/greenmine/base/users/auth.py similarity index 99% rename from greenmine/base/auth.py rename to greenmine/base/users/auth.py index 6ca71405..532934bc 100644 --- a/greenmine/base/auth.py +++ b/greenmine/base/users/auth.py @@ -22,3 +22,5 @@ class SessionAuthentication(BaseAuthentication): return (user, None) + + diff --git a/greenmine/base/fixtures/initial_role.json b/greenmine/base/users/fixtures/initial_role.json similarity index 98% rename from greenmine/base/fixtures/initial_role.json rename to greenmine/base/users/fixtures/initial_role.json index 5e639a98..4f549055 100644 --- a/greenmine/base/fixtures/initial_role.json +++ b/greenmine/base/users/fixtures/initial_role.json @@ -1,7 +1,7 @@ [ { "pk": 1, - "model": "base.role", + "model": "users.role", "fields": { "permissions": [ [ @@ -51,32 +51,32 @@ ], [ "add_role", - "base", + "users", "role" ], [ "change_role", - "base", + "users", "role" ], [ "delete_role", - "base", + "users", "role" ], [ "add_user", - "base", + "users", "user" ], [ "change_user", - "base", + "users", "user" ], [ "delete_user", - "base", + "users", "user" ], [ diff --git a/greenmine/base/fixtures/initial_user.json b/greenmine/base/users/fixtures/initial_user.json similarity index 96% rename from greenmine/base/fixtures/initial_user.json rename to greenmine/base/users/fixtures/initial_user.json index b3390b6f..44df13e2 100644 --- a/greenmine/base/fixtures/initial_user.json +++ b/greenmine/base/users/fixtures/initial_user.json @@ -1,7 +1,7 @@ [ { "pk": 1, - "model": "base.user", + "model": "users.user", "fields": { "username": "admin", "first_name": "", diff --git a/greenmine/base/forms.py b/greenmine/base/users/forms.py similarity index 80% rename from greenmine/base/forms.py rename to greenmine/base/users/forms.py index f356f9ca..ee49efbf 100644 --- a/greenmine/base/forms.py +++ b/greenmine/base/users/forms.py @@ -1,6 +1,11 @@ +# -*- coding: utf-8 -*- + from django import forms -from django.contrib.auth.forms import UserCreationForm as DjangoUserCreationForm, UserChangeForm as DjangoUserChangeForm -from greenmine.base.models import User +from django.contrib.auth.forms import ( + UserCreationForm as DjangoUserCreationForm, + UserChangeForm as DjangoUserChangeForm +) +from .models import User class UserCreationForm(DjangoUserCreationForm): diff --git a/greenmine/base/users/models.py b/greenmine/base/users/models.py new file mode 100644 index 00000000..4145c411 --- /dev/null +++ b/greenmine/base/users/models.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from django.contrib.auth.models import UserManager, AbstractUser + +from greenmine.base.notifications.models import WatcherMixin + + +class User(WatcherMixin, AbstractUser): + color = models.CharField(max_length=9, null=False, blank=False, default="#669933", + verbose_name=_('color')) + description = models.TextField(null=False, blank=True, + verbose_name=_('description')) + photo = models.FileField(upload_to='files/msg', max_length=500, null=True, blank=True, + verbose_name=_('photo')) + default_language = models.CharField(max_length=20, null=False, blank=True, default='', + verbose_name=_('default language')) + default_timezone = models.CharField(max_length=20, null=False, blank=True, default='', + verbose_name=_('default timezone')) + token = models.CharField(max_length=200, null=False, blank=True, default='', + verbose_name=_('token')) + colorize_tags = models.BooleanField(null=False, blank=True, default=False, + verbose_name=_('colorize tags')) + objects = UserManager() + + class Meta: + ordering = ["username"] + + +class Role(models.Model): + name = models.CharField(max_length=200, null=False, blank=False, + verbose_name=_('name')) + slug = models.SlugField(max_length=250, unique=True, null=False, blank=True, + verbose_name=_('slug')) + permissions = models.ManyToManyField('auth.Permission', + related_name='roles', + verbose_name=_('permissions')) + + class Meta: + verbose_name = u'role' + verbose_name_plural = u'roles' + ordering = ['slug'] + + def __unicode__(self): + return self.name diff --git a/greenmine/base/serializers.py b/greenmine/base/users/serializers.py similarity index 80% rename from greenmine/base/serializers.py rename to greenmine/base/users/serializers.py index fab7d503..2655d808 100644 --- a/greenmine/base/serializers.py +++ b/greenmine/base/users/serializers.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from rest_framework import serializers -from greenmine.base.models import User, Role +from .models import User, Role class UserLogged(object): @@ -70,23 +70,3 @@ class RoleSerializer(serializers.ModelSerializer): class Meta: model = Role fields = ('id', 'name', 'slug', 'permissions',) - - -class SearchSerializer(serializers.Serializer): - id = serializers.CharField(max_length=255) - model_name = serializers.CharField(max_length=255) - pk = serializers.IntegerField() - score = serializers.FloatField() - stored_fields = serializers.SerializerMethodField('get_stored_fields') - - def get_stored_fields(self, obj): - return obj.get_stored_fields() - - def restore_object(self, attrs, instance=None): - """ - Given a dictionary of deserialized field values, either update - an existing model instance, or create a new model instance. - """ - if instance is not None: - return instance - return attrs diff --git a/greenmine/base/utils/auth.py b/greenmine/base/users/utils.py similarity index 100% rename from greenmine/base/utils/auth.py rename to greenmine/base/users/utils.py diff --git a/greenmine/documents/models.py b/greenmine/documents/models.py index eef450c8..1e955d67 100644 --- a/greenmine/documents/models.py +++ b/greenmine/documents/models.py @@ -1,6 +1,7 @@ # -* coding: utf-8 -*- from django.db import models +from django.conf import settings from django.utils.translation import ugettext_lazy as _ from picklefield.fields import PickledObjectField @@ -21,7 +22,7 @@ class Document(models.Model): project = models.ForeignKey('scrum.Project', null=False, blank=False, related_name='documents', verbose_name=_('project')) - owner = models.ForeignKey('base.User', null=False, blank=False, + owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False, related_name='owned_documents', verbose_name=_('owner')) attached_file = models.FileField(max_length=1000, null=True, blank=True, diff --git a/greenmine/documents/urls.py b/greenmine/documents/urls.py deleted file mode 100644 index dbf39312..00000000 --- a/greenmine/documents/urls.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- - -from greenmine.base import routers - -from . import api - - -router = routers.SimpleRouter(trailing_slash=False) -router.register(r"documents", api.DocumentsViewSet, base_name="documents") - -urlpatterns = router.urls diff --git a/greenmine/questions/models.py b/greenmine/questions/models.py index 68b1c4e8..f56d24fe 100644 --- a/greenmine/questions/models.py +++ b/greenmine/questions/models.py @@ -2,6 +2,7 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ +from django.conf import settings from django.utils import timezone from django.dispatch import receiver @@ -37,7 +38,7 @@ class Question(models.Model): ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None, verbose_name=_('ref')) - owner = models.ForeignKey('base.User', null=True, blank=True, default=None, + owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None, related_name='owned_questions', verbose_name=_('owner')) status = models.ForeignKey('QuestionStatus', null=False, blank=False, @@ -60,14 +61,14 @@ class Question(models.Model): verbose_name=_('milestone')) finished_date = models.DateTimeField(null=True, blank=True, verbose_name=_('finished date')) - assigned_to = models.ForeignKey('base.User', null=True, blank=True, default=None, + assigned_to = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None, related_name='questions_assigned_to_me', verbose_name=_('assigned_to')) created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, verbose_name=_('created date')) modified_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, verbose_name=_('modified date')) - watchers = models.ManyToManyField('base.User', null=True, blank=True, + watchers = models.ManyToManyField(settings.AUTH_USER_MODEL, null=True, blank=True, related_name='watched_questions', verbose_name=_('watchers')) tags = PickledObjectField(null=False, blank=True, diff --git a/greenmine/questions/urls.py b/greenmine/questions/urls.py deleted file mode 100644 index 3f44bddd..00000000 --- a/greenmine/questions/urls.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- - -from django.conf.urls import patterns, url -from rest_framework.urlpatterns import format_suffix_patterns - -from . import api - - -urlpatterns = format_suffix_patterns(patterns('', - url(r'^questions/$', api.QuestionList.as_view(), name='question-list'), - url(r'^questions/(?P[0-9]+)/$', api.QuestionDetail.as_view(), name='question-detail'), -)) - diff --git a/greenmine/scrum/api.py b/greenmine/scrum/api.py index b757ed02..c5b6702e 100644 --- a/greenmine/scrum/api.py +++ b/greenmine/scrum/api.py @@ -5,7 +5,7 @@ from django.db.models import Q from rest_framework import mixins, viewsets from rest_framework.permissions import IsAuthenticated -from greenmine.base.models import * +from greenmine.base.users.models import * from greenmine.base.notifications.api import NotificationSenderMixin from greenmine.scrum.serializers import * diff --git a/greenmine/scrum/management/commands/sample_data.py b/greenmine/scrum/management/commands/sample_data.py index ed772b85..26aeafb6 100644 --- a/greenmine/scrum/management/commands/sample_data.py +++ b/greenmine/scrum/management/commands/sample_data.py @@ -11,7 +11,7 @@ from django.utils.timezone import now from django.contrib.webdesign import lorem_ipsum -from greenmine.base.models import User, Role +from greenmine.base.users.models import User, Role from greenmine.scrum.models import * from greenmine.questions.models import * diff --git a/greenmine/scrum/models.py b/greenmine/scrum/models.py index 2633ba50..d308a338 100644 --- a/greenmine/scrum/models.py +++ b/greenmine/scrum/models.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- from django.db import models +from django.conf import settings from django.utils import timezone from django.dispatch import receiver from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic +from django.contrib.auth import get_user_model from django.utils.translation import ugettext_lazy as _ from django.db.models.loading import get_model @@ -174,11 +176,11 @@ class Points(models.Model): class Membership(models.Model): - user = models.ForeignKey('base.User', null=False, blank=False, + user = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False, related_name="memberships") project = models.ForeignKey('Project', null=False, blank=False, related_name="memberships") - role = models.ForeignKey('base.Role', null=False, blank=False, + role = models.ForeignKey('users.Role', null=False, blank=False, related_name="memberships") class Meta: @@ -198,10 +200,10 @@ class Project(models.Model, WatchedMixin): verbose_name=_('created date')) modified_date = models.DateTimeField(auto_now=True, null=False, blank=False, verbose_name=_('modified date')) - owner = models.ForeignKey('base.User', null=False, blank=False, + owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False, related_name='owned_projects', verbose_name=_('owner')) - members = models.ManyToManyField('base.User', related_name='projects', through='Membership', + members = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='projects', through='Membership', verbose_name=_('members')) public = models.BooleanField(default=True, null=False, blank=True, verbose_name=_('public')) @@ -264,12 +266,12 @@ class Project(models.Model, WatchedMixin): @property def list_roles(self): - role_model = get_model('base', 'Role') + role_model = get_model('users', 'Role') return role_model.objects.filter(id__in=list(self.memberships.values_list('role', flat=True))) @property def list_users(self): - user_model = get_model('base', 'User') + user_model = get_user_model() return user_model.objects.filter(id__in=list(self.memberships.values_list('user', flat=True))) def update_role_points(self): @@ -298,7 +300,7 @@ class Milestone(models.Model, WatchedMixin): verbose_name=_('name')) slug = models.SlugField(max_length=250, unique=True, null=False, blank=True, verbose_name=_('slug')) - owner = models.ForeignKey('base.User', null=True, blank=True, related_name='owned_milestones', + owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, related_name='owned_milestones', verbose_name=_('owner')) project = models.ForeignKey('Project', null=False, blank=False, related_name='milestones', verbose_name=_('project')) @@ -403,7 +405,7 @@ class RolePoints(models.Model): user_story = models.ForeignKey('UserStory', null=False, blank=False, related_name='role_points', verbose_name=_('user story')) - role = models.ForeignKey('base.Role', null=False, blank=False, + role = models.ForeignKey('users.Role', null=False, blank=False, related_name='role_points', verbose_name=_('role')) points = models.ForeignKey('Points', null=False, blank=False, @@ -425,7 +427,7 @@ class UserStory(WatchedMixin, models.Model): project = models.ForeignKey('Project', null=False, blank=False, related_name='user_stories', verbose_name=_('project')) - owner = models.ForeignKey('base.User', null=True, blank=True, + owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, related_name='owned_user_stories', verbose_name=_('owner')) status = models.ForeignKey('UserStoryStatus', null=False, blank=False, @@ -447,7 +449,7 @@ class UserStory(WatchedMixin, models.Model): verbose_name=_('subject')) description = models.TextField(null=False, blank=True, verbose_name=_('description')) - watchers = models.ManyToManyField('base.User', null=True, blank=True, + watchers = models.ManyToManyField(settings.AUTH_USER_MODEL, null=True, blank=True, related_name='watched_us', verbose_name=_('watchers')) client_requirement = models.BooleanField(default=False, null=False, blank=True, @@ -504,7 +506,7 @@ class UserStory(WatchedMixin, models.Model): class Attachment(models.Model): - owner = models.ForeignKey('base.User', null=False, blank=False, + owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False, related_name='change_attachments', verbose_name=_('owner')) project = models.ForeignKey('Project', null=False, blank=False, @@ -541,7 +543,7 @@ class Task(models.Model, WatchedMixin): verbose_name=_('user story')) ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None, verbose_name=_('ref')) - owner = models.ForeignKey('base.User', null=True, blank=True, default=None, + owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None, related_name='owned_tasks', verbose_name=_('owner')) status = models.ForeignKey('TaskStatus', null=False, blank=False, @@ -563,10 +565,10 @@ class Task(models.Model, WatchedMixin): verbose_name=_('subject')) description = models.TextField(null=False, blank=True, verbose_name=_('description')) - assigned_to = models.ForeignKey('base.User', blank=True, null=True, default=None, + assigned_to = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, default=None, related_name='user_storys_assigned_to_me', verbose_name=_('assigned to')) - watchers = models.ManyToManyField('base.User', null=True, blank=True, + watchers = models.ManyToManyField(settings.AUTH_USER_MODEL, null=True, blank=True, related_name='watched_tasks', verbose_name=_('watchers')) tags = PickledObjectField(null=False, blank=True, @@ -624,7 +626,7 @@ class Issue(models.Model, WatchedMixin): verbose_name=_('uuid')) ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None, verbose_name=_('ref')) - owner = models.ForeignKey('base.User', null=True, blank=True, default=None, + owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None, related_name='owned_issues', verbose_name=_('owner')) status = models.ForeignKey('IssueStatus', null=False, blank=False, @@ -655,10 +657,10 @@ class Issue(models.Model, WatchedMixin): verbose_name=_('subject')) description = models.TextField(null=False, blank=True, verbose_name=_('description')) - assigned_to = models.ForeignKey('base.User', blank=True, null=True, default=None, + assigned_to = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, default=None, related_name='issues_assigned_to_me', verbose_name=_('assigned to')) - watchers = models.ManyToManyField('base.User', null=True, blank=True, + watchers = models.ManyToManyField(settings.AUTH_USER_MODEL, null=True, blank=True, related_name='watched_issues', verbose_name=_('watchers')) tags = PickledObjectField(null=False, blank=True, diff --git a/greenmine/scrum/serializers.py b/greenmine/scrum/serializers.py index 98f1829f..b4212839 100644 --- a/greenmine/scrum/serializers.py +++ b/greenmine/scrum/serializers.py @@ -2,7 +2,7 @@ from rest_framework import serializers -from greenmine.base.models import * +from greenmine.base.users.models import * from greenmine.scrum.models import * from picklefield.fields import dbsafe_encode, dbsafe_decode diff --git a/greenmine/scrum/sigdispatch.py b/greenmine/scrum/sigdispatch.py index ffd60945..11443b99 100644 --- a/greenmine/scrum/sigdispatch.py +++ b/greenmine/scrum/sigdispatch.py @@ -6,7 +6,7 @@ from django.utils.translation import ugettext from django.template.loader import render_to_string from greenmine.base import signals -from greenmine.base.utils.auth import set_token +from greenmine.base.users.utils import set_token from greenmine.base.mail.tasks import send_mail, send_bulk_mail diff --git a/greenmine/scrum/urls.py b/greenmine/scrum/urls.py deleted file mode 100644 index 6bf80715..00000000 --- a/greenmine/scrum/urls.py +++ /dev/null @@ -1,57 +0,0 @@ -# -*- coding: utf-8 -*- - -from django.conf.urls import patterns, url -from rest_framework.urlpatterns import format_suffix_patterns - -from greenmine.scrum import api -from greenmine.base import routers - - -router = routers.DefaultRouter(trailing_slash=False) -router.register(r"projects", api.ProjectViewSet, base_name="projects") -router.register(r"milestones", api.MilestoneViewSet, base_name="milestones") -router.register(r"userstories", api.UserStoryViewSet, base_name="userstories") -router.register(r"issue-attachments", api.IssuesAttachmentViewSet, base_name="issue-attachments") -router.register(r"task-attachments", api.TasksAttachmentViewSet, base_name="task-attachments") -router.register(r"tasks", api.TaskViewSet, base_name="tasks") -router.register(r"issues", api.IssueViewSet, base_name="issues") -router.register(r"severities", api.SeverityViewSet, base_name="severities") -router.register(r"issue-statuses", api.IssueStatusViewSet, base_name="issue-statuses") -router.register(r"task-statuses", api.TaskStatusViewSet, base_name="task-statuses") -router.register(r"userstory-statuses", api.UserStoryStatusViewSet, base_name="userstory-statuses") -router.register(r"priorities", api.PriorityViewSet, base_name="priorities") -router.register(r"issue-types", api.IssueTypeViewSet, base_name="issue-types") -router.register(r"points", api.PointsViewSet, base_name="points") - -urlpatterns = router.urls - -#urlpatterns = format_suffix_patterns(patterns('', -# url(r'^projects/$', api.ProjectList.as_view(), name='project-list'), -# url(r'^projects/(?P[0-9]+)/$', api.ProjectDetail.as_view(), name='project-detail'), -# url(r'^milestones/$', api.MilestoneList.as_view(), name='milestone-list'), -# url(r'^milestones/(?P[0-9]+)/$', api.MilestoneDetail.as_view(), name='milestone-detail'), -# url(r'^user-stories/$', api.UserStoryList.as_view(), name='user-story-list'), -# url(r'^user-stories/(?P[0-9]+)/$', api.UserStoryDetail.as_view(), name='user-story-detail'), -# url(r'^user-stories/points/$', api.PointsList.as_view(), name='points-list'), -# url(r'^user-stories/points/(?P[0-9]+)/$', api.PointsDetail.as_view(), name='points-detail'), -# url(r'^user-stories/statuses/$', api.UserStoryStatusList.as_view(), name='user-story-status-list'), -# url(r'^user-stories/statuses/(?P[0-9]+)/$', api.UserStoryStatusDetail.as_view(), name='user-story-status-detail'), -# url(r'^issues/$', api.IssueList.as_view(), name='issues-list'), -# url(r'^issues/(?P[0-9]+)/$', api.IssueDetail.as_view(), name='issues-detail'), -# url(r'^issues/attachments/$', api.IssuesAttachmentList.as_view(), name='issues-attachment-list'), -# url(r'^issues/attachments/(?P[0-9]+)/$', api.IssuesAttachmentDetail.as_view(), name='issues-attachment-detail'), -# url(r'^issues/statuses/$', api.IssueStatusList.as_view(), name='issues-status-list'), -# url(r'^issues/statuses/(?P[0-9]+)/$', api.IssueStatusDetail.as_view(), name='issues-status-detail'), -# url(r'^issues/types/$', api.IssueTypeList.as_view(), name='issues-type-list'), -# url(r'^issues/types/(?P[0-9]+)/$', api.IssueTypeDetail.as_view(), name='issues-type-detail'), -# url(r'^tasks/$', api.TaskList.as_view(), name='tasks-list'), -# url(r'^tasks/(?P[0-9]+)/$', api.TaskDetail.as_view(), name='tasks-detail'), -# url(r'^tasks/attachments/$', api.TasksAttachmentList.as_view(), name='tasks-attachment-list'), -# url(r'^tasks/attachments/(?P[0-9]+)/$', api.TasksAttachmentDetail.as_view(), name='tasks-attachment-detail'), -# url(r'^severities/$', api.SeverityList.as_view(), name='severity-list'), -# url(r'^severities/(?P[0-9]+)/$', api.SeverityDetail.as_view(), name='severity-detail'), -# url(r'^tasks/statuses/$', api.TaskStatusList.as_view(), name='tasks-status-list'), -# url(r'^tasks/statuses/(?P[0-9]+)/$', api.TaskStatusDetail.as_view(), name='tasks-status-detail'), -# url(r'^priorities/$', api.PriorityList.as_view(), name='priority-list'), -# url(r'^priorities/(?P[0-9]+)/$', api.PriorityDetail.as_view(), name='priority-detail'), -#)) diff --git a/greenmine/settings/common.py b/greenmine/settings/common.py index 659911ae..ef6c4add 100644 --- a/greenmine/settings/common.py +++ b/greenmine/settings/common.py @@ -191,8 +191,10 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', 'greenmine.base', - 'greenmine.base.mail', 'greenmine.base.notifications', + 'greenmine.base.users', + 'greenmine.base.mail', + 'greenmine.base.searches', 'greenmine.scrum', 'greenmine.wiki', 'greenmine.questions', @@ -258,7 +260,7 @@ LOGGING = { } -AUTH_USER_MODEL = 'base.User' +AUTH_USER_MODEL = 'users.User' FORMAT_MODULE_PATH = 'greenmine.base.formats' DATE_INPUT_FORMATS = ( '%Y-%m-%d', '%m/%d/%Y', '%d/%m/%Y', '%b %d %Y', @@ -289,7 +291,7 @@ HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor' REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'greenmine.base.auth.SessionAuthentication', + 'greenmine.base.users.auth.SessionAuthentication', ), 'FILTER_BACKEND': 'greenmine.base.filters.SimpleFilterBackend', } diff --git a/greenmine/urls.py b/greenmine/urls.py index 25f92ec9..4708d05b 100644 --- a/greenmine/urls.py +++ b/greenmine/urls.py @@ -6,13 +6,65 @@ from django.contrib import admin admin.autodiscover() +from greenmine.base import routers from greenmine.base.api import ApiRoot +from greenmine.base.users.api import ( + LoginViewSet, + LogoutViewSet, + RolesViewSet, + UsersViewSet +) +from greenmine.base.searches.api import SearchViewSet +from greenmine.scrum.api import ( + MilestoneViewSet, + PriorityViewSet, + ProjectViewSet, + SeverityViewSet, + UserStoryStatusViewSet, + UserStoryViewSet, + TaskStatusViewSet, + TaskViewSet, + TasksAttachmentViewSet, + PointsViewSet, + IssueStatusViewSet, + IssueTypeViewSet, + IssueViewSet, + IssuesAttachmentViewSet +) +router = routers.DefaultRouter(trailing_slash=False) +# greenmine.base.users +router.register(r"users", UsersViewSet, base_name="users") +router.register(r"roles", RolesViewSet, base_name="roles") +router.register(r"auth/login", LoginViewSet, base_name="auth-login") +router.register(r"auth/logout", LogoutViewSet, base_name="auth-logout") +# greenmine.base.searches +router.register(r"search", SearchViewSet, base_name="search") +# greenmine.scrum +router.register(r"projects", ProjectViewSet, base_name="projects") +router.register(r"milestones", MilestoneViewSet, base_name="milestones") +router.register(r"userstories", UserStoryViewSet, base_name="userstories") +router.register(r"issue-attachments", IssuesAttachmentViewSet, base_name="issue-attachments") +router.register(r"task-attachments", TasksAttachmentViewSet, base_name="task-attachments") +router.register(r"tasks", TaskViewSet, base_name="tasks") +router.register(r"issues", IssueViewSet, base_name="issues") +router.register(r"severities", SeverityViewSet, base_name="severities") +router.register(r"issue-statuses", IssueStatusViewSet, base_name="issue-statuses") +router.register(r"task-statuses", TaskStatusViewSet, base_name="task-statuses") +router.register(r"userstory-statuses", UserStoryStatusViewSet, base_name="userstory-statuses") +router.register(r"priorities", PriorityViewSet, base_name="priorities") +router.register(r"issue-types", IssueTypeViewSet, base_name="issue-types") +router.register(r"points", PointsViewSet, base_name="points") +#greenmine.issues +#greenmine.wiki +#greenmine.documents + urlpatterns = patterns('', - url(r'^api/v1/', include('greenmine.base.urls')), - url(r'^api/v1/', include('greenmine.scrum.urls')), - url(r'^api/v1/', include('greenmine.wiki.urls')), + url(r'^api/v1$', ApiRoot.as_view()), + url(r'^api/v1/', include(router.urls)), + # TODO: Refactor to use ViewSet + #url(r'^api/v1/', include('greenmine.wiki.urls')), # TODO: Finish the documents and questions app #url(r'^api/v1/', include('greenmine.questions.urls')), #url(r'^api/v1/', include('greenmine.documents.urls')), diff --git a/greenmine/wiki/models.py b/greenmine/wiki/models.py index e9328f1b..c70c803f 100644 --- a/greenmine/wiki/models.py +++ b/greenmine/wiki/models.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from django.db import models +from django.conf import settings from django.utils.translation import ugettext_lazy as _ @@ -12,10 +13,10 @@ class WikiPage(models.Model): verbose_name=_('slug')) content = models.TextField(null=False, blank=True, verbose_name=_('content')) - owner = models.ForeignKey('base.User', null=True, blank=True, + owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, related_name='owned_wiki_pages', verbose_name=_('owner')) - watchers = models.ManyToManyField('base.User', null=True, blank=True, + watchers = models.ManyToManyField(settings.AUTH_USER_MODEL, null=True, blank=True, related_name='watched_wiki_pages', verbose_name=_('watchers')) created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, @@ -42,7 +43,7 @@ class WikiPageAttachment(models.Model): wikipage = models.ForeignKey('WikiPage', null=False, blank=False, related_name='attachments', verbose_name=_('wiki page')) - owner = models.ForeignKey('base.User', null=False, blank=False, + owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False, related_name='owned_wiki_attachments', verbose_name=_('owner')) created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, diff --git a/greenmine/wiki/urls.py b/greenmine/wiki/urls.py deleted file mode 100644 index 8115b023..00000000 --- a/greenmine/wiki/urls.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- - -from django.conf.urls import patterns, url -from rest_framework.urlpatterns import format_suffix_patterns -from greenmine.wiki import api - -urlpatterns = format_suffix_patterns(patterns('', - url(r'^pages$', api.WikiPageList.as_view(), name='wiki-page-list'), - url(r'^pages/(?P\d+)-(?P[\w\-\d]+)$', api.WikiPageDetail.as_view(), name='wiki-page-detail'), - #url(r'^wiki_page_attachments/$', api.WikiPageAttachmentList.as_view(), name='wiki-page-attachment-list'), - #url(r'^wiki_page_attachments/(?P[0-9]+)/$', api.WikiPageAttachmentDetail.as_view(), name='wiki-page-attachment-detail'), -)) -