Commit d281e23bb665e949cf8dedc12725838405f1ce12

Authored by Sergio Oliveira
2 parents 4a86615c 1ce3e428

Merge pull request #4 from TracyWebTech/master

Updating
Showing 48 changed files with 682 additions and 546 deletions   Show diff stats
fabfile.py
... ... @@ -81,7 +81,6 @@ def deploy(update=False):
81 81 if update:
82 82 update_requirements()
83 83  
84   - load_badges()
85 84 with cd('~/colab/src/'), prefix(WORKON_COLAB):
86 85 run('python manage.py syncdb')
87 86 run('python manage.py migrate')
... ... @@ -91,17 +90,6 @@ def deploy(update=False):
91 90 sudo('supervisorctl restart all')
92 91  
93 92  
94   -def load_badges(local=False):
95   - path = '/vagrant/' if local else '~/colab/'
96   -
97   - run(u'mkdir -p {}www/media/badges'.format(path))
98   -
99   - with cd(u'{}src/'.format(path)), prefix(WORKON_COLAB):
100   - run('cp badger/fixtures/images/*.png ../www/media/badges/')
101   - run('python manage.py loaddata badger/fixtures/badges.json')
102   - run('python manage.py update_badges')
103   -
104   -
105 93 def rebuild_index(age=None, batch=None):
106 94 with cd('~/colab/src/'), prefix(WORKON_COLAB):
107 95 age_arg = ''
... ... @@ -139,5 +127,4 @@ def runserver(update_requirements=False):
139 127  
140 128 run('python manage.py syncdb')
141 129 run('python manage.py migrate')
142   - load_badges(local=True)
143 130 run('python manage.py runserver 0.0.0.0:7000')
... ...
puppet/modules/colab/templates/nginx/site_default.erb
... ... @@ -40,6 +40,14 @@ server {
40 40 proxy_pass http://10.1.2.81;
41 41 }
42 42  
  43 + location /ci/static {
  44 + proxy_pass http://10.1.2.171:8080;
  45 + }
  46 +
  47 + location /ci/adjuncts {
  48 + proxy_pass http://10.1.2.171:8080;
  49 + }
  50 +
43 51 location / {
44 52 try_files /home/colab/colab/www$uri @django;
45 53 }
... ...
requirements.txt
... ... @@ -5,7 +5,7 @@ django-piston==0.2.3
5 5 pytz==2011n
6 6 chardet==1.0.1
7 7 python-dateutil==1.5
8   -django-cliauth==0.9
  8 +django-cliauth==0.9.1
9 9 django-mobile==0.3.0
10 10 django-haystack==2.1
11 11 pysolr==2.1
... ... @@ -28,10 +28,10 @@ tornado==3.1.1
28 28 # Deps for Single SignOn (SSO)
29 29 git+https://github.com/mozilla/django-browserid
30 30  
31   -django-revproxy==0.2.5
  31 +django-revproxy==0.2.7
32 32  
33 33 # Converse.js (XMPP client)
34   -django-conversejs==0.2.8
  34 +django-conversejs==0.2.9
35 35 git+https://github.com/TracyWebTech/SleekXMPP@fix-gevent
36 36  
37 37 # Feedzilla (planet) and deps
... ...
src/accounts/errors.py 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +class XMPPChangePwdException(Exception):
  2 + """Error changing XMPP Account password"""
... ...
src/accounts/forms.py
... ... @@ -4,6 +4,8 @@ from django import forms
4 4 from django.contrib.auth import get_user_model
5 5 from django.utils.translation import ugettext_lazy as _
6 6  
  7 +from conversejs.models import XMPPAccount
  8 +
7 9 from super_archives.models import MailingList
8 10 from .utils.validators import validate_social_account
9 11  
... ... @@ -50,7 +52,7 @@ class UserUpdateForm(UserForm):
50 52  
51 53 class Meta:
52 54 model = User
53   - fields = ('username', 'first_name', 'last_name',
  55 + fields = ('first_name', 'last_name',
54 56 'institution', 'role', 'twitter', 'facebook',
55 57 'google_talk', 'webpage')
56 58  
... ... @@ -64,3 +66,38 @@ class ListsForm(forms.Form):
64 66 required=False,
65 67 widget=forms.CheckboxSelectMultiple,
66 68 choices=LISTS_NAMES)
  69 +
  70 +
  71 +class ChangeXMPPPasswordForm(forms.ModelForm):
  72 + password1 = forms.CharField(label=_("Password"),
  73 + widget=forms.PasswordInput)
  74 + password2 = forms.CharField(label=_("Password confirmation"),
  75 + widget=forms.PasswordInput,
  76 + help_text=_("Enter the same password as above, for verification."))
  77 +
  78 + class Meta:
  79 + model = XMPPAccount
  80 + fields = ('password1', 'password2')
  81 +
  82 + def __init__(self, *args, **kwargs):
  83 + super(ChangeXMPPPasswordForm, self).__init__(*args, **kwargs)
  84 +
  85 + for field_name, field in self.fields.items():
  86 + # Adds form-control class to all form fields
  87 + field.widget.attrs.update({'class': 'form-control'})
  88 +
  89 + def clean_password2(self):
  90 + password1 = self.cleaned_data.get("password1")
  91 + password2 = self.cleaned_data.get("password2")
  92 + if password1 and password2 and password1 != password2:
  93 + raise forms.ValidationError(
  94 + _("Password mismatch"),
  95 + code='password_mismatch',
  96 + )
  97 + return password2
  98 +
  99 + def save(self, commit=True):
  100 + self.instance.password = self.cleaned_data['password2']
  101 + if commit:
  102 + self.instance.save()
  103 + return self.instance
... ...
src/accounts/models.py
  1 +# -*- coding: utf-8 -*-
1 2  
2 3 import urlparse
3 4  
4   -from django.db import models
  5 +from django.db import models, DatabaseError
  6 +from django.contrib.auth.hashers import check_password
5 7 from django.contrib.auth.models import AbstractUser
6 8 from django.core.urlresolvers import reverse
7 9  
  10 +from conversejs import xmpp
  11 +
8 12 from .utils import mailman
9 13  
10 14  
... ... @@ -18,6 +22,13 @@ class User(AbstractUser):
18 22 verification_hash = models.CharField(max_length=32, null=True, blank=True)
19 23 modified = models.DateTimeField(auto_now=True)
20 24  
  25 + def check_password(self, raw_password):
  26 +
  27 + if self.xmpp.exists() and raw_password == self.xmpp.first().password:
  28 + return True
  29 +
  30 + return super(User, self).check_password(raw_password)
  31 +
