Commit 8068038a78e7740d61c0465d74d40f118e339585

Authored by Sergio Oliveira
2 parents f1e92a83 cb01d435

Merge branch 'fix_master'

.gitignore
... ... @@ -23,6 +23,7 @@ coverage_report/
23 23  
24 24 # Whoosh Index
25 25 tests/whoosh_index/
  26 +colab/whoosh_index/
26 27  
27 28 # Open Build System
28 29 .obs/
... ...
.travis.yml
... ... @@ -19,3 +19,11 @@ script:
19 19  
20 20 after_success:
21 21 - coveralls
  22 +
  23 +notifications:
  24 + irc:
  25 + channels:
  26 + - "chat.freenode.net#colab"
  27 + on_success: change
  28 + on_failure: always
  29 + use_notice: true
... ...
... ... @@ -1,57 +0,0 @@
1   -TODO
2   ------
3   -
4   -Emails
5   -=======
6   -* Atualizar versão do Mailman
7   -* Sincronizar informações de membership do mailman com o django
8   -* Usar informações do banco de dados local ao inves de fazer queries constantes
9   -* Logar erros ao falhar inscricao ou remocao de listas
10   -* Processo de cadastro em listas com moderacao
11   -* Permitir moderacao de mensagens pelo Colab
12   -* Permitir a gestao de listas pelo Colab
13   -* Não perder o email em caso de falha de envio. Exibir o erro mas trazer a mensagem de volta para o usuário
14   -* Permitir apenas que usuarios pertencentes a lista enviem mensagens
15   -
16   -
17   -
18   -Async
19   -=====
20   -* Usar celery para tornar tasks como envio de emails asincronas.
21   -
22   -
23   -Planet
24   -======
25   -
26   -* Paginator esta quebrado em telas xs
27   -
28   -
29   -Chat
30   -====
31   -
32   -* Permitir que usuario altere senha
33   -
34   -
35   -Interface
36   -=========
37   -
38   -* Utilizar paginador do bootstrap 3 em todas as telas
39   -* Implementar breadcrumbs
40   -* Melhorar filtros para interfaces móveis
41   -* Paginar discussoes
42   -* Paginar dashboard de discussoes
43   -* Highlight mostrar resultados normalizados (com e sem acentos)
44   -* Adicionar opção para escolha de idioma
45   -
46   -
47   -Outros
48   -=======
49   -
50   -* Contabilizar votos dentro do modelo de mensagem. Com a implementação atual ordenar uma thread por votos é uma operação muito cara.
51   -* Utilizar SOLR para listar documentos relevantes ao inves de thread.score
52   -* Fazer thread querysets ter um objeto (most_relevant_message)
53   -* BUG: alguns subjects comecam e terminam com [] fazendo com que a RE de limpeza apague todo o subject.
54   -* BUG: mensagens importadas por listas erradas
55   -* Migração e reorganização do conteúdo do trac/wiki para o novo colab
56   -* Indice criado manualmente. Automatizar:
57   - * create index super_archives_message_body_idx ON super_archives_message ((substring(body,0,1024)));
Vagrantfile
... ... @@ -6,10 +6,10 @@
6 6 # - trusty64
7 7 # - chef/centos-7.0
8 8  
9   -default_box = "precise64"
  9 +default_box = "trusty64"
10 10 if $stdin.isatty
11 11 if Dir.glob(File.join(File.dirname("__FILE__"), '.vagrant/**/id')).empty?
12   - options = ["precise64", "trusty64", "chef/centos-7.0"]
  12 + options = ["trusty64", "chef/centos-7.0"]
13 13  
14 14 puts "Bases boxes available locally:"
15 15 puts '------------------------------'
... ...
colab/accounts/templates/accounts/user_detail.html
1 1 {% extends "base.html" %}
2 2  
3   -{% load i18n gravatar i18n_model %}
  3 +{% load i18n gravatar %}
