Permalink
Browse files

Created groups app with group list, profile, and edit views.

[bug 661853, bug 661847]
  • Loading branch information...
rlr committed Jun 10, 2011
1 parent 666ccc5 commit 786a896a26eb0917593237c017487609ddb920c5
View
No changes.
View
@@ -0,0 +1,9 @@
+from django.contrib import admin
+
+from groups.models import GroupProfile
+
+
+class GroupProfileAdmin(admin.ModelAdmin):
+ raw_id_fields = ['leaders']
+
+admin.site.register(GroupProfile, GroupProfileAdmin)
View
@@ -0,0 +1,11 @@
+from django import forms
+
+from groups.models import GroupProfile
+
+
+class GroupProfileForm(forms.ModelForm):
+ """The form for editing the group's profile."""
+
+ class Meta(object):
+ model = GroupProfile
+ fields = ['information']
View
@@ -0,0 +1,12 @@
+from django.conf import settings
+
+from jingo import register
+
+
+@register.function
+def group_avatar(group_profile):
+ """Return a URL to the group's avatar."""
+ if group_profile.avatar:
+ return group_profile.avatar.url
+ else:
+ return settings.DEFAULT_AVATAR
View
@@ -0,0 +1,41 @@
+from django.conf import settings
+from django.contrib.auth.models import Group, User
+from django.db import models
+from django.template.defaultfilters import slugify
+
+from tower import ugettext_lazy as _lazy
+
+from sumo.models import ModelBase
+from sumo.urlresolvers import reverse
+from wiki.parser import wiki_to_html
+
+
+class GroupProfile(ModelBase):
+ """Profile model for groups."""
+ slug = models.SlugField(unique=True, editable=False, blank=False,
+ null=False, max_length=80)
+ group = models.ForeignKey(Group, related_name='profile')
+ leaders = models.ManyToManyField(User)
+ information = models.TextField(help_text=u'Use Wiki Syntax')
+ information_html = models.TextField(editable=False)
+ avatar = models.ImageField(upload_to=settings.GROUP_AVATAR_PATH, null=True,
+ blank=True, verbose_name=_lazy(u'Avatar'),
+ max_length=settings.MAX_FILEPATH_LENGTH)
+
+ class Meta:
+ ordering = ['slug']
+
+ def __unicode__(self):
+ return unicode(self.group)
+
+ def get_absolute_url(self):
+ return reverse('groups.profile', args=[self.slug])
+
+ def save(self, *args, **kwargs):
+ """Set slug on first save and parse information to html."""
+ if not self.slug:
+ self.slug = slugify(self.group.name)
+
+ self.information_html = wiki_to_html(self.information)
+
+ super(GroupProfile, self).save(*args, **kwargs)
@@ -0,0 +1,4 @@
+{# vim: set ts=2 et sts=2 sw=2: #}
+{% extends "base.html" %}
+{% set styles = ('groups',) %}
+{% set scripts = ('groups',) %}
@@ -0,0 +1,18 @@
+{# vim: set ts=2 et sts=2 sw=2: #}
+{% extends "groups/base.html" %}
+{% from "includes/common_macros.html" import content_editor %}
+{% set title = _('Edit | {group} | Groups')|f(group=profile.group.name) %}
+
+{% block content %}
+ <article id="group-profile" class="main">
+ <h1>{{ _('Edit {group} profile information')|f(group=profile.group.name) }}</h1>
+
+ <form action="" method="post">
+ {{ csrf() }}
+ {{ content_editor(form.information) }}
+ <input type="submit" name="save" value="{{ _('Save') }}" />
+ </form>
+
+ {# TODO: Preview <div id="doc-content"></div> #}
+ </article>
+{% endblock %}
@@ -0,0 +1,17 @@
+{# vim: set ts=2 et sts=2 sw=2: #}
+{% extends "groups/base.html" %}
+{% set title = _('Groups') %}
+
+{% block content %}
+ <article id="group-list" class="main">
+ {% if user.is_staff and user.has_perm('groups.add_groupprofile') %}
+ <a class="add" href="{{ url('admin:groups_groupprofile_add') }}">{{ _('Add group profile') }}</a>
+ {% endif %}
+ <h1>{{ title }}</h1>
+ <ul>
+ {% for profile in groups %}
+ <li><a href="{{ profile.get_absolute_url() }}">{{ profile.group.name }}</a></li>
+ {% endfor %}
+ </ul>
+ </article>
+{% endblock %}
@@ -0,0 +1,56 @@
+{# vim: set ts=2 et sts=2 sw=2: #}
+{% extends "groups/base.html" %}
+{% set title = _('{group} | Groups')|f(group=profile.group.name) %}
+
+{% block content %}
+ <article id="group-profile" class="main">
+ <section id="avatar-area">
+ <img src="{{ group_avatar(profile) }}" alt="" />
+ {# TODO: leaders and admins can change avatar #}
+ </section>
+ <section id="main-area">
+ {% if user.is_staff and user.has_perm('groups.change_groupprofile') %}
+ <a class="edit" href="{{ url('admin:groups_groupprofile_change', profile.id) }}">{{ _('Edit in admin') }}</a>
+ {% endif %}
+ <h1>{{ profile.group.name }}</h1>
+
+ {% if user.has_perm('groups.change_groupprofile') or user in leaders %}
+ <a class="edit" href="{{ url('groups.edit', profile.slug) }}">{{ _('Edit group profile') }}</a>
+ {% endif %}
+ <div id="doc-content">
+ {{ profile.information_html|safe }}
+ </div>
+
+ <h2>{{ _('Group Leaders') }}</h2>
+ <ul class="users leaders">
+ {% for user in leaders %}
+ {{ user_row(user) }}
+ {% endfor %}
+ </ul>
+ {# TODO: admins can add/remove leaders #}
+
+ <h2>{{ _('Group Members') }}</h2>
+ <ul class="users members">
+ {% for user in members %}
+ {{ user_row(user) }}
+ {% endfor %}
+ </ul>
+ {# TODO: leaders and admins can add/remove members #}
+ </section>
+ </article>
+{% endblock %}
+
+{% macro user_row(user) -%}
+ <li>
+ <div class="avatar">
+ <a href="{{ profile_url(user) }}">
+ <img src="{{ profile_avatar(user) }}" alt="" />
+ </a>
+ </div>
+ <div class="info">
+ <a href="{{ profile_url(user) }}">
+ {{ display_name(user) }}
+ </a>
+ </div>
+ </li>
+{%- endmacro %}
@@ -0,0 +1,7 @@
+from groups.models import GroupProfile
+from sumo.tests import with_save
+
+
+@with_save
+def group_profile(**kwargs):
+ return GroupProfile(**kwargs)
@@ -0,0 +1,47 @@
+from nose.tools import eq_
+
+from groups.models import GroupProfile
+from groups.tests import group_profile
+from sumo.tests import TestCase
+from sumo.urlresolvers import reverse
+from users.tests import user, group, add_permission
+
+
+class EditGroupProfileTests(TestCase):
+ def setUp(self):
+ super(EditGroupProfileTests, self).setUp()
+ self.user = user(save=True)
+ self.group_profile = group_profile(group=group(save=True), save=True)
+ self.client.login(username=self.user.username, password='testpass')
+
+ def _verify_get_and_post(self):
+ slug = self.group_profile.slug
+ # Verify GET
+ r = self.client.get(reverse('groups.edit', args=[slug]), follow=True)
+ eq_(r.status_code, 200)
+ # Verify POST
+ r = self.client.post(reverse('groups.edit', locale='en-US',
+ args=[slug]),
+ {'information': '=new info='})
+ eq_(r.status_code, 302)
+ gp = GroupProfile.uncached.get(slug=slug)
+ eq_(gp.information, '=new info=')
+
+ def test_edit_with_perm(self):
+ add_permission(self.user, GroupProfile, 'change_groupprofile')
+ self._verify_get_and_post()
+
+ def test_edit_as_leader(self):
+ self.group_profile.leaders.add(self.user)
+ self._verify_get_and_post()
+
+ def test_edit_without_perm(self):
+ slug = self.group_profile.slug
+ # Try GET
+ r = self.client.get(reverse('groups.edit', args=[slug]), follow=True)
+ eq_(r.status_code, 403)
+ # Try POST
+ r = self.client.post(reverse('groups.edit', locale='en-US',
+ args=[slug]),
+ {'information': '=new info='})
+ eq_(r.status_code, 403)
View
@@ -0,0 +1,7 @@
+from django.conf.urls.defaults import patterns, url
+
+urlpatterns = patterns('groups.views',
+ url(r'^$', 'list', name='groups.list'),
+ url(r'^/(?P<group_slug>[^/]+)$', 'profile', name='groups.profile'),
+ url(r'^/(?P<group_slug>[^/]+)/edit$', 'edit', name='groups.edit'),
+)
View
@@ -0,0 +1,46 @@
+from django.contrib import messages
+from django.core.exceptions import PermissionDenied
+from django.http import HttpResponseRedirect
+from django.shortcuts import get_object_or_404
+from django.views.decorators.http import require_http_methods
+
+import jingo
+from tower import ugettext as _
+
+from access.decorators import login_required
+from groups.forms import GroupProfileForm
+from groups.models import GroupProfile
+
+
+def list(request):
+ groups = GroupProfile.objects.select_related('group').all()
+ return jingo.render(request, 'groups/list.html', {'groups': groups})
+
+
+def profile(request, group_slug):
+ prof = get_object_or_404(GroupProfile, slug=group_slug)
+ leaders = prof.leaders.all()
+ members = prof.group.user_set.all()
+ return jingo.render(request, 'groups/profile.html',
+ {'profile': prof, 'leaders': leaders,
+ 'members': members})
+
+
+@login_required
+@require_http_methods(['GET', 'POST'])
+def edit(request, group_slug):
+ prof = get_object_or_404(GroupProfile, slug=group_slug)
+
+ if not (request.user.has_perm('groups.change_groupprofile') or
+ request.user in prof.leaders.all()):
+ raise PermissionDenied
+
+ form = GroupProfileForm(request.POST or None, instance=prof)
+ if request.method == 'POST' and form.is_valid():
+ form.save()
+ messages.add_message(request, messages.SUCCESS,
+ _('Group information updated successfully!'))
+ return HttpResponseRedirect(prof.get_absolute_url())
+
+ return jingo.render(request, 'groups/edit.html',
+ {'form': form, 'profile': prof})
View
@@ -0,0 +1,86 @@
+/*
+ * Groups app CSS
+ */
+
+article.main ul {
+ list-style-position: inside;
+ margin: 0 0 15px;
+}
+
+article.main h2 {
+ font-size: 18px;
+ margin: 20px 0 0;
+}
+
+/* list page */
+#group-list a.add {
+ float: right;
+ padding: 0 0 0 13px;
+ position: relative;
+}
+
+#group-list a.add:before {
+ content: '+';
+ font-family: Arial;
+ font-weight: bold;
+ font-size: 16px;
+ left: 0;
+ position: absolute;
+ top: -2px;
+}
+
+/* profile page */
+#main-area {
+ margin: 0 0 0 65px;
+}
+
+#avatar-area {
+ float: left;
+ margin-left: 0;
+ text-align: center;
+ width: 48px;
+}
+
+#group-profile a.edit {
+ background: transparent url(../img/icons.actions.png) no-repeat left -25px;
+ float: right;
+ padding: 0 0 0 18px;
+}
+
+ul.users {
+ list-style: none;
+}
+
+ul.users li {
+ padding: 5px 0;
+ vertical-align: top;
+}
+
+ul.users div.avatar {
+ display: inline-block;
+ margin: 0 10px 0 0;
+ height: 48px;
+ width: 48px;
+}
+
+ul.users div.avatar img {
+ height: 48px;
+ width: 48px;
+}
+
+ul.users div.info {
+ display: inline-block;
+ min-height: 45px;
+ padding: 3px 0 0;
+ vertical-align: top;
+}
+
+/* edit page */
+div.editor {
+ margin: 0 0 10px;
+ padding: 0;
+}
+
+#id_information {
+ height: 350px;
+}
View
@@ -0,0 +1,17 @@
+/*
+ * JS for Groups app
+ */
+
+(function($) {
+
+"use strict";
+
+function init() {
+ // Marky for information edit:
+ var buttons = Marky.allButtonsExceptShowfor();
+ Marky.createCustomToolbar('.editor-tools', '#id_information', buttons);
+}
+
+$(document).ready(init);
+
+}(jQuery));
Oops, something went wrong.

0 comments on commit 786a896

Please sign in to comment.