21 32 def get_absolute_url(self):
22 33 return reverse('user_profile', kwargs={'username': self.username})
23 34  
... ...
src/accounts/templates/accounts/change_password.html 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +{% extends "base.html" %}
  2 +{% load i18n %}
  3 +
  4 +{% block main-content %}
  5 +<form method="POST" role="form">
  6 + {% csrf_token %}
  7 + <div class="row">
  8 + <h2>{% trans "Change XMPP Client and SVN Password" %}</h2>
  9 + <div class="col-lg-4 col-md-4 col-sm-12 col-xs-12">
  10 + {% for field in form %}
  11 + <div class="form-group required{% if field.errors %} alert alert-danger has-error{% endif %}">
  12 + <label for="{{ field.name }}" class="control-label">{{ field.label }}</label>
  13 + {{ field }}
  14 + {{ field.errors }}
  15 + </div>
  16 + {% endfor %}
  17 + <button class="btn btn-primary">{% trans "Change Password" %}</button>
  18 + </div>
  19 + </div>
  20 +</form>
  21 +{% endblock %}
... ...
src/accounts/templates/accounts/user_detail.html
... ... @@ -109,7 +109,7 @@
109 109 <div>
110 110 {% for badge in user_.badge_set.all %}
111 111 {% translate badge as badge_trans %}
112   - <img src="{{ badge.get_badge_url }}" title="({{ badge_trans.title }}) {{ badge_trans.description }}" />
  112 + <img src="data:image/png;base64,{{ badge.image_base64 }}" title="({{ badge_trans.title }}) {{ badge_trans.description }}" />