4 4  
5 5 {% block title %}Perfil{% endblock %}
6 6  
... ... @@ -126,25 +126,6 @@
126 126 </div>
127 127 </div>
128 128  
129   -
130   - {% if user_.badge_set.exists %}
131   - <div class="col-lg-8 col-md-12 col-sm-7">
132   - <div class="panel panel-default">
133   - <div class="panel-heading">
134   - <h3 class="panel-title">{% trans "Badges" %}</h3>
135   - </div>
136   - <div class="panel-body">
137   - <div>
138   - {% for badge in user_.badge_set.all %}
139   - {% translate badge as badge_trans %}
140   - <img src="data:image/png;base64,{{ badge.image_base64 }}" title="({{ badge_trans.title }}) {{ badge_trans.description }}" />
141   - {% endfor %}
142   - </div>
143   - </div>
144   - </div>
145   - </div>
146   - {% endif %}
147   -
148 129 </div> <!-- End of user-profile row -->
149 130  
150 131 <div class="row">
... ...
colab/api/__init__.py
colab/api/resources.py
... ... @@ -1,120 +0,0 @@
1   -# -*- coding: utf-8 -*-
2   -
3   -from django.contrib.auth import get_user_model
4   -
5   -from tastypie import fields
6   -from tastypie.constants import ALL_WITH_RELATIONS, ALL
7   -from tastypie.resources import ModelResource
8   -
9   -from colab.super_archives.models import Message, EmailAddress
10   -# from proxy.trac.models import Revision, Ticket, Wiki
11   -
12   -User = get_user_model()
13   -
14   -
15   -class UserResource(ModelResource):
16   - class Meta:
17   - queryset = User.objects.filter(is_active=True)
18   - resource_name = 'user'
19   - fields = ['username', 'institution', 'role', 'bio', 'first_name',
20   - 'last_name', 'email']
21   - allowed_methods = ['get', ]
22   - filtering = {
23   - 'email': ('exact', ),
24   - 'username': ALL,
25   - 'institution': ALL,
26   - 'role': ALL,
27   - 'bio': ALL,
28   - }
29   -
30   - def dehydrate_email(self, bundle):
31   - return ''
32   -
33   -
34   -class EmailAddressResource(ModelResource):
35   - user = fields.ForeignKey(UserResource, 'user', full=False, null=True)
36   -
37   - class Meta:
38   - queryset = EmailAddress.objects.all()
39   - resource_name = 'emailaddress'
40   - excludes = ['md5', ]
41   - allowed_methods = ['get', ]
42   - filtering = {
43   - 'address': ('exact', ),
44   - 'user': ALL_WITH_RELATIONS,
45   - 'real_name': ALL,
46   - }
47   -
48   - def dehydrate_address(self, bundle):
49   - return ''
50   -
51   -
52   -class MessageResource(ModelResource):
53   - from_address = fields.ForeignKey(EmailAddressResource, 'from_address',
54   - full=False)
55   -
56   - class Meta:
57   - queryset = Message.objects.all()
58   - resource_name = 'message'
59   - excludes = ['spam', 'subject_clean', 'message_id']
60   - filtering = {
61   - 'from_address': ALL_WITH_RELATIONS,
62   - 'subject': ALL,
63   - 'body': ALL,
64   - 'received_time': ALL,
65   - }
66   -
67   -
68   -# class RevisionResource(ModelResource):
69   -# class Meta:
70   -# queryset = Revision.objects.all()
71   -# resource_name = 'revision'
72   -# excludes = ['collaborators', ]
73   -# filtering = {
74   -# 'key': ALL,
75   -# 'rev': ALL,
76   -# 'author': ALL,
77   -# 'message': ALL,
78   -# 'repository_name': ALL,
79   -# 'created': ALL,
80   -# }
81   -#
82   -#
83   -# class TicketResource(ModelResource):
84   -# class Meta:
85   -# queryset = Ticket.objects.all()
86   -# resource_name = 'ticket'
87   -# excludes = ['collaborators', ]
88   -# filtering = {
89   -# 'id': ALL,
90   -# 'summary': ALL,
91   -# 'description': ALL,
92   -# 'milestone': ALL,
93   -# 'priority': ALL,
94   -# 'component': ALL,
95   -# 'version': ALL,
96   -# 'severity': ALL,
97   -# 'reporter': ALL,
98   -# 'author': ALL,
99   -# 'status': ALL,
100   -# 'keywords': ALL,
101   -# 'created': ALL,
102   -# 'modified': ALL,
103   -# 'modified_by': ALL,
104   -# }
105   -#
106   -#
107   -# class WikiResource(ModelResource):
108   -# class Meta:
109   -# queryset = Wiki.objects.all()
110   -# resource_name = 'wiki'
111   -# excludes = ['collaborators', ]
112   -# filtering = {
113   -# 'name': ALL,
114   -# 'wiki_text': ALL,
115   -# 'author': ALL,
116   -# 'name': ALL,
117   -# 'created': ALL,
118   -# 'modified': ALL,
119   -# 'modified_by': ALL,
120   -# }
colab/api/urls.py
... ... @@ -1,22 +0,0 @@
1   -# -*- coding: utf-8 -*-
2   -
3   -from django.conf.urls import patterns, include, url
4   -
5   -from tastypie.api import Api
6   -
7   -from .resources import (UserResource, EmailAddressResource, MessageResource)
8   -from .views import VoteView
9   -
10   -
11   -api = Api(api_name='v1')
12   -api.register(UserResource())
13   -api.register(EmailAddressResource())
14   -api.register(MessageResource())
15   -
16   -
17   -urlpatterns = patterns('',
18   - url(r'message/(?P<msg_id>\d+)/vote$', VoteView.as_view()),
19   -
20   - # tastypie urls
21   - url(r'', include(api.urls)),
22   -)
colab/api/views.py
... ... @@ -1,45 +0,0 @@
1   -
2   -from django import http
3   -from django.db import IntegrityError
4   -from django.views.generic import View
5   -from django.core.exceptions import ObjectDoesNotExist
6   -
7   -
8   -from colab.super_archives.models import Message
9   -
10   -
11   -class VoteView(View):
12   -
13   - http_method_names = [u'get', u'put', u'delete', u'head']
14   -
15   - def put(self, request, msg_id):
16   - if not request.user.is_authenticated():
17   - return http.HttpResponseForbidden()
18   -
19   - try:
20   - Message.objects.get(id=msg_id).vote(request.user)
21   - except IntegrityError:
22   - # 409 Conflict
23   - # used for duplicated entries
24   - return http.HttpResponse(status=409)
25   -
26   - # 201 Created
27   - return http.HttpResponse(status=201)
28   -
29   - def get(self, request, msg_id):
30   - votes = Message.objects.get(id=msg_id).votes_count()
31   - return http.HttpResponse(votes, content_type='application/json')
32   -
33   - def delete(self, request, msg_id):
34   - if not request.user.is_authenticated():
35   - return http.HttpResponseForbidden()
36   -
37   - try:
38   - Message.objects.get(id=msg_id).unvote(request.user)
39   - except ObjectDoesNotExist:
40   - return http.HttpResponseGone()
41   -
42   - # 204 No Content
43   - # empty body, as per RFC2616.
44   - # object deleted
45   - return http.HttpResponse(status=204)
colab/management/initconfig.py
... ... @@ -32,12 +32,17 @@ ALLOWED_HOSTS = [
32 32 # SOCIAL_NETWORK_ENABLED = True
33 33  
34 34 ## Database settings
35   -DATABASES = {{
36   - 'default': {{
37   - 'ENGINE': 'django.db.backends.sqlite3',
38   - 'NAME': 'colab.sqlite3',
39   - }}
40   -}}
  35 +##
  36 +## When DEBUG is True colab will create the DB on
  37 +## the repository root. In case of production settings
  38 +## (DEBUG False) the DB settings must be set.
  39 +##
  40 +# DATABASES = {{
  41 +# 'default': {{
  42 +# 'ENGINE': 'django.db.backends.sqlite3',
  43 +# 'NAME': '/path/to/colab.sqlite3',
  44 +# }}
  45 +# }}
41 46  
42 47 ## Disable indexing
43 48 ROBOTS_NOINDEX = False
... ...
colab/settings.py
... ... @@ -9,6 +9,7 @@ https://docs.djangoproject.com/en/1.7/ref/settings/
9 9 """
10 10  
11 11 BROKER_URL = 'amqp://guest:guest@localhost:5672/'
  12 +
12 13 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
13 14 import os
14 15 BASE_DIR = os.path.dirname(__file__)
... ... @@ -43,10 +44,8 @@ INSTALLED_APPS = (
43 44 'colab.accounts',
44 45  
45 46 # Not standard apps
46   - 'django_mobile',
47 47 'haystack',
48 48 'hitcounter',
49   - 'i18n_model',
50 49 'taggit',
51 50 'djcelery',
52 51  
... ... @@ -54,10 +53,10 @@ INSTALLED_APPS = (
54 53 'colab.home',
55 54 'colab.plugins',
56 55 'colab.super_archives',
57   - 'colab.api',
58 56 'colab.rss',
59 57 'colab.search',
60 58 'colab.tz',
  59 + 'colab.utils',
61 60 )
62 61  
63 62 ROOT_URLCONF = 'colab.urls'
... ... @@ -168,7 +167,15 @@ HAYSTACK_CUSTOM_HIGHLIGHTER = &#39;colab.utils.highlighting.ColabHighlighter&#39;
168 167 HAYSTACK_CONNECTIONS = {
169 168 'default': {
170 169 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
171   - 'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
  170 + 'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
  171 + }
  172 +}
  173 +
  174 +DEFAULT_DATABASE = os.path.join(BASE_DIR, 'colab.sqlite3')
  175 +DATABASES = {
  176 + 'default': {
  177 + 'ENGINE': 'django.db.backends.sqlite3',
  178 + 'NAME': DEFAULT_DATABASE,
172 179 }
173 180 }
174 181  
... ... @@ -183,7 +190,6 @@ TEMPLATE_CONTEXT_PROCESSORS = (
183 190 'django.core.context_processors.tz',
184 191 'django.contrib.messages.context_processors.messages',
185 192 'django.core.context_processors.request',
186   - 'django_mobile.context_processors.is_mobile',
187 193 'colab.super_archives.context_processors.mailarchive',
188 194 'colab.plugins.context_processors.colab_apps',
189 195 'colab.home.context_processors.robots',
... ... @@ -200,8 +206,6 @@ MIDDLEWARE_CLASSES = (
200 206 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
201 207 'django.contrib.messages.middleware.MessageMiddleware',
202 208 'django.middleware.clickjacking.XFrameOptionsMiddleware',
203   - 'django_mobile.middleware.MobileDetectionMiddleware',
204   - 'django_mobile.middleware.SetFlavourMiddleware',
205 209 'colab.tz.middleware.TimezoneMiddleware',
206 210 )
207 211  
... ... @@ -245,12 +249,12 @@ REVPROXY_ADD_REMOTE_USER = True
245 249 # Tastypie settings
246 250 TASTYPIE_DEFAULT_FORMATS = ['json', ]
247 251  
248   -from .utils.conf import load_colab_apps, load_py_settings
  252 +from .utils import conf
249 253  
250 254 SOCIAL_NETWORK_ENABLED = locals().get('SOCIAL_NETWORK_ENABLED') or False
251 255  
252   -locals().update(load_colab_apps())
253   -locals().update(load_py_settings())
  256 +locals().update(conf.load_colab_apps())
  257 +locals().update(conf.load_py_settings())
254 258  
255 259 COLAB_APPS = locals().get('COLAB_APPS') or {}
256 260 PROXIED_APPS = {}
... ... @@ -290,3 +294,5 @@ STATICFILES_DIRS += [
290 294 TEMPLATE_DIRS += (
291 295 os.path.join(BASE_DIR, 'templates'),
292 296 )
  297 +
  298 +conf.validate_database(DATABASES, DEFAULT_DATABASE, DEBUG)
... ...
colab/static/css/screen.css
... ... @@ -275,88 +275,6 @@ ul.unstyled-list .glyphicon-chevron-right {
275 275 border-color: #999;
276 276 }
277 277  
278   -/* Converse JS */
279   -
280   -#chatpanel form#converse-login {
281   - padding: 0;
282   -}
283   -
284   -#chatpanel form#converse-login input,
285   -#chatpanel form#converse-login label {
286   - margin: 2px 10px;
287   -}
288   -
289   -#chatpanel form#converse-login .login-submit{
290   - margin-top: 10px;
291   -}
292   -
293   -#chatpanel a.configure-chatroom-button,
294   -#chatpanel a.close-chatbox-button {
295   - background-color: #F6F6F6;
296   - margin-right: 0.6em;
297   -}
298   -
299   -#chatpanel .chat-head #controlbox-tabs li {
300   - width:32%;
301   -}
302   -
303   -#chatpanel .oc-chat-head {
304   - height: 37px;
305   -}
306   -
307   -#chatpanel .chatbox dl.dropdown {
308   - margin-top: 0;
309   -}
310   -
311   -#chatpanel #converse-roster {
312   - height: 207px;
313   -}
314   -
315   -#chatpanel #toggle-controlbox {
316   - font-size: 100% !important;
317   - padding: 0 4px 26px !important;
318   -}
319   -
320   -#chatpanel {
321   - height: 342px !important;
322   -}
323   -
324   -#chatpanel div#controlbox-panes {
325   - width: auto;
326   -}
327   -
328   -#chatpanel .chat-head-chatbox, #chatpanel .chat-head-chatroom {
329   - height: 40.5px;
330   -}
331   -
332   -#chatpanel form.sendXMPPMessage {
333   - height: 76px;
334   - border: none;
335   -}
336   -
337   -#chatpanel form.sendXMPPMessage textarea {
338   - width: 189px;
339   - max-width: 285px;
340   - max-height: 66px;
341   -}
342   -
343   -#chatpanel .chatbox form.sendXMPPMessage textarea {
344   - max-width: 189px;
345   -}
346   -
347   -#chatpanel .chat-content,
348   -#chatpanel .chatroom .participants{
349   - width: auto;
350   -}
351   -
352   -#chatpanel div#chatrooms {
353   - overflow-y: visible;
354   -}
355   -
356   -
357   -/* End of Converse JS*/
358   -
359   -
360 278 /* user profile update */
361 279  
362 280 .btn.delete-email:not(:hover) {
... ...
colab/static/img/changeset.png

158 Bytes

colab/static/img/opendata3.png

296 Bytes

colab/static/img/thread.png

208 Bytes

colab/static/img/ticket.png

270 Bytes

colab/static/img/wiki.png

202 Bytes

colab/super_archives/templates/message-thread.html
... ... @@ -59,7 +59,7 @@
59 59 console.debug('trying to vote');
60 60 $btn.button('loading');
61 61 $ajax = $.ajax({
62   - url: "/api/message/" + msg_id + "/vote",
  62 + url: "/archives/message/" + msg_id + "/vote",
63 63 type: method,
64 64 context: $btn.get(0),
65 65 beforeSend: function(xhr, settings) {
... ...
colab/super_archives/urls.py
1 1 from django.conf.urls import patterns, url
2 2  
3   -from .views import EmailView, EmailValidationView, ThreadView
4   -from .views import ThreadDashboardView
  3 +from .views import (EmailView, EmailValidationView, ThreadView,
  4 + ThreadDashboardView, VoteView)
5 5  
6 6  
7 7 urlpatterns = patterns(
... ... @@ -13,4 +13,5 @@ urlpatterns = patterns(
13 13 name="archive_email_validation_view"),
14 14 url(r'manage/email/(?P<key>[0-9a-z]{32})?', EmailView.as_view(),
15 15 name="archive_email_view"),
  16 + url(r'message/(?P<msg_id>\d+)/vote$', VoteView.as_view()),
16 17 )
... ...
colab/super_archives/views.py
... ... @@ -32,6 +32,8 @@ class ThreadView(View):
32 32 thread = get_object_or_404(Thread, subject_token=thread_token,
33 33 mailinglist__name=mailinglist)
34 34  
  35 + # TODO: Refactor this code
  36 + # Use local flag is_private instead of always check the API!!
35 37 all_privates = dict(mailman.all_lists(private=True))
36 38 if all_privates[thread.mailinglist.name]:
37 39 if not request.user.is_authenticated():
... ... @@ -294,3 +296,40 @@ class EmailValidationView(View):
294 296 return http.HttpResponseServerError()
295 297  
296 298 return http.HttpResponse(status=204)
  299 +
  300 +
  301 +class VoteView(View):
  302 +
  303 + http_method_names = [u'get', u'put', u'delete', u'head']
  304 +
  305 + def put(self, request, msg_id):
  306 + if not request.user.is_authenticated():
  307 + return http.HttpResponseForbidden()
  308 +
  309 + try:
  310 + Message.objects.get(id=msg_id).vote(request.user)
  311 + except IntegrityError:
  312 + # 409 Conflict
  313 + # used for duplicated entries
  314 + return http.HttpResponse(status=409)
  315 +
  316 + # 201 Created
  317 + return http.HttpResponse(status=201)
  318 +
  319 + def get(self, request, msg_id):
  320 + votes = Message.objects.get(id=msg_id).votes_count()
  321 + return http.HttpResponse(votes, content_type='application/json')
  322 +
  323 + def delete(self, request, msg_id):
  324 + if not request.user.is_authenticated():
  325 + return http.HttpResponseForbidden()
  326 +
  327 + try:
  328 + Message.objects.get(id=msg_id).unvote(request.user)
  329 + except ObjectDoesNotExist:
  330 + return http.HttpResponseGone()
  331 +
  332 + # 204 No Content
  333 + # empty body, as per RFC2616.
  334 + # object deleted
  335 + return http.HttpResponse(status=204)
... ...
colab/templates/footer.html
... ... @@ -6,7 +6,6 @@
6 6 <p class="col-lg-12 text-center">{% trans "Last email imported at" %} {{ last_imported_message.received_time|date:'DATETIME_FORMAT' }}</p>
7 7 {% endif %}
8 8 <p class="col-lg-12 text-center">
9   - <a href="{% url 'opendata' %}"><img src="{% static 'img/opendata3.png' %}"/></a>
10 9 <a href="http://creativecommons.org/licenses/by-sa/4.0/deed.pt_BR"><img src="{% static 'img/cc_by_sa.png' %}"/></a>
11 10 </p>
12 11 <p class="col-lg-12 text-center">
... ...
colab/templates/open-data.html
... ... @@ -1,144 +0,0 @@
1   -{% extends "base.html" %}
2   -{% load i18n %}
3   -
4   -{% block main-content %}
5   - <div class="col-lg-12">
6   - <h2>{% trans "OpenData - Communities" %}</h2>
7   - <p>{% trans "If you are interested in any other data that is not provided by this API, please contact us via the ticketing system (you must be registered in order to create a ticket)." %}</p>
8   -
9   - <h3>{% trans "Retrieving data via API" %}</h3>
10   - <p>{% trans "Colab API works through HTTP/REST, always returning JSON objects." %}</p>
11   - <p>
12   - {% trans "The base API URL is" %}:
13   - {% url 'api_v1_top_level' api_name='v1' as BASE_API_URL %}
14   - <a href="{{ BASE_API_URL }}">{{ BASE_API_URL }}</a>
15   - </p>
16   -
17   - <ul class="list-unstyled">
18   - <li>
19   - <p>{% trans "Each model listed below has a resource_uri field available, which is the object's data URI." %}</p>
20   - <p>{% trans "The following list contains the available models to retrieve data and its fields available for filtering" %}:</p>
21   - <ul>
22   - <li>
23   - <strong><a href="{{ BASE_API_URL }}user/">user</a></strong>:
24   - {% trans "Fields" %}: <i>username, email, institution, role, first_name, last_name and bio</i>
25   - <p><strong>{% trans "The email field is not shown for user's privacy, but you can use it to filter" %}</strong></p>
26   - <ul>
27   - <li><i>username</i> - {% trans "The user's username" %}</li>
28   - <li><i>email</i> - {% trans "The user's email address" %}</li>
29   - <li><i>institution</i> - {% trans "What is the user's institution" %}</li>
30   - <li><i>role</i> - {% trans "What is the user's role" %}</li>
31   - <li><i>first_name</i> - {% trans "The user's first name" %}</li>
32   - <li><i>last_name</i> - {% trans "The user's last name" %}</li>
33   - <li><i>bio</i> - {% trans "A mini bio of the user" %}</li>
34   - </ul>
35   - <br />
36   - </li>
37   - <li>
38   - <strong><a href="{{ BASE_API_URL }}emailaddress/">emailaddress</a></strong>:
39   - {% trans "Fields" %}: <i>user, address and real_name</i>
40   - <p><strong>{% trans "The address field is not shown for user's privacy, but you can use it to filter" %}</strong></p>
41   - <ul>
42   - <li><i>user</i> - {% trans "It has a relationshop with the user described above" %}</li>
43   - <li><i>address</i> - {% trans "An email address" %}</li>
44   - <li><i>real_name</i> - {% trans "The user's real name" %}</li>
45   - </ul>
46   - <br />
47   - </li>
48   - <li>
49   - <strong><a href="{{ BASE_API_URL }}message/">message</a></strong>:
50   - {% trans "Fields" %}: <i>from_address, body, id, received_time and subject</i>
51   - <ul>
52   - <li><i>from_address</i> - {% trans "It has a relationship with the emailaddress described above" %}</li>
53   - <li><i>body</i> - {% trans "The message's body" %}</li>
54   - <li><i>subject</i> - {% trans "The message's subject" %}</li>
55   - <li><i>id</i> - {% trans "The message's id" %}</li>
56   - <li><i>received_time</i> - {% trans "The message's received time" %}</li>
57   - </ul>
58   - <br />
59   - </li>
60   - <li>
61   - <strong><a href="{{ BASE_API_URL }}revision/">revision</a></strong>:
62   - {% trans "Fields" %}: <i>author, created, key, message and repository_name</i>
63   - <ul>
64   - <li><i>author</i> - {% trans "The revision's author username" %}</li>
65   - <li><i>created</i> - {% trans "When the revision's were created" %}</li>
66   - <li><i>key</i> - {% trans "The revision's key" %}</li>
67   - <li><i>message</i> - {% trans "The revision's message" %}</li>
68   - <li><i>repository_name</i> - {% trans "The revision's repository name" %}</li>
69   - </ul>
70   - <br />
71   - </li>
72   - <li>
73   - <strong><a href="{{ BASE_API_URL }}ticket/">ticket</a></strong>:
74   - {% trans "Fields" %}: <i>author, component, created, description, id, keywords, milestone, modified, modified_by, priority, reporter, severity, status, summary and version</i>
75   - <ul>
76   - <li><i>author</i> - {% trans "The ticket's author username" %}</li>
77   - <li><i>component</i> - {% trans "The ticket's component" %}</li>
78   - <li><i>created</i> - {% trans "When the ticket's were created" %}</li>
79   - <li><i>description</i> - {% trans "The ticket's description" %}</li>
80   - <li><i>id</i> - {% trans "The ticket's id" %}</li>
81   - <li><i>keywords</i> - {% trans "The ticket's keywords" %}</li>
82   - <li><i>milestone</i> - {% trans "The ticket's milestone" %}</li>
83   - <li><i>modified</i> - {% trans "The time of the last modification" %}</li>
84   - <li><i>modified_by</i> - {% trans "The username of the last user who modified the ticket" %}</li>
85   - <li><i>priority</i> - {% trans "The ticket's priority" %}</li>
86   - <li><i>severity</i> - {% trans "The ticket's severity" %}</li>
87   - <li><i>status</i> - {% trans "The ticket's status" %}</li>
88   - <li><i>summary</i> - {% trans "The ticket's summary" %}</li>
89   - <li><i>version</i> - {% trans "The ticket's version" %}</li>
90   - </ul>
91   - <br />
92   - </li>
93   - <li>
94   - <strong><a href="{{ BASE_API_URL }}wiki/">wiki</a></strong>:
95   - {% trans "Fields" %}: <i>author, created, modified, modified_by, name and wiki_text</i>
96   - <ul>
97   - <li><i>author</i> - {% trans "The wiki's author username" %}</li>
98   - <li><i>created</i> - {% trans "When the wiki's were created" %}</li>
99   - <li><i>modified</i> - {% trans "The time of the last modification" %}</li>
100   - <li><i>modified_by</i> - {% trans "The username of the last user who modified the wiki" %}</li>
101   - <li><i>name</i> - {% trans "The wiki's name" %}</li>
102   - <li><i>wiki_text</i> - {% trans "the wiki's content" %}</li>
103   - </ul>
104   - </li>
105   - </ul>
106   - </li>
107   - </ul>
108   - <ul class="list-unstyled">
109   - <li><h3>{% trans 'Parameters' %}:</h3></li>
110   - <li class="divider"></li>
111   - <li>
112   - <h4><i>limit</i> - {% trans "Results per page" %}</h4>
113   - {% trans "Number of results to be displayed per page." %}
114   - <i>{% trans "Default: 20" %}</i>.
115   - </li>
116   - <li class="divider"></li>
117   - <li>
118   - <h4><i>offset</i> - {% trans "Starts of n element" %}</h4>
119   - {% trans "Where n is the index of the first result to appear in the page." %}
120   - <i>{% trans "Default: 0" %}</i>.
121   - </li>
122   - <li class="divider"><h3>{% trans "Filtering" %}:</h3></li>
123   - <li>
124   - <h4><i>fieldname</i> - {% trans "The field name" %}</h4>
125   - {% trans "If you are looking for a specific wiki, and you know the wiki's name, you can filter it as below" %}
126   - <p><i>...{{ BASE_API_URL }}wiki/?name={% trans "WikiName" %}</i></p>
127   - <p>{% trans "Where &quot;name&quot; is the fieldname and &quot;WikiName&quot; is the value you want to filter." %}</p>
128   - <h4>{% trans "Usage" %}</h4>
129   - <p>{% trans "You can also filter using Django lookup fields with the double underscores, just as below" %}</p>
130   - <p><i>...{{ BASE_API_URL }}wiki/?wiki_text__startswith={% trans "Wiki" %}</i></p>
131   - <p><i>...{{ BASE_API_URL }}ticket/?author__endswith={% trans "test" %}</i></p>
132   - <p><i>...{{ BASE_API_URL }}message/?body__contains={% trans "test" %}</i></p>
133   - <h4>{% trans "Usage with relationships" %}</h4>
134   - <p>{% trans "You can use related fields to filter too. So, you can filter by any field of emailaddress using the 'from_address' field of message, which has a relation to emailaddress. You will achieve the related fields by using double underscore and the field's name. See the example below" %}</p>
135   - <p><i>...{{ BASE_API_URL }}message/?from_address__real_name__contains=Name</i></p>
136   - <p>{% trans "So, real_name is a field of emailaddress, and you had access to this field by a message field called from_address and using double underscore to say you want to use a field of that relationship" %}</p>
137   - <p><strong>{% trans "Note: email filters must be exact. Which means that __contains, __startswith, __endswith and others won't work" %}</strong></p>
138   - <p>{% trans "Another example of usage with relations. Used to retrieve all messages of a given user, using the username or the email field" %}</p>
139   - <p><i>...{{ BASE_API_URL }}message/?from_address__user__username=username</i></p>
140   - <p><i>...{{ BASE_API_URL }}message/?from_address__user__email=usermailexample@host.com</i></p>
141   - </li>
142   - </ul>
143   - </div>
144   -{% endblock %}
colab/urls.py
... ... @@ -8,31 +8,19 @@ from django.views.generic import RedirectView
8 8 admin.autodiscover()
9 9  
10 10 urlpatterns = patterns('',
  11 + url(r'^$', RedirectView.as_view(url=settings.COLAB_HOME_URL), name='home'),
11 12 url(r'^robots.txt$', 'colab.home.views.robots', name='robots'),
12 13 url(r'^dashboard$', 'colab.home.views.dashboard', name='dashboard'),
13   - url(r'^$', RedirectView.as_view(url=settings.COLAB_HOME_URL), name='home'),
14   -
15   - url(r'^open-data/$', TemplateView.as_view(template_name='open-data.html'),
16   - name='opendata'),
17   -
18 14 url(r'^search/', include('colab.search.urls')),
19   - url(r'^archives/', include('colab.super_archives.urls')),
20   - url(r'^api/', include('colab.api.urls')),
21 15 url(r'^rss/', include('colab.rss.urls')),
22 16  
23   - # Kept for backwards compatibility
24   - url(r'^user/', include('colab.accounts.urls')),
25   - # Kept for backwards compatibility
26   - url(r'^user/', include('colab.accounts.urls')),
27   - # (same here) TODO: move to nginx
28   - url(r'^signup/', include('colab.accounts.urls')),
29 17 url(r'^account/', include('colab.accounts.urls')),
30 18 url(r'^myaccount/(?P<route>.*)$',
31 19 'colab.accounts.views.myaccount_redirect', name='myaccount'),
32 20  
33   - # Uncomment the next line to enable the admin:
34 21 url(r'^colab/admin/', include(admin.site.urls)),
35 22  
  23 + url(r'^archives/', include('colab.super_archives.urls')),
36 24 url(r'^gitlab/', include('colab.plugins.gitlab.urls')),
37 25 url(r'^mezuro/', include('colab.plugins.mezuro.urls')),
38 26 url(r'^social/', include('colab.plugins.noosfero.urls')),
... ...
colab/utils/conf.py
... ... @@ -13,6 +13,14 @@ class InaccessibleSettings(ImproperlyConfigured):
13 13 Check if the file exists and if you have read permissions."""
14 14  
15 15  
  16 +class DatabaseUndefined(ImproperlyConfigured):
  17 + """Default database is not set.
  18 +
  19 + When DEBUG is set to True a local sqlite database can be used for
  20 + developement porposes but otherwise the `default` database must
  21 + be set."""
  22 +
  23 +
16 24 def _load_py_file(py_path, path):
17 25 original_path = sys.path
18 26  
... ... @@ -94,3 +102,11 @@ def load_colab_apps():
94 102 COLAB_APPS[app_name][key] = value
95 103  
96 104 return {'COLAB_APPS': COLAB_APPS}
  105 +
  106 +
  107 +def validate_database(database_dict, default_db, debug):
  108 + db_name = database_dict.get('default', {}).get('NAME')
  109 + if not debug and db_name == default_db:
  110 + msg = ('Since DEBUG is set to False DATABASE must be set on '
  111 + 'Colab settings')
  112 + raise DatabaseUndefined(msg)
... ...
colab/utils/tests/__init__.py 0 → 100644
colab/utils/tests/test_conf.py 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +
  2 +from django.test import TestCase, override_settings
  3 +from django.conf import settings
  4 +
  5 +from ..conf import DatabaseUndefined, validate_database
  6 +
  7 +
  8 +class TestConf(TestCase):
  9 +
  10 + @override_settings(DEBUG=False, DATABASES={
  11 + 'default': {
  12 + 'NAME': settings.DEFAULT_DATABASE,
  13 + },
  14 + })
  15 + def test_database_undefined(self):
  16 + with self.assertRaises(DatabaseUndefined):
  17 + validate_database(settings.DATABASES, settings.DEFAULT_DATABASE,
  18 + settings.DEBUG)
... ...
misc/etc/cron.d/colab
... ... @@ -2,14 +2,8 @@
2 2  
3 3 34 2 * * * colab colab-admin rebuild_index --noinput &> /dev/null
4 4  
5   -0 * * * * colab colab-admin cleanup_snippets &> /dev/null
6   -
7   -*/5 * * * * colab colab-admin update_badges &> /dev/null
8   -
9 5 * * * * * colab colab-admin import_proxy_data &> /dev/null
10 6  
11 7 27 3 * * * colab colab-admin clearsessions &> /dev/null
12 8  
13   -10 * * * * colab colab-admin update_planet &> /dev/null
14   -
15 9 * * * * * colab colab-admin import_emails &> /dev/null
... ...
setup.py
... ... @@ -3,26 +3,23 @@ from setuptools import setup, find_packages
3 3  
4 4  
5 5 REQUIREMENTS = [
6   - 'Django>=1.7.8,<1.8',
  6 + 'Django>=1.7.9,<1.8',
7 7 'pytz>=2011n',
8 8 'django-hitcounter>=0.1.1',
9   - 'django-tastypie>=0.12.1',
10   - 'django-revproxy>=0.9.3',
  9 +
  10 + # Search
11 11 'django-haystack>=2.2',
12 12 'Whoosh>=2.7.0',
13 13  
14   - # Diazo
  14 + # revproxy
  15 + 'django-revproxy>=0.9.3',
15 16 'diazo>=1.0.5',
16 17  
17   - # Celery
  18 + # Async Signals
18 19 'django-celery==3.1.16',
19 20  
20 21 ### Move out of colab (as plugins):
21 22  
22   - # Deps for badger
23   - 'Pillow==2.8.1',
24   - 'django-i18n-model>=0.0.7',
25   -
26 23 # Deps for super_archives
27 24 'etiquetando==0.1',
28 25 'django-taggit>=0.12.1',
... ... @@ -32,10 +29,6 @@ REQUIREMENTS = [
32 29 # Deps for gitlab plugin
33 30 'python-dateutil>=1.5',
34 31 'requests',
35   -
36   - # Converse.js (XMPP client)
37   - 'django-conversejs>=0.3.4',
38   - 'django-mobile>=0.3.0',
39 32 ]
40 33  
41 34 TEST_REQUIREMENTS = [
... ...
tests/colab_settings.py
1 1  
2 2 ## Set to false in production
3   -DEBUG = False
  3 +DEBUG = True
4 4 TEMPLATE_DEBUG = False
5 5  
6 6 ## System admins
... ... @@ -25,15 +25,7 @@ ALLOWED_HOSTS = [
25 25 ]
26 26  
27 27 ### Uncomment to enable social networks fields profile
28   -# SOCIAL_NETWORK_ENABLED = True
29   -
30   -## Database settings
31   -DATABASES = {
32   - 'default': {
33   - 'ENGINE': 'django.db.backends.sqlite3',
34   - 'NAME': 'colab.sqlite3',
35   - }
36   -}
  28 +SOCIAL_NETWORK_ENABLED = True
37 29  
38 30 ## Disable indexing
39 31 ROBOTS_NOINDEX = True
... ... @@ -59,3 +51,5 @@ LOGGING = {
59 51 },
60 52 },
61 53 }
  54 +
  55 +STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
... ...
tests/run.py
... ... @@ -3,7 +3,7 @@
3 3 import os
4 4 import sys
5 5  
6   -os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings'
  6 +os.environ['DJANGO_SETTINGS_MODULE'] = 'colab.settings'
7 7 os.environ['COLAB_SETTINGS'] = 'tests/colab_settings.py'
8 8 os.environ['COLAB_PLUGINS'] = 'tests/plugins.d'
9 9 os.environ['COVERAGE_PROCESS_START'] = '.coveragerc'
... ...
tests/settings.py
... ... @@ -1,47 +0,0 @@
1   -from colab.settings import * # noqa
2   -
3   -SOCIAL_NETWORK_ENABLED = True
4   -STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
5   -
6   -LOGGING = {
7   - 'version': 1,
8   -
9   - 'handlers': {
10   - 'null': {
11   - 'level': 'DEBUG',
12   - 'class': 'logging.NullHandler',
13   - },
14   - },
15   -
16   - 'loggers': {
17   - 'colab.mailman': {
18   - 'handlers': ['null'],
19   - 'propagate': False,
20   - },
21   - 'haystack': {
22   - 'handlers': ['null'],
23   - 'propagate': False,
24   - },
25   - 'pysolr': {
26   - 'handlers': ['null'],
27   - 'propagate': False,
28   - },
29   - },
30   -}
31   -
32   -import os
33   -HAYSTACK_CONNECTIONS = {
34   - 'default': {
35   - 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
36   - 'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
37   - },
38   -}
39   -
40   -SECRET_KEY = 'not-a-secret'
41   -
42   -DATABASES = {
43   - 'default': {
44   - 'ENGINE': 'django.db.backends.sqlite3',
45   - 'NAME': 'colab.sqlite',
46   - }
47   -}
vagrant/centos.sh
... ... @@ -15,53 +15,16 @@ if [ -n &quot;$http_proxy&quot; ]; then
15 15 fi
16 16 fi
17 17  
18   -### Install dependencies
19   -
20   -yum -y groupinstall "Development tools"
21   -
22   -yum install -y git unzip mercurial libev-devel gettext libxml2-devel libxslt-devel openssl-devel libffi-devel libjpeg-turbo-devel zlib-devel freetype-devel postgresql-devel python-devel postgresql-server java epel-release
23 18  
24   -### Install Rabbitmq
25   -yum install -y rabbitmq-server
26   -systemctl start rabbitmq-server
27   -
28   -### Install Virtualenvwrapper
29   -which pip2.7 > /dev/null ||
30   - curl -s -L https://raw.githubusercontent.com/pypa/pip/1.5.6/contrib/get-pip.py |
31   - python2.7
32   -
33   -if [ ! -f /etc/profile.d/virtualenvwrapper.sh ]
34   -then
35   - pip install virtualenvwrapper
36   - cat > "/etc/profile.d/virtualenvwrapper.sh" <<EOF
37   -export VIRTUALENVWRAPPER_PYTHON="/usr/bin/python2.7"
38   -source /usr/bin/virtualenvwrapper.sh
39   -EOF
40   -fi
41   -
42   -### Create conf directory
43   -mkdir -p /etc/colab
44   -chown vagrant:vagrant /etc/colab
45   -
46   -
47   -## Configuring postgres
48   -if [ ! -f /var/lib/pgsql/data/pg_hba.conf.bkp ]; then
49   - service postgresql initdb
50   - cp /var/lib/pgsql/data/pg_hba.conf /var/lib/pgsql/data/pg_hba.conf.bkp
51   - echo "local all all ident" > /var/lib/pgsql/data/pg_hba.conf
52   - echo "host all all 127.0.0.1/32 md5" >> /var/lib/pgsql/data/pg_hba.conf
53   - echo "host all all ::1/128 md5" >> /var/lib/pgsql/data/pg_hba.conf
54   - service postgresql restart
55   -fi
  19 +### Install dependencies
56 20  
  21 +yum install -y epel-release
57 22  
58   -### Create colab user in PostgreSQL
59   -echo "CREATE USER colab WITH PASSWORD 'colab';" | sudo -u postgres -i psql 2> /dev/null || echo
60   -echo "ALTER USER colab CREATEDB;" | sudo -u postgres -i psql 2> /dev/null
  23 +yum -y groupinstall "Development tools"
61 24  
62   -### Create colab DB in PostgreSQL
63   -sudo -u postgres -i createdb --owner=colab colab 2> /dev/null | echo
  25 +yum install -y git unzip gettext libxml2-devel libxslt-devel openssl-devel libffi-devel python-devel python-pip python-virtualenvwrapper rabbitmq-server
64 26  
65   -### Forcing postgresql to start at boot
66   -sudo chkconfig postgresql on
67 27  
  28 +### Init Rabbitmq
  29 +chkconfig rabbitmq-server on
  30 +systemctl start rabbitmq-server
... ...
vagrant/provision.sh
... ... @@ -31,6 +31,10 @@ for dir in /vagrant/colab /vagrant; do
31 31 done
32 32 pip install -e $basedir
33 33  
  34 +### Create conf directory
  35 +sudo mkdir -p /etc/colab
  36 +sudo chown vagrant:vagrant /etc/colab
  37 +
34 38 if [ ! -s /etc/colab/settings.py ]; then
35 39 colab-init-config > /etc/colab/settings.py
36 40 fi
... ... @@ -41,4 +45,4 @@ colab-admin loaddata /vagrant/tests/test_data.json
41 45 # Init.d Celery files
42 46 sudo cp $basedir/vagrant/misc/etc/init.d/celeryd /etc/init.d/
43 47 sudo cp $basedir/vagrant/misc/etc/default/celeryd /etc/default/
44   -sudo systemctl start celeryd
  48 +sudo service celeryd start
... ...
vagrant/ubuntu.sh
... ... @@ -5,16 +5,4 @@ set -ex
5 5 ### Install dependencies
6 6 apt-get update
7 7  
8   -apt-get install curl git unzip mercurial build-essential libev-dev gettext libxml2-dev libxslt1-dev libssl-dev libffi-dev libjpeg-dev zlib1g-dev libfreetype6-dev libpq-dev python-dev postgresql virtualenvwrapper python-pip java-common -y
9   -
10   -
11   -### Create conf directory
12   -mkdir -p /etc/colab
13   -chown vagrant:vagrant /etc/colab
14   -
15   -### Create colab user in PostgreSQL
16   -echo "CREATE USER colab WITH PASSWORD 'colab';" | sudo -u postgres -i psql 2> /dev/null || echo
17   -echo "ALTER USER colab CREATEDB;" | sudo -u postgres -i psql 2> /dev/null
18   -
19   -#i## Create colab DB in PostgreSQL
20   -sudo -u postgres -i createdb --owner=colab colab 2> /dev/null | echo
  8 +apt-get install curl git unzip build-essential gettext libxml2-dev libxslt1-dev libssl-dev libffi-dev python-dev virtualenvwrapper python-pip rabbitmq-server -y
... ...