Permalink
Browse files

[Bug 790798] Front end for locale announcements.

Users that have permission can create locale announcements from the
locale dashboard.
  • Loading branch information...
mythmon committed Dec 28, 2012
1 parent 0e7b4cc commit f67ea61de6b028db41b0432dd221e1023d551e7c
@@ -0,0 +1,22 @@
+from datetime import date
+
+from django import forms
+
+from tower import ugettext as _
+
+
+class AnnouncementForm(forms.Form):
+ """Form for collecting information about an announcement.
+
+ This is not a ModelForm, and does not include the group or locale fields,
+ because it should only be used in a context where the group or locale is
+ implicit, and should not be user controllable. If you need a user
+ controllable locale or group, use the admin interface.
+
+ """
+ content = forms.CharField(label=_('Content'), max_length=10000,
+ widget=forms.Textarea)
+ show_after = forms.DateField(label=_('Show after'), initial=date.today,
+ input_formats=['%Y-%m-%d'])
+ show_until = forms.DateField(label=_('Show until'), required=False,
+ input_formats=['%Y-%m-%d'])
@@ -0,0 +1,98 @@
+from datetime import datetime
+
+from nose.tools import eq_
+
+from announcements.models import Announcement
+from announcements.tests import announcement
+from sumo.tests import TestCase
+from sumo.urlresolvers import reverse
+from users.tests import user, add_permission
+from wiki.tests import locale
+
+
+class TestCreateLocaleAnnouncement(TestCase):
+
+ def setUp(self):
+ self.locale = locale(save=True, locale='es')
+
+ def _create_test(self, status, count):
+ """Login, or other setup, then call this."""
+ url = reverse('announcements.create_for_locale', locale='es')
+ resp = self.client.post(url, {
+ 'content': 'Look at me!',
+ 'show_after': '2012-01-01',
+ })
+ eq_(resp.status_code, status)
+ eq_(Announcement.objects.count(), count)
+
+ def test_create(self):
+ u = user(save=True, is_superuser=1)
+ self.client.login(username=u.username, password='testpass')
+ self._create_test(200, 1)
+
+ def test_leader(self):
+ u = user(save=True)
+ self.locale.leaders.add(u)
+ self.locale.save()
+ self.client.login(username=u.username, password='testpass')
+ self._create_test(200, 1)
+
+ def test_has_permission(self):
+ u = user(save=True)
+ add_permission(u, Announcement, 'add_announcement')
+ self.client.login(username=u.username, password='testpass')
+ self._create_test(200, 1)
+
+ def test_no_perms(self):
+ u = user(save=True)
+ self.client.login(username=u.username, password='testpass')
+ self._create_test(403, 0)
+
+ def test_anon(self):
+ self._create_test(302, 0)
+
+
+class TestDeleteAnnouncement(TestCase):
+
+ def setUp(self):
+ self.locale = locale(save=True, locale='es')
+
+ self.u = user(save=True)
+
+ self.locale.leaders.add(self.u)
+ self.locale.save()
+
+ self.announcement = announcement(save=True, creator=self.u,
+ locale=self.locale, content="Look at me!",
+ show_after=datetime(2012, 01, 01, 0, 0, 0))
+
+ def _delete_test(self, id, status, count):
+ """Login, or other setup, then call this."""
+ url = reverse('announcements.delete', locale='es', args=(id,))
+ resp = self.client.post(url)
+ eq_(resp.status_code, status)
+ eq_(Announcement.objects.count(), count)
+
+ def test_delete(self):
+ u = user(save=True, is_superuser=1)
+ self.client.login(username=u.username, password='testpass')
+ self._delete_test(self.announcement.id, 204, 0)
+
+ def test_leader(self):
+ # Use the user that was created in setUp.
+ self.client.login(username=self.u.username, password='testpass')
+ self._delete_test(self.announcement.id, 204, 0)
+
+ def test_has_permission(self):
+ u = user(save=True)
+ add_permission(u, Announcement, 'add_announcement')
+ self.client.login(username=u.username, password='testpass')
+ self._delete_test(self.announcement.id, 204, 0)
+
+ def test_no_perms(self):
+ u = user(save=True)
+ self.client.login(username=u.username, password='testpass')
+ self._delete_test(self.announcement.id, 403, 1)
+
+ def test_anon(self):
+ self._delete_test(self.announcement.id, 302, 1)
@@ -0,0 +1,9 @@
+from django.conf.urls.defaults import patterns, url
+
+
+urlpatterns = patterns('announcements.views',
+ url(r'^/create/locale$', 'create_for_locale',
+ name='announcements.create_for_locale'),
+ url(r'^/(?P<announcement_id>\d+)/delete$', 'delete',
+ name='announcements.delete'),
+)
@@ -0,0 +1,55 @@
+import json
+
+from django.http import HttpResponse, HttpResponseForbidden
+from django.shortcuts import get_object_or_404
+from django.views.decorators.http import require_POST
+from access.decorators import login_required
+
+from announcements.forms import AnnouncementForm
+from announcements.models import Announcement
+from wiki.models import Locale
+
+
+@require_POST
+@login_required
+def create_for_locale(request):
+ """An ajax view to create a new announcement for the current locale."""
+ user = request.user
+ locale = Locale.objects.get(locale=request.locale)
+
+ if not user_can_announce(user, locale):
+ return HttpResponseForbidden()
+
+ form = AnnouncementForm(request.POST)
+
+ if form.is_valid():
+ a = Announcement(creator=user, locale=locale, **form.cleaned_data)
+ a.save()
+ return HttpResponse(json.dumps({'id': a.id}),
+ content_type="application/json")
+ else:
+ return HttpResponse(json.dumps(form.errors), status=400,
+ content_type="application/json")
+
+
+@require_POST
+@login_required
+def delete(request, announcement_id):
+ """An ajax view to delete an announcement."""
+ user = request.user
+ locale = Locale.objects.get(locale=request.locale)
+
+ if not user_can_announce(user, locale):
+ return HttpResponseForbidden()
+
+ a = get_object_or_404(Announcement, id=announcement_id)
+ a.delete()
+
+ return HttpResponse("", status=204)
+
+
+def user_can_announce(user, locale):
+ if user.is_anonymous():
+ return False
+ return (user.has_perm('announcements.add_announcement') or
+ user in locale.leaders.all())
@@ -18,12 +18,43 @@ <h1>{{ title }}</h1>
{% for a in announcements %}
<li>
{{ a.content|wiki_to_html }}
- <p>{{ datetimeformat(a.show_after) }}</p>
+ <p>
+ {{ datetimeformat(a.show_after, 'date') }}
+ {% if user_can_announce %}
+ <a href="{{ url('announcements.delete', a.id) }}" class="delete">
+ {{ _('Delete') }}
+ </a>
+ {% endif %}
+ </p>
</li>
{% endfor %}
</ul>
{% endif %}
+ {% if user_can_announce %}
+ <div id="create-announcement">
+ <button class="btn">
+ <img src="{{ MEDIA_URL }}img/blank.png" />
+ {{ _('Create announcement') }}
+ </button>
+ <span class="success">{{ _('Created successfully') }}</span>
+ <div class="kbox" data-target="#create-announcement > .btn" data-position="none" data-close-on-out-click="true">
+ <form action="{{ url('announcements.create_for_locale') }}" method="POST">
+ {{ csrf() }}
+ <ul>
+ {{ announce_form.as_ul() }}
+ <li>
+ <button type="submit" class="btn btn-submit" value="Submit">
+ {{ _('Create') }}
+ </button>
+ <img src="{{ MEDIA_URL }}img/wait-trans.gif" class="spinner" />
+ </i>
+ </ul>
+ </form>
+ </div>
+ </div>
+ {% endif %}
+
<div class="choice-list">
<label>{{ _('Show information for:') }}</label>
<ul>
@@ -1,19 +1,20 @@
-from datetime import timedelta
+from datetime import timedelta, datetime
import json
from django.conf import settings
from nose import SkipTest
from nose.tools import eq_
+from announcements.tests import announcement
from dashboards.cron import cache_most_unhelpful_kb_articles
from dashboards.readouts import CONTRIBUTOR_READOUTS
from sumo.tests import TestCase
from sumo.urlresolvers import reverse
from sumo.redis_utils import redis_client, RedisError
from users.tests import user, group
from wiki.models import HelpfulVote
-from wiki.tests import revision
+from wiki.tests import revision, locale
class LocalizationDashTests(TestCase):
@@ -26,6 +27,45 @@ def test_redirect_to_contributor_dash(self):
locale='en-US'))
+def LocalizationDashAnnouncementsTests(TestCase):
+
+ def setUp(self):
+ self.locale1 = locale(save=True, locale='es')
+
+ self.u1 = user(save=True)
+ self.u2 = user(save=True)
+ self.u3 = user(save=True)
+
+ self.u1.is_superuser = 1
+ self.u1.save()
+
+ self.locale1.leaders.add(self.u2)
+ self.locale1.save()
+
+ self.announcement = announcement(save=True, creator=self.u2,
+ locale=self.locale1, content="Look at me!",
+ show_after=datetime(2012, 01, 01, 0, 0, 0))
+
+ def test_show_create(self):
+ self.client.login(username=self.u1.username, password='testpass')
+ resp = self.client.get(reverse('dashboards.localization'))
+ self.assertContains(resp, 'id="create-announcement"')
+
+ def test_show_for_authed(self):
+ self.client.login(username=self.u2.username, password='testpass')
+ resp = self.client.get(reverse('dashboards.localization'))
+ self.assertContains(resp, 'id="create-announcement"')
+
+ def test_hide_for_not_authed(self):
+ self.client.login(username=self.u3.username, password='testpass')
+ resp = self.client.get(reverse('dashboards.localization'))
+ self.assertNotContains(resp, 'id="create-announcement"')
+
+ def test_hide_for_anon(self):
+ resp = self.client.get(reverse('dashboards.localization'))
+ self.assertNotContains(resp, 'id="create-announcement"')
+
+
class ContributorDashTests(TestCase):
def test_main_view(self):
"""Assert the top page of the contributor dash resolves, renders."""
View
@@ -5,6 +5,7 @@
import jingo
from announcements.models import Announcement
+from announcements.forms import AnnouncementForm
from dashboards import ACTIONS_PER_PAGE
from sumo_locales import LOCALES
from sumo.utils import paginate
@@ -55,6 +56,7 @@ def render_readouts(request, readouts, template, locale=None, extra_data=None):
'is_watching_default_ready':
ReadyRevisionEvent.is_notifying(request.user),
'on_default_locale': on_default_locale,
+ 'announce_form': AnnouncementForm(),
'announcements': Announcement.get_for_locale_name(current_locale),
}
if extra_data:
View
@@ -1,5 +1,4 @@
import colorsys
-from functools import partial
import json
import logging
import math
@@ -13,6 +12,7 @@
from tower import ugettext as _
from access.decorators import login_required
+from announcements.views import user_can_announce
from announcements.models import Announcement
from dashboards.personal import GROUP_DASHBOARDS
from dashboards.readouts import (overview_rows, READOUTS, L10N_READOUTS,
@@ -23,6 +23,7 @@
from sumo.redis_utils import redis_client, RedisError
from sumo.urlresolvers import reverse
from sumo.utils import paginate, smart_int
+from wiki.models import Locale
log = logging.getLogger('k.dashboards')
@@ -69,7 +70,16 @@ def localization(request):
"""Render aggregate data about articles in a non-default locale."""
if request.locale == settings.WIKI_DEFAULT_LANGUAGE:
return HttpResponseRedirect(reverse('dashboards.contributors'))
- data = {'overview_rows': overview_rows(request.locale)}
+ locales = Locale.objects.filter(locale=request.locale)
+ if locales:
+ permission = user_can_announce(request.user, locales[0])
+ else:
+ permission = False
+
+ data = {
+ 'overview_rows': overview_rows(request.locale),
+ 'user_can_announce': permission
+ }
return render_readouts(request, L10N_READOUTS, 'localization.html',
extra_data=data)
Oops, something went wrong.

0 comments on commit f67ea61

Please sign in to comment.