113 113 {% endfor %}
114 114 </div>
115 115 </div>
... ...
src/accounts/templates/accounts/user_update_form.html
... ... @@ -178,6 +178,21 @@ $(function() {
178 178 </div>
179 179 </div>
180 180 </div>
  181 +
  182 + <div class="col-lg-4 col-md-5 col-sm-12 col-xm-12">
  183 + <div class="panel panel-default">
  184 + <div class="panel-heading">
  185 + <h3 class="panel-title">{% trans "Change SVN and XMPP Client password" %}</h3>
  186 + </div>
  187 + <div class="panel-body">
  188 + <div class="form-group">
  189 + {% trans "You don't need to change this password. Change it only if you really know what you are doing." %}
  190 + </div>
  191 + <a href="{% url 'change_password' %}" class="btn btn-primary pull-right">{% trans "Change Password" %}</a>
  192 + </div>
  193 + </div>
  194 + </div>
  195 +
181 196 </div>
182 197 <div class="row">
183 198 <div class="submit">
... ...
src/accounts/urls.py
1 1  
2 2 from django.conf.urls import patterns, include, url
3 3  
4   -from .views import UserProfileDetailView, UserProfileUpdateView, \
5   - ManageUserSubscriptionsView
  4 +from .views import (UserProfileDetailView, UserProfileUpdateView,
  5 + ManageUserSubscriptionsView, ChangeXMPPPasswordView)
6 6  
7 7  
8 8 urlpatterns = patterns('',
9 9 url(r'^register/$', 'accounts.views.signup', name='signup'),
10 10  
  11 + url(r'^change-password/$',
  12 + ChangeXMPPPasswordView.as_view(), name='change_password'),
  13 +
  14 + url(r'^logout/?$', 'django.contrib.auth.views.logout'),
  15 +
11 16 url(r'^(?P<username>[\w@+.-]+)/?$',
12 17 UserProfileDetailView.as_view(), name='user_profile'),
13 18  
... ...
src/accounts/views.py
... ... @@ -6,20 +6,26 @@ import datetime
6 6 from collections import OrderedDict
7 7  
8 8 from django.contrib import messages
  9 +from django.db import transaction
9 10 from django.db.models import Count
10 11 from django.contrib.auth import get_user_model
11 12 from django.utils.translation import ugettext as _
12   -from django.shortcuts import render, redirect
  13 +from django.shortcuts import render, redirect, get_object_or_404
13 14 from django.core.urlresolvers import reverse
14 15 from django.core.exceptions import PermissionDenied
15 16 from django.views.generic import DetailView, UpdateView
  17 +from django.utils.decorators import method_decorator
16 18  
  19 +from conversejs import xmpp
  20 +from conversejs.models import XMPPAccount
17 21 from haystack.query import SearchQuerySet
18 22  
19 23 from super_archives.models import EmailAddress, Message
20 24 from super_archives.utils.email import send_email_lists
21 25 from search.utils import trans
22   -from .forms import UserCreationForm, ListsForm, UserUpdateForm
  26 +from .forms import (UserCreationForm, ListsForm, UserUpdateForm,
  27 + ChangeXMPPPasswordForm)
  28 +from .errors import XMPPChangePwdException
23 29 from .utils import mailman
24 30  
25 31  
... ... @@ -129,6 +135,14 @@ class ManageUserSubscriptionsView(UserProfileBaseMixin, DetailView):
129 135 http_method_names = [u'get', u'post']
130 136 template_name = u'accounts/manage_subscriptions.html'
131 137  
  138 + def get_object(self, *args, **kwargs):
  139 + obj = super(ManageUserSubscriptionsView, self).get_object(*args,
  140 + **kwargs)
  141 + if self.request.user != obj and not self.request.user.is_superuser:
  142 + raise PermissionDenied
  143 +
  144 + return obj
  145 +
132 146 def post(self, request, *args, **kwargs):
133 147 user = self.get_object()
134 148 for email in user.emails.values_list('address', flat=True):
... ... @@ -160,3 +174,47 @@ class ManageUserSubscriptionsView(UserProfileBaseMixin, DetailView):
160 174 context.update(kwargs)
161 175  
162 176 return super(ManageUserSubscriptionsView, self).get_context_data(**context)
  177 +
  178 +
  179 +class ChangeXMPPPasswordView(UpdateView):
  180 + model = XMPPAccount
  181 + form_class = ChangeXMPPPasswordForm
  182 + fields = ['password', ]
  183 + template_name = 'accounts/change_password.html'
  184 +
  185 + def get_success_url(self):
  186 + return reverse('user_profile', kwargs={
  187 + 'username': self.request.user.username
  188 + })
  189 +
  190 + def get_object(self, queryset=None):
  191 + obj = get_object_or_404(XMPPAccount, user=self.request.user.pk)
  192 + self.old_password = obj.password
  193 + return obj
  194 +
  195 + def form_valid(self, form):
  196 + transaction.set_autocommit(False)
  197 +
  198 + response = super(ChangeXMPPPasswordView, self).form_valid(form)
  199 +
  200 + changed = xmpp.change_password(
  201 + self.object.jid,
  202 + self.old_password,
  203 + form.cleaned_data['password1']
  204 + )
  205 +
  206 + if not changed:
  207 + messages.error(
  208 + self.request,
  209 + _(u'Could not change your password. Please, try again later.')
  210 + )
  211 + transaction.rollback()
  212 + return response
  213 + else:
  214 + transaction.commit()
  215 +
  216 + messages.success(
  217 + self.request,
  218 + _("You've changed your password successfully!")
  219 + )
  220 + return response
... ...
src/badger/admin.py
... ... @@ -3,6 +3,7 @@
3 3 from django.contrib import admin
4 4 from django.utils.translation import ugettext_lazy as _
5 5  
  6 +from .forms import BadgeForm
6 7 from .models import Badge, BadgeI18N
7 8  
8 9  
... ... @@ -11,6 +12,7 @@ class BadgeI18NInline(admin.TabularInline):
11 12  
12 13  
13 14 class BadgeAdmin(admin.ModelAdmin):
  15 + form = BadgeForm
14 16 inlines = [BadgeI18NInline, ]
15 17 list_display = ['title', 'description', 'order']
16 18 list_editable = ['order', ]
... ...
src/badger/fixtures/badges.json
... ... @@ -1,502 +0,0 @@
1   -[
2   -{
3   - "pk": 6,
4   - "model": "badger.badge",
5   - "fields": {
6   - "awardees": [],
7   - "comparison": "gte",
8   - "description": "More than 10000 Messages",
9   - "title": "Diamond Messenger",
10   - "image": "badges/diamond_messenger.png",
11   - "value": 10000,
12   - "user_attr": "messages",
13   - "type": "auto",
14   - "order": 4
15   - }
16   -},
17   -{
18   - "pk": 8,
19   - "model": "badger.badge",
20   - "fields": {
21   - "awardees": [],
22   - "comparison": "gte",
23   - "description": "More than 100 messages",
24   - "title": "Brass Messenger",
25   - "image": "badges/brass_messenger.png",
26   - "value": 100,
27   - "user_attr": "messages",
28   - "type": "auto",
29   - "order": 1
30   - }
31   -},
32   -{
33   - "pk": 9,
34   - "model": "badger.badge",
35   - "fields": {
36   - "awardees": [],
37   - "comparison": "gte",
38   - "description": "More than 500 Messages",
39   - "title": "Silver Messenger",
40   - "image": "badges/silver_messenger.png",
41   - "value": 500,
42   - "user_attr": "messages",
43   - "type": "auto",
44   - "order": 2
45   - }
46   -},
47   -{
48   - "pk": 10,
49   - "model": "badger.badge",
50   - "fields": {
51   - "awardees": [],
52   - "comparison": "gte",
53   - "description": "More than 1000 Messages",
54   - "title": "Gold Messenger",
55   - "image": "badges/gold_messenger.png",
56   - "value": 1000,
57   - "user_attr": "messages",
58   - "type": "auto",
59   - "order": 3
60   - }
61   -},
62   -{
63   - "pk": 11,
64   - "model": "badger.badge",
65   - "fields": {
66   - "awardees": [],
67   - "comparison": "gte",
68   - "description": "More than 10 Tickets",
69   - "title": "Brass Tracker",
70   - "image": "badges/brass_tracker.png",
71   - "value": 10,
72   - "user_attr": "tickets",
73   - "type": "auto",
74   - "order": 5
75   - }
76   -},
77   -{
78   - "pk": 12,
79   - "model": "badger.badge",
80   - "fields": {
81   - "awardees": [],
82   - "comparison": "gte",
83   - "description": "More than 50 Tickets",
84   - "title": "Silver Tracker",
85   - "image": "badges/silver_tracker.png",
86   - "value": 50,
87   - "user_attr": "tickets",
88   - "type": "auto",
89   - "order": 6
90   - }
91   -},
92   -{
93   - "pk": 13,
94   - "model": "badger.badge",
95   - "fields": {
96   - "awardees": [],
97   - "comparison": "gte",
98   - "description": "Mais que 100 Tickets",
99   - "title": "Gold Tracker",
100   - "image": "badges/gold_tracker.png",
101   - "value": 100,
102   - "user_attr": "tickets",
103   - "type": "auto",
104   - "order": 7
105   - }
106   -},
107   -{
108   - "pk": 14,
109   - "model": "badger.badge",
110   - "fields": {
111   - "awardees": [],
112   - "comparison": "gte",
113   - "description": "Mais que 500 Tickets",
114   - "title": "Diamond Tracker",
115   - "image": "badges/diamond_tracker.png",
116   - "value": 500,
117   - "user_attr": "tickets",
118   - "type": "auto",
119   - "order": 8
120   - }
121   -},
122   -{
123   - "pk": 15,
124   - "model": "badger.badge",
125   - "fields": {
126   - "awardees": [],
127   - "comparison": "gte",
128   - "description": "More than 100 Changesets",
129   - "title": "Brass Coder",
130   - "image": "badges/brass_coder.png",
131   - "value": 100,
132   - "user_attr": "revisions",
133   - "type": "auto",
134   - "order": 9
135   - }
136   -},
137   -{
138   - "pk": 16,
139   - "model": "badger.badge",
140   - "fields": {
141   - "awardees": [],
142   - "comparison": "gte",
143   - "description": "More than 500 Changesets",
144   - "title": "Silver Coder",
145   - "image": "badges/silver_coder.png",
146   - "value": 500,
147   - "user_attr": "revisions",
148   - "type": "auto",
149   - "order": 10
150   - }
151   -},
152   -{
153   - "pk": 17,
154   - "model": "badger.badge",
155   - "fields": {
156   - "awardees": [],
157   - "comparison": "gte",
158   - "description": "More than 1000 Changesets",
159   - "title": "Gold Coder",
160   - "image": "badges/gold_coder.png",
161   - "value": 1000,
162   - "user_attr": "revisions",
163   - "type": "auto",
164   - "order": 11
165   - }
166   -},
167   -{
168   - "pk": 18,
169   - "model": "badger.badge",
170   - "fields": {
171   - "awardees": [],
172   - "comparison": "gte",
173   - "description": "More than 10000 Changesets",
174   - "title": "Diamond Coder",
175   - "image": "badges/diamond_coder.png",
176   - "value": 10000,
177   - "user_attr": "revisions",
178   - "type": "auto",
179   - "order": 12
180   - }
181   -},
182   -{
183   - "pk": 19,
184   - "model": "badger.badge",
185   - "fields": {
186   - "awardees": [],
187   - "comparison": "gte",
188   - "description": "More than 10 Wikis",
189   - "title": "Brass Writer",
190   - "image": "badges/brass_writer.png",
191   - "value": 10,
192   - "user_attr": "wikis",
193   - "type": "auto",
194   - "order": 13
195   - }
196   -},
197   -{
198   - "pk": 20,
199   - "model": "badger.badge",
200   - "fields": {
201   - "awardees": [],
202   - "comparison": "gte",
203   - "description": "More than 50 Wikis",
204   - "title": "Silver Writer",
205   - "image": "badges/silver_writer.png",
206   - "value": 50,
207   - "user_attr": "wikis",
208   - "type": "auto",
209   - "order": 14
210   - }
211   -},
212   -{
213   - "pk": 21,
214   - "model": "badger.badge",
215   - "fields": {
216   - "awardees": [],
217   - "comparison": "gte",
218   - "description": "More than 100 Wikis",
219   - "title": "Gold Writer",
220   - "image": "badges/gold_writer.png",
221   - "value": 100,
222   - "user_attr": "wikis",
223   - "type": "auto",
224   - "order": 15
225   - }
226   -},
227   -{
228   - "pk": 22,
229   - "model": "badger.badge",
230   - "fields": {
231   - "awardees": [],
232   - "comparison": "gte",
233   - "description": "More than 500 Wikis",
234   - "title": "Diamond Writer",
235   - "image": "badges/diamond_writer.png",
236   - "value": 500,
237   - "user_attr": "wikis",
238   - "type": "auto",
239   - "order": 16
240   - }
241   -},
242   -{
243   - "pk": 23,
244   - "model": "badger.badge",
245   - "fields": {
246   - "awardees": [],
247   - "comparison": "gte",
248   - "description": "More than 500 Contributions",
249   - "title": "Brass Contributor",
250   - "image": "badges/brass_contributor.png",
251   - "value": 500,
252   - "user_attr": "contributions",
253   - "type": "auto",
254   - "order": 17
255   - }
256   -},
257   -{
258   - "pk": 24,
259   - "model": "badger.badge",
260   - "fields": {
261   - "awardees": [],
262   - "comparison": "gte",
263   - "description": "More than 1000 Contributions",
264   - "title": "Silver Contributor",
265   - "image": "badges/silver_contributor.png",
266   - "value": 1000,
267   - "user_attr": "contributions",
268   - "type": "auto",
269   - "order": 18
270   - }
271   -},
272   -{
273   - "pk": 25,
274   - "model": "badger.badge",
275   - "fields": {
276   - "awardees": [],
277   - "comparison": "gte",
278   - "description": "More than 3000 Contributions",
279   - "title": "Gold Contributor",
280   - "image": "badges/gold_contributor.png",
281   - "value": 3000,
282   - "user_attr": "contributions",
283   - "type": "auto",
284   - "order": 19
285   - }
286   -},
287   -{
288   - "pk": 26,
289   - "model": "badger.badge",
290   - "fields": {
291   - "awardees": [],
292   - "comparison": "gte",
293   - "description": "More than 5000 Contributions",
294   - "title": "Diamond Contributor",
295   - "image": "badges/diamond_contributor.png",
296   - "value": 5000,
297   - "user_attr": "contributions",
298   - "type": "auto",
299   - "order": 20
300   - }
301   -},
302   -{
303   - "pk": 1,
304   - "model": "badger.badgei18n",
305   - "fields": {
306   - "i18n_language": "pt-br",
307   - "i18n_source": 8,
308   - "description": "Mais que 100 Mensagens",
309   - "title": "Mensageiro Bronze"
310   - }
311   -},
312   -{
313   - "pk": 2,
314   - "model": "badger.badgei18n",
315   - "fields": {
316   - "i18n_language": "pt-br",
317   - "i18n_source": 9,
318   - "description": "Mais que 500 Mensagens",
319   - "title": "Mensageiro Prata"
320   - }
321   -},
322   -{
323   - "pk": 3,
324   - "model": "badger.badgei18n",
325   - "fields": {
326   - "i18n_language": "pt-br",
327   - "i18n_source": 6,
328   - "description": "Mais que 10000 Mensagens",
329   - "title": "Mensageiro Diamante"
330   - }
331   -},
332   -{
333   - "pk": 4,
334   - "model": "badger.badgei18n",
335   - "fields": {
336   - "i18n_language": "pt-br",
337   - "i18n_source": 10,
338   - "description": "Mais que 1000 Mensagens",
339   - "title": "Mensageiro Ouro"
340   - }
341   -},
342   -{
343   - "pk": 5,
344   - "model": "badger.badgei18n",
345   - "fields": {
346   - "i18n_language": "pt-br",
347   - "i18n_source": 11,
348   - "description": "Mais que 10 T\u00edquetes",
349   - "title": "Rastreador Bronze"
350   - }
351   -},
352   -{
353   - "pk": 6,
354   - "model": "badger.badgei18n",
355   - "fields": {
356   - "i18n_language": "pt-br",
357   - "i18n_source": 12,
358   - "description": "Mais que 50 T\u00edquetes",
359   - "title": "Rastreador Prata"
360   - }
361   -},
362   -{
363   - "pk": 7,
364   - "model": "badger.badgei18n",
365   - "fields": {
366   - "i18n_language": "pt-br",
367   - "i18n_source": 13,
368   - "description": "Mais que 100 T\u00edquetes",
369   - "title": "Rastreador Ouro"
370   - }
371   -},
372   -{
373   - "pk": 8,
374   - "model": "badger.badgei18n",
375   - "fields": {
376   - "i18n_language": "pt-br",
377   - "i18n_source": 14,
378   - "description": "Mais que 500 T\u00edquetes",
379   - "title": "Rastreador Diamante"
380   - }
381   -},
382   -{
383   - "pk": 9,
384   - "model": "badger.badgei18n",
385   - "fields": {
386   - "i18n_language": "pt-br",
387   - "i18n_source": 15,
388   - "description": "Mais que 100 Modifica\u00e7\u00f5es de c\u00f3digo",
389   - "title": "Programador Bronze"
390   - }
391   -},
392   -{
393   - "pk": 10,
394   - "model": "badger.badgei18n",
395   - "fields": {
396   - "i18n_language": "pt-br",
397   - "i18n_source": 16,
398   - "description": "Mais que 500 Modifica\u00e7\u00f5es de c\u00f3digo",
399   - "title": "Programador Prata"
400   - }
401   -},
402   -{
403   - "pk": 11,
404   - "model": "badger.badgei18n",
405   - "fields": {
406   - "i18n_language": "pt-br",
407   - "i18n_source": 17,
408   - "description": "Mais que 1000 Modifica\u00e7\u00f5es de c\u00f3digo",
409   - "title": "Programador Ouro"
410   - }
411   -},
412   -{
413   - "pk": 12,
414   - "model": "badger.badgei18n",
415   - "fields": {
416   - "i18n_language": "pt-br",
417   - "i18n_source": 18,
418   - "description": "Mais que 10000 Modifica\u00e7\u00f5es de c\u00f3digo",
419   - "title": "Programador Diamante"
420   - }
421   -},
422   -{
423   - "pk": 13,
424   - "model": "badger.badgei18n",
425   - "fields": {
426   - "i18n_language": "pt-br",
427   - "i18n_source": 19,
428   - "description": "Escreveu mais que 10 Wikis",
429   - "title": "Escritor Bronze"
430   - }
431   -},
432   -{
433   - "pk": 14,
434   - "model": "badger.badgei18n",
435   - "fields": {
436   - "i18n_language": "pt-br",
437   - "i18n_source": 20,
438   - "description": "Escreveu mais que 50 Wikis",
439   - "title": "Escritor Prata"
440   - }
441   -},
442   -{
443   - "pk": 15,
444   - "model": "badger.badgei18n",
445   - "fields": {
446   - "i18n_language": "pt-br",
447   - "i18n_source": 21,
448   - "description": "Escreveu mais que 100 Wikis",
449   - "title": "Escritor Ouro"
450   - }
451   -},
452   -{
453   - "pk": 16,
454   - "model": "badger.badgei18n",
455   - "fields": {
456   - "i18n_language": "pt-br",
457   - "i18n_source": 22,
458   - "description": "Escreveu mais que 500 Wikis",
459   - "title": "Escritor Diamante"
460   - }
461   -},
462   -{
463   - "pk": 17,
464   - "model": "badger.badgei18n",
465   - "fields": {
466   - "i18n_language": "pt-br",
467   - "i18n_source": 23,
468   - "description": "Mais que 500 Contribui\u00e7\u00f5es",
469   - "title": "Contribuidor Bronze"
470   - }
471   -},
472   -{
473   - "pk": 18,
474   - "model": "badger.badgei18n",
475   - "fields": {
476   - "i18n_language": "pt-br",
477   - "i18n_source": 24,
478   - "description": "Mais que 1000 Contribui\u00e7\u00f5es",
479   - "title": "Contribuidor Prata"
480   - }
481   -},
482   -{
483   - "pk": 19,
484   - "model": "badger.badgei18n",
485   - "fields": {
486   - "i18n_language": "pt-br",
487   - "i18n_source": 25,
488   - "description": "Mais que 3000 Contribui\u00e7\u00f5es",
489   - "title": "Contribuidor Ouro"
490   - }
491   -},
492   -{
493   - "pk": 20,
494   - "model": "badger.badgei18n",
495   - "fields": {
496   - "i18n_language": "pt-br",
497   - "i18n_source": 26,
498   - "description": "Mais que 5000 Contribui\u00e7\u00f5es",
499   - "title": "Contribuidor Diamante"
500   - }
501   -}
502   -]
src/badger/fixtures/images/brass_coder.png

3.33 KB

src/badger/fixtures/images/brass_contributor.png

3.65 KB

src/badger/fixtures/images/brass_messenger.png

3.2 KB

src/badger/fixtures/images/brass_tracker.png

3.48 KB

src/badger/fixtures/images/brass_writer.png

3.51 KB

src/badger/fixtures/images/diamond_coder.png

3.04 KB

src/badger/fixtures/images/diamond_contributor.png

3.16 KB

src/badger/fixtures/images/diamond_messenger.png

2.81 KB

src/badger/fixtures/images/diamond_tracker.png

3.02 KB

src/badger/fixtures/images/diamond_writer.png

3.05 KB

src/badger/fixtures/images/gold_coder.png

2.77 KB

src/badger/fixtures/images/gold_contributor.png

2.87 KB

src/badger/fixtures/images/gold_messenger.png

2.56 KB

src/badger/fixtures/images/gold_tracker.png

2.81 KB

src/badger/fixtures/images/gold_writer.png

2.81 KB

src/badger/fixtures/images/silver_coder.png

2.75 KB

src/badger/fixtures/images/silver_contributor.png

2.71 KB

src/badger/fixtures/images/silver_messenger.png

2.47 KB

src/badger/fixtures/images/silver_tracker.png

2.62 KB

src/badger/fixtures/images/silver_writer.png

2.66 KB

src/badger/forms.py 0 → 100644
... ... @@ -0,0 +1,48 @@
  1 +# -*- coding: utf-8 -*-
  2 +
  3 +import base64
  4 +
  5 +from django import forms
  6 +from django.utils.translation import ugettext_lazy as _
  7 +
  8 +from PIL import Image
  9 +
  10 +from .models import Badge
  11 +
  12 +try:
  13 + from cStringIO import StringIO
  14 +except ImportError:
  15 + from StringIO import StringIO
  16 +
  17 +
  18 +class BadgeForm(forms.ModelForm):
  19 + image = forms.ImageField(label=_(u'Image'), required=False)
  20 +
  21 + class Meta:
  22 + model = Badge
  23 + fields = (
  24 + 'title', 'description', 'image', 'user_attr', 'comparison',
  25 + 'value', 'awardees'
  26 + )
  27 +
  28 + def clean_image(self):
  29 + if not self.instance.pk and not self.cleaned_data['image']:
  30 + raise forms.ValidationError(_(u'You must add an Image'))
  31 + return self.cleaned_data['image']
  32 +
  33 + def save(self, commit=True):
  34 +
  35 + instance = super(BadgeForm, self).save(commit=False)
  36 +
  37 + if self.cleaned_data['image']:
  38 + img = Image.open(self.cleaned_data['image'])
  39 + img = img.resize((50, 50), Image.ANTIALIAS)
  40 + f = StringIO()
  41 + img.save(f, 'png')
  42 + instance.image_base64 = f.getvalue().encode('base64')
  43 + f.close()
  44 +
  45 + if commit:
  46 + instance.save()
  47 +
  48 + return instance
... ...
src/badger/migrations/0005_auto__add_field_badge_image_base64.py 0 → 100644
... ... @@ -0,0 +1,91 @@
  1 +# -*- coding: utf-8 -*-
  2 +import datetime
  3 +from south.db import db
  4 +from south.v2 import SchemaMigration
  5 +from django.db import models
  6 +
  7 +
  8 +class Migration(SchemaMigration):
  9 +
  10 + def forwards(self, orm):
  11 + # Adding field 'Badge.image_base64'
  12 + db.add_column(u'badger_badge', 'image_base64',
  13 + self.gf('django.db.models.fields.TextField')(default=''),
  14 + keep_default=False)
  15 +
  16 +
  17 + def backwards(self, orm):
  18 + # Deleting field 'Badge.image_base64'
  19 + db.delete_column(u'badger_badge', 'image_base64')
  20 +
  21 +
  22 + models = {
  23 + u'accounts.user': {
  24 + 'Meta': {'object_name': 'User'},
  25 + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  26 + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
  27 + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
  28 + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  29 + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
  30 + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
  31 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  32 + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
  33 + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
  34 + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  35 + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  36 + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  37 + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  38 + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
  39 + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
  40 + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
  41 + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
  42 + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
  43 + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}),
  44 + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
  45 + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'})
  46 + },
  47 + u'auth.group': {
  48 + 'Meta': {'object_name': 'Group'},
  49 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  50 + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
  51 + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
  52 + },
  53 + u'auth.permission': {
  54 + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
  55 + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  56 + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
  57 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  58 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
  59 + },
  60 + u'badger.badge': {
  61 + 'Meta': {'ordering': "['order']", 'object_name': 'Badge'},
  62 + 'awardees': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['accounts.User']", 'null': 'True', 'blank': 'True'}),
  63 + 'comparison': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
  64 + 'description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
  65 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  66 + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
  67 + 'image_base64': ('django.db.models.fields.TextField', [], {}),
  68 + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '100'}),
  69 + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
  70 + 'type': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
  71 + 'user_attr': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
  72 + 'value': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'})
  73 + },
  74 + u'badger.badgei18n': {
  75 + 'Meta': {'unique_together': "(('i18n_source', 'i18n_language'),)", 'object_name': 'BadgeI18N'},
  76 + 'description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
  77 + 'i18n_language': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
  78 + 'i18n_source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'to': u"orm['badger.Badge']"}),
  79 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  80 + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
  81 + },
  82 + u'contenttypes.contenttype': {
  83 + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
  84 + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  85 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  86 + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  87 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
  88 + }
  89 + }
  90 +
  91 + complete_apps = ['badger']
