From 76b66bf959ae6907852260cf071ee0edff38cb18 Mon Sep 17 00:00:00 2001 From: Sanj Date: Sun, 6 Mar 2011 18:21:28 +0530 Subject: [PATCH] added user --- itf/user/__init__.py | 0 itf/user/models.py | 110 +++++ itf/user/templates/contact_email.txt | 4 + itf/user/templates/password_reset_email.txt | 7 + itf/user/tests.py | 23 + itf/user/urls.py | 13 + itf/user/views.py | 478 ++++++++++++++++++++ 7 files changed, 635 insertions(+) create mode 100644 itf/user/__init__.py create mode 100644 itf/user/models.py create mode 100644 itf/user/templates/contact_email.txt create mode 100644 itf/user/templates/password_reset_email.txt create mode 100644 itf/user/tests.py create mode 100644 itf/user/urls.py create mode 100644 itf/user/views.py diff --git a/itf/user/__init__.py b/itf/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/itf/user/models.py b/itf/user/models.py new file mode 100644 index 0000000..899f182 --- /dev/null +++ b/itf/user/models.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 +from datetime import datetime + +from django.contrib.auth.models import User +from django.db import models +from django.db.models import Max + +from ox.django.fields import DictField + +#from app.models import site_config +site_config = { + 'user': { + 'ui': {} + } +} +# from itemlist.models import List, Position + + +class UserProfile(models.Model): + reset_token = models.TextField(blank=True, null=True) + user = models.ForeignKey(User, unique=True) +# validate_token = models.TextField(blank=True, null=True) +# is_validated = models.BooleanField(default=False) + files_updated = models.DateTimeField(default=datetime.now) + newsletter = models.BooleanField(default=True) + ui = DictField(default={}) + preferences = DictField(default={}) + + def get_preferences(self): + prefs = self.preferences + prefs['email'] = self.user.email + return prefs + + def get_ui(self): + ui = {} + config = site_config() + ui.update(config['user']['ui']) + def updateUI(ui, new): + ''' + only update set keys in dicts + ''' + for key in new: + if isinstance(new[key], dict) and key in ui: + ui[key] = updateUI(ui[key], new[key]) + else: + ui[key] = new[key] + return ui + ui = updateUI(ui, self.ui) + if not 'lists' in ui: + ui['lists'] = {} + ui['lists'][''] = config['uiDefaults']['list'] + + def add(lists, section): + ids = [] + for l in lists: + qs = Position.objects.filter(section=section) + if section == 'featured': + try: + pos = Position.objects.get(list=l, section=section) + created = False + except Position.DoesNotExist: + pos = Position(list=l, section=section, user=self.user) + pos.save() + created = True + else: + pos, created = Position.objects.get_or_create(list=l, user=self.user, section=section) + qs = qs.filter(user=self.user) + if created: + pos.position = qs.aggregate(Max('position'))['position__max'] + 1 + pos.save() + id = l.get_id() + ''' + if id not in ui['lists']: + ui['lists'][id] = {} + ui['lists'][id].update(ui['lists']['']) + ''' + ids.append(id) + return ids + + ids = [''] + ids += add(self.user.lists.exclude(status="featured"), 'personal') + ids += add(self.user.subscribed_lists.filter(status='public'), 'public') + ids += add(List.objects.filter(status='featured'), 'featured') + for i in ui['lists'].keys(): + if i not in ids: + del ui['lists'][i] + return ui + +def user_post_save(sender, instance, **kwargs): + profile, new = UserProfile.objects.get_or_create(user=instance) + +models.signals.post_save.connect(user_post_save, sender=User) + + +def get_user_json(user): +# profile = user.get_profile() + result = {} + for key in ('username', ): + result[key] = getattr(user, key) + if user.is_superuser: + result['level'] = 'admin' + elif user.is_staff: + result['level'] = 'staff' + else: + result['level'] = 'member' + result['groups'] = [g.name for g in user.groups.all()] +# result['preferences'] = profile.get_preferences() +# result['ui'] = profile.get_ui() + return result diff --git a/itf/user/templates/contact_email.txt b/itf/user/templates/contact_email.txt new file mode 100644 index 0000000..43243d0 --- /dev/null +++ b/itf/user/templates/contact_email.txt @@ -0,0 +1,4 @@ +Hi {{sitename}} admin, + +someone sent you a message: +{{message}} diff --git a/itf/user/templates/password_reset_email.txt b/itf/user/templates/password_reset_email.txt new file mode 100644 index 0000000..84d11c6 --- /dev/null +++ b/itf/user/templates/password_reset_email.txt @@ -0,0 +1,7 @@ +To reset your password, please use the following token: + +{{token}} + +If you do not want to reset your password, no further action is required. + +{{sitename}} - {{url}} diff --git a/itf/user/tests.py b/itf/user/tests.py new file mode 100644 index 0000000..2247054 --- /dev/null +++ b/itf/user/tests.py @@ -0,0 +1,23 @@ +""" +This file demonstrates two different styles of tests (one doctest and one +unittest). These will both pass when you run "manage.py test". + +Replace these with more appropriate tests for your application. +""" + +from django.test import TestCase + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.failUnlessEqual(1 + 1, 2) + +__test__ = {"doctest": """ +Another way to test that 1 + 1 is equal to 2. + +>>> 1 + 1 == 2 +True +"""} + diff --git a/itf/user/urls.py b/itf/user/urls.py new file mode 100644 index 0000000..5866193 --- /dev/null +++ b/itf/user/urls.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 + +from django.conf.urls.defaults import * + + +urlpatterns = patterns("user.views", + (r'^preferences', 'preferences'), + (r'^login', 'login'), + (r'^logout', 'logout'), + (r'^register', 'register'), +) + diff --git a/itf/user/views.py b/itf/user/views.py new file mode 100644 index 0000000..5eb8430 --- /dev/null +++ b/itf/user/views.py @@ -0,0 +1,478 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 +import random +random.seed() + +from django import forms +from django.contrib.auth.models import User +from django.contrib.auth import authenticate, login, logout +from django.template import RequestContext, loader +from django.utils import simplejson as json +from django.conf import settings +from django.core.mail import send_mail, BadHeaderError + +from ox.django.shortcuts import render_to_json_response, json_response +from ox.django.decorators import login_required_json +import ox + +import models + +from api.actions import actions +from app.models import site_config +from django.db.models import Q + +# from item.models import Access, Item + + +class LoginForm(forms.Form): + usernameOrEmail = forms.TextInput() + password = forms.TextInput() + + +def signin(request): + ''' + param data { + username: 'username', + password: 'password' + } + + return { + status: {'code': 200, 'text': 'ok'} + data: { + errors: { + username: 'Unknown Username', + password: 'Incorrect Password' + } + user: { + ... + } + } + } + ''' + data = json.loads(request.POST['data']) + form = LoginForm(data, request.FILES) + if form.is_valid(): + typ = data['usernameOrEmail'][0]['id'] + val = data['usernameOrEmail'][1] + qstring = typ + "__iexact" + q = Q(**{qstring:val}) + if models.User.objects.filter(q).count == 0: + response = json_response({ + 'errors': { + 'username': 'Unknown Username or E-Mail address' + } + }) + else: + if typ == 'email': + uname = models.User.objects.get(email=val).username + else: + uname = val + + user = authenticate(username=uname, password=data['password']) + if user is not None: + if user.is_active: + login(request, user) + user_json = models.get_user_json(user) + response = json_response({ + 'user': user_json + }) + else: + response = json_response({ + 'errors': { + 'username': 'User Disabled' + } + }) + else: + response = json_response({ + 'errors': { + 'password': 'Incorrect Password' + } + }) + else: + response = json_response(status=400, text='invalid data') + return render_to_json_response(response) +actions.register(signin, "login", cache=False) + + +def signout(request): + ''' + param data { + } + + return { + status: {'code': int, 'text': string} + data: { + user: { + default user + } + } + } + ''' + response = json_response(text='ok') + if request.user.is_authenticated(): + response = json_response(text='logged out') + logout(request) + +# response['data']['user'] = site_config()['user'] + response['data']['user'] = {} + return render_to_json_response(response) +actions.register(signout, "logout", cache=False) + + +class RegisterForm(forms.Form): + username = forms.TextInput() + password = forms.TextInput() + email = forms.TextInput() + + +def register(request): + ''' + param data { + username: 'username', + password: 'password', + email: 'emailaddress' + } + + return { + status: {'code': int, 'text': string} + data: { + errors: { + username: 'Unknown Username', + password: 'Incorrect Password' + } + user: { + ... + } + } + } + ''' + data = json.loads(request.POST['data']) + form = RegisterForm(data, request.FILES) + if form.is_valid(): + if models.User.objects.filter(username=form.data['username']).count() > 0: + response = json_response({ + 'errors': { + 'username': 'Username already exists' + } + }) + elif models.User.objects.filter(email=form.data['email']).count() > 0: + response = json_response({ + 'errors': { + 'email': 'Email address already exits' + } + }) + elif not form.data['password']: + response = json_response({ + 'errors': { + 'password': 'Password can not be empty' + } + }) + else: + first_user = models.User.objects.count() == 0 + user = models.User(username=form.data['username'], email=form.data['email']) + user.set_password(form.data['password']) + #make first user admin + user.is_superuser = first_user + user.is_staff = first_user + user.save() + #create default user lists: + ''' + for l in settings.DEFAULT_BOXES: + list = models.List(name=l['name'], user=user) + for key in ('query', 'public', 'featured'): + if key in l: + setattr(list, key, l[key]) + list.save() + ''' + user = authenticate(username=form.data['username'], + password=form.data['password']) + login(request, user) + user_json = models.get_user_json(user) + response = json_response({ + 'user': user_json + }, text='account created') + else: + response = json_response(status=400, text='invalid data') + return render_to_json_response(response) +actions.register(register, cache=False) + + +def resetPassword(request): + ''' + param data { + token: reset token + password: new password + } + + return { + status: {'code': int, 'text': string} + data: { + errors: { + token: 'Invalid token' + } + user { + } + } + } + ''' + data = json.loads(request.POST['data']) + if 'token' in data and 'password' in data: + if not data['password']: + response = json_response({ + 'errors': { + 'password': 'Password can not be empty' + } + }) + else: + qs = models.UserProfile.objects.filter(reset_token=data['token']) + if qs.count() == 1: + user = qs[0].user + user.set_password(data['password']) + user.save() + user_profile = user.get_profile() + user_profile.reset_token = None + user_profile.save() + user = authenticate(username=user.username, password=data['password']) + login(request, user) + + user_json = models.get_user_json(user) + response = json_response({ + 'user': user_json + }, text='password reset') + else: + response = json_response({ + 'errors': { + 'token': 'Invalid token' + } + }) + + else: + response = json_response(status=400, text='invalid data') + return render_to_json_response(response) +actions.register(resetPassword, cache=False) + + +def requestToken(request): + ''' + param data { + username: username, + email: email + } + + return { + status: {'code': int, 'text': string} + data: { + errors: { + username: 'Unknown Username' + email: 'Unknown Email' + } + username: user + } + } + ''' + data = json.loads(request.POST['data']) + user = None + if 'username' in data: + try: + user = models.User.objects.get(username=data['username']) + except models.User.DoesNotExist: + user = None + elif 'email' in data: + try: + user = models.User.objects.get(email=data['email']) + except models.User.DoesNotExist: + user = None + if user: + while True: + token = ox.to32(random.randint(32768, 1048575)) + if models.UserProfile.objects.filter(reset_token=token).count() == 0: + break + user_profile = user.get_profile() + user_profile.reset_token = token + user_profile.save() + + template = loader.get_template('password_reset_email.txt') + context = RequestContext(request, { + 'url': request.build_absolute_uri("/"), + 'token': token, + 'sitename': settings.SITENAME, + }) + message = template.render(context) + subject = '%s - Reset Password' % settings.SITENAME + user.email_user(subject, message) + response = json_response({ + 'username': user.username + }, text='password reset email sent') + else: + response = json_response({ + 'errors': { + } + }) + if 'username' in data: + response['data']['errors']['username'] = 'Unknown Username' + elif 'email' in data: + response['data']['errors']['email'] = 'Unknown Email' + else: + response = json_response(status=400, text='invalid data') + return render_to_json_response(response) +actions.register(requestToken, cache=False) + + +def findUser(request): + ''' + param data { + key: "username", + value: "foo", operator: "=" + } + + return { + 'status': {'code': int, 'text': string} + 'data': { + users = ['user1', 'user2'] + } + } + ''' + #FIXME: support other operators and keys + data = json.loads(request.POST['data']) + response = json_response(status=200, text='ok') + if data['key'] == 'email': + response['data']['users'] = [u.username for u in User.objects.filter(email__iexact=data['value'])] + else: + response['data']['users'] = [u.username for u in User.objects.filter(username__iexact=data['value'])] + return render_to_json_response(response) +actions.register(findUser) + + +class ContactForm(forms.Form): + email = forms.EmailField() + subject = forms.TextInput() + message = forms.TextInput() + + +def contact(request): + ''' + param data { + 'email': string, + 'message': string + } + + return { + 'status': {'code': int, 'text': string} + } + ''' + data = json.loads(request.POST['data']) + form = ContactForm(data, request.FILES) + if form.is_valid(): + email = data['email'] + template = loader.get_template('contact_email.txt') + context = RequestContext(request, { + 'sitename': settings.SITENAME, + 'email': email, + 'message': data['message'], + }) + message = template.render(context) + subject = '%s contact: %s' % (settings.SITENAME, data['subject']) + response = json_response(text='message sent') + try: + send_mail(subject, message, email, [settings.DEFAULT_FROM_EMAIL, ]) + except BadHeaderError: + response = json_response(status=400, text='invalid data') + else: + response = json_response(status=400, text='invalid data') + return render_to_json_response(response) +actions.register(contact, cache=False) + + +def getPositionById(list, key): + for i in range(0, len(list)): + if list[i]['id'] == key: + return i + return -1 + + +@login_required_json +def setPreferences(request): + ''' + param data { + key.subkey: value + } + return + ''' + data = json.loads(request.POST['data']) + keys = data.keys()[0].split('.') + value = data.values()[0] + profile = request.user.get_profile() + p = profile.preferences + while len(keys)>1: + key = keys.pop(0) + if isinstance(p, list): + p = p[getPositionById(p, key)] + else: + p = p[key] + p[keys[0]] = value + profile.save() + response = json_response() + return render_to_json_response(response) +actions.register(setPreferences, cache=False) + + +@login_required_json +def resetUI(request): + ''' + reset user ui settings to defaults + param data { + } + + return { + 'status': {'code': int, 'text': string} + } + ''' + profile = request.user.get_profile() + profile.ui = {} + profile.save() + response = json_response() + return render_to_json_response(response) +actions.register(resetUI, cache=False) + +def setUI(request): + ''' + param data { + key.subkey: value + } + you can set nested keys + api.setUI({"lists|my|ListView": "icons"}) + + return { + 'status': {'code': int, 'text': string} + } + ''' + data = json.loads(request.POST['data']) + if request.user.is_authenticated(): + for key in data: + keys = key.split('|') + value = data[key] + profile = request.user.get_profile() + p = profile.ui + while len(keys)>1: + key = keys.pop(0) + if isinstance(p, list): + p = p[getPositionById(p, key)] + else: + if key not in p: + p[key] = {} + p = p[key] + p[keys[0]] = value + profile.save() + + if data.get('item', False): + item = Item.objects.get(itemId=data['item']) + if request.user.is_authenticated(): + access, created = Access.objects.get_or_create(item=item, user=request.user) + else: + access, created = Access.objects.get_or_create(item=item, user=None) + access.save() + + response = json_response() + return render_to_json_response(response) +actions.register(setUI, cache=False) +