0 92 \ No newline at end of file
... ...
src/badger/migrations/0006_imgpath_to_base64_field.py 0 → 100644
... ... @@ -0,0 +1,94 @@
  1 +# -*- coding: utf-8 -*-
  2 +import base64
  3 +import os
  4 +
  5 +import datetime
  6 +from south.db import db
  7 +from south.v2 import DataMigration
  8 +from django.db import models
  9 +from django.conf import settings
  10 +
  11 +class Migration(DataMigration):
  12 +
  13 + def forwards(self, orm):
  14 + for obj in orm.Badge.objects.all():
  15 + img = open(os.path.join(settings.MEDIA_ROOT, obj.image.path))
  16 + obj.image_base64 = base64.b64encode(img.read())
  17 + obj.save()
  18 +
  19 + def backwards(self, orm):
  20 + for obj in orm.Badge.objects.all():
  21 + obj.image_base64 = ''
  22 + obj.save()
  23 +
  24 + models = {
  25 + u'accounts.user': {
  26 + 'Meta': {'object_name': 'User'},
  27 + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  28 + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
  29 + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
  30 + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  31 + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
  32 + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
  33 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  34 + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
  35 + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
  36 + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  37 + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  38 + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  39 + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  40 + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
  41 + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
  42 + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
  43 + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
  44 + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
  45 + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}),
  46 + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
  47 + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'})
  48 + },
  49 + u'auth.group': {
  50 + 'Meta': {'object_name': 'Group'},
  51 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  52 + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
  53 + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
  54 + },
  55 + u'auth.permission': {
  56 + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
  57 + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  58 + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
  59 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  60 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
  61 + },
  62 + u'badger.badge': {
  63 + 'Meta': {'ordering': "['order']", 'object_name': 'Badge'},
  64 + 'awardees': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['accounts.User']", 'null': 'True', 'blank': 'True'}),
  65 + 'comparison': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
  66 + 'description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
  67 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  68 + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
  69 + 'image_base64': ('django.db.models.fields.TextField', [], {}),
  70 + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '100'}),
  71 + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
  72 + 'type': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
  73 + 'user_attr': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
  74 + 'value': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'})
  75 + },
  76 + u'badger.badgei18n': {
  77 + 'Meta': {'unique_together': "(('i18n_source', 'i18n_language'),)", 'object_name': 'BadgeI18N'},
  78 + 'description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
  79 + 'i18n_language': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
  80 + 'i18n_source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'to': u"orm['badger.Badge']"}),
  81 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  82 + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
  83 + },
  84 + u'contenttypes.contenttype': {
  85 + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
  86 + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  87 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  88 + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  89 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
  90 + }
  91 + }
  92 +
  93 + complete_apps = ['badger']
  94 + symmetrical = True
... ...
src/badger/migrations/0007_auto__del_field_badge_image.py 0 → 100644
... ... @@ -0,0 +1,90 @@
  1 +# -*- coding: utf-8 -*-
  2 +import datetime
  3 +from south.db import db
  4 +from south.v2 import SchemaMigration
  5 +from django.db import models
  6 +
  7 +
  8 +class Migration(SchemaMigration):
  9 +
  10 + def forwards(self, orm):
  11 + # Deleting field 'Badge.image'
  12 + db.delete_column(u'badger_badge', 'image')
  13 +
  14 +
  15 + def backwards(self, orm):
  16 + # Adding field 'Badge.image'
  17 + db.add_column(u'badger_badge', 'image',
  18 + self.gf('django.db.models.fields.files.ImageField')(default='', max_length=100),
  19 + keep_default=False)
  20 +
  21 +
  22 + models = {
  23 + u'accounts.user': {
  24 + 'Meta': {'object_name': 'User'},
  25 + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  26 + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
  27 + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
  28 + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  29 + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
  30 + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
  31 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  32 + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
  33 + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
  34 + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  35 + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  36 + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  37 + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  38 + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
  39 + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
  40 + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
  41 + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
  42 + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
  43 + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}),
  44 + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
  45 + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'})
  46 + },
  47 + u'auth.group': {
  48 + 'Meta': {'object_name': 'Group'},
  49 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  50 + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
  51 + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
  52 + },
  53 + u'auth.permission': {
  54 + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
  55 + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  56 + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
  57 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  58 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
  59 + },
  60 + u'badger.badge': {
  61 + 'Meta': {'ordering': "['order']", 'object_name': 'Badge'},
  62 + 'awardees': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['accounts.User']", 'null': 'True', 'blank': 'True'}),
  63 + 'comparison': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
  64 + 'description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
  65 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  66 + 'image_base64': ('django.db.models.fields.TextField', [], {}),
  67 + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '100'}),
  68 + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
  69 + 'type': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
  70 + 'user_attr': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
  71 + 'value': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'})
  72 + },
  73 + u'badger.badgei18n': {
  74 + 'Meta': {'unique_together': "(('i18n_source', 'i18n_language'),)", 'object_name': 'BadgeI18N'},
  75 + 'description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
  76 + 'i18n_language': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
  77 + 'i18n_source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'to': u"orm['badger.Badge']"}),
  78 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  79 + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
  80 + },
  81 + u'contenttypes.contenttype': {
  82 + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
  83 + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  84 + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  85 + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  86 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
  87 + }
  88 + }
  89 +
  90 + complete_apps = ['badger']
0 91 \ No newline at end of file
... ...
src/badger/models.py
... ... @@ -4,7 +4,6 @@ from django.conf import settings
4 4 from django.contrib.auth import get_user_model
5 5 from django.db import models
6 6 from django.utils.translation import ugettext_lazy as _
7   -from PIL import Image
8 7 from i18n_model.models import I18nModel
9 8  
10 9  
... ... @@ -38,7 +37,7 @@ class Badge(models.Model):
38 37 null=True)
39 38 description = models.CharField(_(u'Description'), max_length=200,
40 39 blank=True, null=True)
41   - image = models.ImageField(upload_to='badges')
  40 + image_base64 = models.TextField(_(u'Image'))
42 41 type = models.CharField(_(u'Type'), max_length=200, choices=TYPE_CHOICES)
43 42 user_attr = models.CharField(
44 43 _(u'User attribute'),max_length=100,
... ... @@ -71,16 +70,6 @@ class Badge(models.Model):
71 70 verbose_name_plural = _(u'Badges')
72 71 ordering = ['order', ]
73 72  
74   - def get_badge_url(self):
75   - return u'{}{}'.format(settings.MEDIA_URL, self.image)
76   -
77   - def save(self, *args, **kwargs):
78   - img = Image.open(self.image)
79   - (width, height) = img.size
80   - img = img.resize((50, 50), Image.ANTIALIAS)
81   - super(Badge, self).save(*args, **kwargs)
82   - img.save(self.image.path)
83   -
84 73 def __unicode__(self):
85 74 return u'{} ({}, {})'.format(
86 75 self.title,
... ...
src/colab/custom_settings.py
... ... @@ -183,10 +183,12 @@ LOGGING = {
183 183 'django_browserid': {
184 184 'handlers': ['sentry'],
185 185 'level': 'WARNING',
  186 + 'propagate': False,
186 187 },
187 188 'conversejs': {
188 189 'handlers': ['console'],
189 190 'level': 'DEBUG',
  191 + 'propagate': False,
190 192 },
191 193 }
192 194 }
... ...
src/locale/pt_BR/LC_MESSAGES/django.mo
No preview for this file type
src/locale/pt_BR/LC_MESSAGES/django.po
... ... @@ -7,7 +7,7 @@ msgid &quot;&quot;
7 7 msgstr ""
8 8 "Project-Id-Version: PACKAGE VERSION\n"
9 9 "Report-Msgid-Bugs-To: \n"
10   -"POT-Creation-Date: 2013-11-21 10:48+0000\n"
  10 +"POT-Creation-Date: 2013-11-26 13:17+0000\n"
11 11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12 12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 13 "Language-Team: LANGUAGE <LL@li.org>\n"
... ... @@ -29,19 +29,35 @@ msgstr &quot;Permissões&quot;
29 29 msgid "Important dates"
30 30 msgstr "Datas importantes"
31 31  
32   -#: accounts/forms.py:22
  32 +#: accounts/forms.py:24
33 33 msgid "Social account does not exist"
34 34 msgstr "Conta social não existe"
35 35  
36   -#: accounts/forms.py:63
  36 +#: accounts/forms.py:65
37 37 msgid "Mailing lists"
38 38 msgstr "Listas de e-mail"
39 39  
40   -#: accounts/views.py:121
  40 +#: accounts/forms.py:72
  41 +msgid "Password"
  42 +msgstr "Senha"
  43 +
  44 +#: accounts/forms.py:74
  45 +msgid "Password confirmation"
  46 +msgstr "Confirmação de senha"
  47 +
  48 +#: accounts/forms.py:76
  49 +msgid "Enter the same password as above, for verification."
  50 +msgstr "Digite a mesma senha que acima, para verificação."
  51 +
  52 +#: accounts/forms.py:94
  53 +msgid "Password mismatch"
  54 +msgstr "Senhas diferentes"
  55 +
  56 +#: accounts/views.py:127
41 57 msgid "Your profile has been created!"
42 58 msgstr "Seu perfil foi criado!"
43 59  
44   -#: accounts/views.py:122
  60 +#: accounts/views.py:128
45 61 msgid ""
46 62 "You must login to validated your profile. Profiles not validated are deleted "
47 63 "in 24h."
... ... @@ -49,11 +65,29 @@ msgstr &quot;&quot;
49 65 "Você deve se logar para validar seu perfil. Perfis não validados serão "
50 66 "deletados em 24h."
51 67  
  68 +#: accounts/views.py:209
  69 +msgid "Could not change your password. Please, try again later."
  70 +msgstr ""
  71 +"Não conseguimos alterar sua senha. Por favor, tente novamente mais tarde."
  72 +
  73 +#: accounts/views.py:218
  74 +msgid "You've changed your password successfully!"
  75 +msgstr "Senha alterada com sucesso!"
  76 +
52 77 #: accounts/management/commands/delete_invalid.py:42
53 78 #, python-format
54 79 msgid "%(count)s users deleted."
55 80 msgstr "%(count)s usuários deletados."
56 81  
  82 +#: accounts/templates/accounts/change_password.html:8
  83 +msgid "Change XMPP Client and SVN Password"
  84 +msgstr "Trocar senha do SVN e do Mensageiro"
  85 +
  86 +#: accounts/templates/accounts/change_password.html:17
  87 +#: accounts/templates/accounts/user_update_form.html:191
  88 +msgid "Change Password"
  89 +msgstr "Trocar senha"
  90 +
57 91 #: accounts/templates/accounts/manage_subscriptions.html:6
58 92 msgid "Mailing List Subscriptions"
59 93 msgstr "Inscrições em listas de e-mails"
... ... @@ -195,7 +229,19 @@ msgstr &quot;Adicionar outro endereço de e-mail&quot;
195 229 msgid "Add"
196 230 msgstr "Adicionar"
197 231  
198   -#: accounts/templates/accounts/user_update_form.html:184
  232 +#: accounts/templates/accounts/user_update_form.html:185
  233 +msgid "Change SVN and XMPP Client password"
  234 +msgstr "Trocar a senha do SVN e do Mensageiro"
  235 +
  236 +#: accounts/templates/accounts/user_update_form.html:189
  237 +msgid ""
  238 +"You don't need to change this password. Change it only if you really know "
  239 +"what you are doing."
  240 +msgstr ""
  241 +"Você não precisa trocar essa senha. Troque-a somente se tem certeza do que "
  242 +"está fazendo."
  243 +
  244 +#: accounts/templates/accounts/user_update_form.html:199
199 245 msgid "Update"
200 246 msgstr "Atualizar"
201 247  
... ...
src/proxy/diazo/jenkins.xml 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +<rules
  2 + xmlns="http://namespaces.plone.org/diazo"
  3 + xmlns:css="http://namespaces.plone.org/diazo/css"
  4 + xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  5 +
  6 + <before theme-children="/html/head" content-children="/html/head" />
  7 + <before css:theme-children="#main-content" css:content-children="body" />
  8 +
  9 + <merge attributes="class" css:theme="body" css:content="body" />
  10 + <drop css:content="#top-panel" />
  11 +
  12 + <after theme-children="/html/head">
  13 + <script>jQuery.noConflict();</script>
  14 + <style>
  15 + #breadcrumbs {
  16 + border: 0 !important;
  17 + }
  18 +
  19 + #right-top-nav {
  20 + margin-right: 5em !important;
  21 + }
  22 + </style>
  23 + </after>
  24 +
  25 +</rules>
... ...
src/proxy/migrations/0004_replace_wiki_view.py 0 → 100644
... ... @@ -0,0 +1,102 @@
  1 +# -*- coding: utf-8 -*-
  2 +import datetime
  3 +from django.db import connections
  4 +from south.db import db
  5 +from south.v2 import DataMigration
  6 +from django.db import models
  7 +
  8 +class Migration(DataMigration):
  9 +
  10 + def forwards(self, orm):
  11 + # Selecting trac database
  12 + connection = connections['trac']
  13 +
  14 + cursor = connection.cursor()
  15 +
  16 + cursor.execute('''
  17 + CREATE OR REPLACE VIEW wiki_view AS SELECT
  18 + wiki.name AS name,
  19 + (SELECT wiki2.text FROM wiki AS wiki2 WHERE wiki2.name = wiki.name
  20 + AND wiki2.version = MAX(wiki.version)) AS wiki_text,
  21 + (SELECT wiki3.author FROM wiki AS wiki3 WHERE wiki3.name = wiki.name
  22 + AND wiki3.version = 1) AS author,
  23 + string_agg(DISTINCT wiki.author, ', ') AS collaborators,
  24 + TIMESTAMP WITH TIME ZONE 'epoch' + (MIN(wiki.time)/1000000) * INTERVAL '1s' AS created,
  25 + TIMESTAMP WITH TIME ZONE 'epoch' + (MAX(wiki.time)/1000000) * INTERVAL '1s' AS modified
  26 + FROM wiki
  27 + GROUP BY wiki.name;
  28 + ''')
  29 +
  30 + def backwards(self, orm):
  31 + # Selecting trac database
  32 + connection = connections['trac']
  33 +
  34 + cursor = connection.cursor()
  35 +
  36 + cursor.execute('''
  37 + CREATE OR REPLACE VIEW wiki_view AS SELECT
  38 + wiki.name AS name,
  39 + (SELECT wiki2.text FROM wiki AS wiki2 WHERE wiki2.name = wiki.name
  40 + AND wiki2.version = MAX(wiki.version)) AS wiki_text,
  41 + (SELECT wiki3.author FROM wiki AS wiki3 WHERE wiki3.name = wiki.name
  42 + AND wiki3.version = 1) AS author,
  43 + string_agg(DISTINCT wiki.author, ', ') AS collaborators,
  44 + TIMESTAMP WITH TIME ZONE 'epoch' + (MAX(wiki.time)/1000000) * INTERVAL '1s' AS created,
  45 + TIMESTAMP WITH TIME ZONE 'epoch' + (MIN(wiki.time)/1000000) * INTERVAL '1s' AS modified
  46 + FROM wiki
  47 + GROUP BY wiki.name;
  48 + ''')
  49 +
  50 + models = {
  51 + u'proxy.attachment': {
  52 + 'Meta': {'object_name': 'Attachment', 'db_table': "'attachment_view'", 'managed': 'False'},
  53 + 'attach_id': ('django.db.models.fields.TextField', [], {}),
  54 + 'author': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  55 + 'created': ('django.db.models.fields.DateTimeField', [], {'blank': 'True'}),
  56 + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  57 + 'filename': ('django.db.models.fields.TextField', [], {}),
  58 + 'mimetype': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  59 + 'size': ('django.db.models.fields.IntegerField', [], {'blank': 'True'}),
  60 + 'url': ('django.db.models.fields.TextField', [], {'primary_key': 'True'}),
  61 + 'used_by': ('django.db.models.fields.TextField', [], {})
  62 + },
  63 + u'proxy.revision': {
  64 + 'Meta': {'object_name': 'Revision', 'db_table': "'revision_view'", 'managed': 'False'},
  65 + 'author': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  66 + 'created': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
  67 + 'key': ('django.db.models.fields.TextField', [], {'primary_key': 'True'}),
  68 + 'message': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  69 + 'repository_name': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  70 + 'rev': ('django.db.models.fields.TextField', [], {'blank': 'True'})
  71 + },
  72 + u'proxy.ticket': {
  73 + 'Meta': {'object_name': 'Ticket', 'db_table': "'ticket_view'", 'managed': 'False'},
  74 + 'author': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  75 + 'collaborators': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  76 + 'component': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  77 + 'created': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
  78 + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  79 + 'id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}),
  80 + 'keywords': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  81 + 'milestone': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  82 + 'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
  83 + 'priority': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  84 + 'reporter': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  85 + 'severity': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  86 + 'status': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  87 + 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  88 + 'version': ('django.db.models.fields.TextField', [], {'blank': 'True'})
  89 + },
  90 + u'proxy.wiki': {
  91 + 'Meta': {'object_name': 'Wiki', 'db_table': "'wiki_view'", 'managed': 'False'},
  92 + 'author': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  93 + 'collaborators': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
  94 + 'created': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
  95 + 'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
  96 + 'name': ('django.db.models.fields.TextField', [], {'primary_key': 'True'}),
  97 + 'wiki_text': ('django.db.models.fields.TextField', [], {'blank': 'True'})
  98 + }
  99 + }
  100 +
  101 + complete_apps = ['proxy']
  102 + symmetrical = True
... ...
src/search/forms.py
... ... @@ -101,6 +101,8 @@ class ColabSearchForm(SearchForm):
101 101 'q.alt': '*.*',
102 102 'pf': 'title^2.1 author^1.9 description^1.7',
103 103 'mm': '2<70%',
  104 +
  105 + # Date boosting: http://wiki.apache.org/solr/FunctionQuery#Date_Boosting
104 106 'bf': 'recip(ms(NOW/HOUR,modified),3.16e-11,1,1)^10',
105 107 }
106 108  
... ...
src/super_archives/search_indexes.py
... ... @@ -25,6 +25,9 @@ class ThreadIndex(BaseIndex, indexes.Indexable):
25 25 model_attr='mailinglist__get_absolute_url',
26 26 indexed=False,
27 27 )
  28 + latest_message_pk = indexes.IntegerField(
  29 + model_attr='latest_message__pk', indexed=False
  30 + )
28 31 score = indexes.IntegerField(model_attr='score')
29 32  
30 33 def get_model(self):
... ...
src/super_archives/templates/message-preview.html
... ... @@ -10,7 +10,7 @@
10 10 {% endif %}
11 11  
12 12 {% if result.title %}
13   - <a href="{{ result.url }}" {% if result.latest_description %}title="{{ result.latest_description|escape|truncatechars:150 }}"{% elif result.description %}title="{{ result.description|escape|truncatechars:150 }}"{% endif %}>
  13 + <a href="{{ result.url }}{% if result.type == 'thread' and result.latest_message_pk %}#msg-{{ result.latest_message_pk }}{% elif result.type == 'thread' and result.pk %}#msg-{{ result.pk }}{% endif %}" {% if result.latest_description %}title="{{ result.latest_description|escape|truncatechars:150 }}"{% elif result.description %}title="{{ result.description|escape|truncatechars:150 }}"{% endif %}>
14 14 <span class="subject">
15 15 <!-- a striptags filter was raising an error here because using with highlight -->
16 16 {% if query %}
... ...
src/tz/middleware.py
... ... @@ -10,11 +10,11 @@ class TimezoneMiddleware(object):
10 10  
11 11 try:
12 12 offset = int(offset) * -1
  13 + tz = pytz.FixedOffset(offset)
13 14 except ValueError:
14 15 offset = 0
15 16  
16 17 if offset:
17   - tz = pytz.FixedOffset(offset)
18 18 timezone.activate(tz)
19 19 else:
20 20 timezone.deactivate()
... ...
src/tz/templates/tz/set_utc_offset.html
1 1 <script type="text/javascript">
2 2 var date = new Date();
3   - $.cookie('utc_offset', date.getTimezoneOffset(), { path: '/' });
  3 + jQuery.cookie('utc_offset', date.getTimezoneOffset(), { path: '/' });
4 4 </script>
... ...