Commit b534565bb6f84185e046b1e613039dc0f0f90064

Authored by seocam
1 parent 0625388d

Merge com a ultima versao do bitcket: https://bitbucket.org/seocam/atu-colab/src/4ee3ca57614e


git-svn-id: http://repositorio.interlegis.gov.br/colab/trunk@6126 bee1b3ed-c3eb-0310-9994-b88e04532788
TODO.rst
... ... @@ -9,9 +9,9 @@ TODO
9 9 * Yure: Adicionar data do ultimo import de emails no footer
10 10 * Yure: Detectar links no conteudo e exibi-los como tal
11 11 * Yure: BUG: Display of HTML emails are wrong
12   -* Yure: Cadastrar usuário em lista pelo formulario de cadastro
13 12 * Arquivo "search.html" existente em "atu-colab/colab/templates" pode ser melhorado com relação ao conteúdo repetitivo dos "Tipos" exibidos no "Filtro" da página
14   -* Criar validador de urls para twitter, facebook e página pessoal do user profile
  13 +* BUG: Criar validador de urls para twitter, facebook e página pessoal do user profile
  14 +* Mostrar dados do twitter, facebook, gtalk e página pessoal somente para os usuários que estiverem logados
15 15  
16 16 * Configurar ADMINS no arquivo settings_local.py
17 17 * HTTPS para o trac, subversion e colab
... ... @@ -20,11 +20,10 @@ TODO
20 20 * Timezones no trac/colab/solr nao estao compativeis
21 21  
22 22 * Template de login nao exibe corretamente no firefox/linux
23   -* Quando usuario se cadastra com email errado o email nunca eh validado,
24   -e o username fica preso 'pra sempre'.
  23 +* Quando usuario se cadastra com email errado o email nunca eh validado, e o username fica preso 'pra sempre'.
25 24 * Nome dos usuarios errado nos emails que vem do Solr
26 25 * Adicionar ordering na busca
27   -* Criar tipo usuario no solr
  26 +* Criar tipo usuario no solr
28 27 * Substituir sistema de cadastro por django-registration
29 28 * Utilizar pysolr para efetuar queries no Solr
30 29 * Melhorar buscas (case insensitive match, palavras com acentos)
... ... @@ -40,13 +39,13 @@ e o username fica preso 'pra sempre'.
40 39 * Merge emails dos usuarios
41 40 * Implementar badge system
42 41 * Melhorar filtros
43   -* Link do thread preview deve enviar para mensagem da thread (anchor) (Útil? discutir com Jean)
  42 +* Link do thread preview deve enviar para mensagem da thread (anchor)
44 43 * Tornar todas as strings traduziveis
45 44 * Sugestão de como a divisão do edital seria melhor
46 45 * Indexar anexos da wiki (using Tika http://wiki.apache.org/solr/ExtractingRequestHandler)
47 46 * Filtrar usando calendario (como google analytics)
48 47 * Melhorar relevancia das buscas usando dismax queryparser
49   -* Chat estilo Gmail
  48 +* Chat estilo Gmail usando o mensageiro Interlegis
50 49 * Sistema de gerencia de conteúdo
51 50 * Versao mobile
52 51 * Exibir discussões relacionadas na barra da direita das discussões
... ... @@ -56,9 +55,14 @@ e o username fica preso 'pra sempre'.
56 55 * Contar page views no trac (ticket, wiki e changeset) e utiliza-los para rankear paginas nas buscas
57 56 * Mostrar highlight nas buscas
58 57 * Sistema de tags para as mensagens
  58 +* Tag cloud para as mensagens, ao lado direito da thread
59 59 * Pagina home para cada lista com os mesmo filtros da home atual
60 60 * Permitir que usuario entre e saia de listas ao editar perfil
61 61 * Planet Interlegis (agregador de blogs)
62   -* Filtros especificos para tipos diferentes na busca
  62 +* Filtros específicos para tipos diferentes na busca da thread
  63 +* Link para a mensagem original no histórico do Mailman (popup ajax)
  64 +* Filtro de mensagens nas listas acumulativos, podendo ligar e desligar todos
  65 +* Reduzir campos na tela de cadastro, transferindo-os como aba para a tela de profile, junto com a aba de listas a se inscrever)
  66 +
63 67 * Indice criado manualmente. Automatizar:
64 68 * create index super_archives_message_body_idx ON super_archives_message ((substring(body,0,1024)));
... ...
colab/rss/__init__.py 0 → 100644
colab/rss/feeds.py 0 → 100644
... ... @@ -0,0 +1,74 @@
  1 +#!/usr/bin/env python
  2 +# encoding: utf-8
  3 +
  4 +from django.contrib.syndication.views import Feed
  5 +from django.utils.translation import ugettext as _
  6 +
  7 +from colab.super_archives.models import Thread
  8 +from colab.super_archives import queries
  9 +from colab import solrutils
  10 +
  11 +class LatestThreadsFeeds(Feed):
  12 + title = _(u'Últimas Discussões')
  13 + link = '/rss/threads/latest/'
  14 +
  15 + def items(self):
  16 + return queries.get_latest_threads()[:20]
  17 +
  18 + def item_link(self, item):
  19 + return item.latest_message.url
  20 +
  21 + def item_title(self, item):
  22 + return item.latest_message.subject_clean
  23 +
  24 + def item_description(self, item):
  25 + return item.latest_message.body
  26 +
  27 +
  28 +class HottestThreadsFeeds(Feed):
  29 + title = _(u'Discussões Mais Relevantes')
  30 + link = '/rss/threads/hottest/'
  31 +
  32 + def items(self):
  33 + return queries.get_hottest_threads()[:20]
  34 +
  35 + def item_link(self, item):
  36 + return item.latest_message.url
  37 +
  38 + def item_title(self, item):
  39 + return item.latest_message.subject_clean
  40 +
  41 + def item_description(self, item):
  42 + return item.latest_message.body
  43 +
  44 +
  45 +class LatestColabFeeds(Feed):
  46 + title = _(u'Últimas Colaborações')
  47 + link = '/rss/colab/latest/'
  48 +
  49 + def items(self):
  50 + items = solrutils.get_latest_collaborations(20)
  51 + return items
  52 +
  53 + def item_title(self, item):
  54 + type_ = item.get('Type') + ': '
  55 + mailinglist = item.get('mailinglist')
  56 +
  57 + if mailinglist:
  58 + prefix = type_ + mailinglist + ' - '
  59 + else:
  60 + prefix = type_
  61 +
  62 + return prefix + item.get('Title')
  63 +
  64 + def item_description(self, item):
  65 + return item.get('Description')
  66 +
  67 + def item_link(self, item):
  68 + if item.get('Type') != 'thread':
  69 + url = item.get('url')
  70 + else:
  71 + url = 'http://colab.interlegis.leg.br'
  72 + url += item.get('url')
  73 + return url
  74 +
... ...
colab/rss/urls.py 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +from django.conf.urls.defaults import patterns, url
  2 +import feeds
  3 +
  4 +urlpatterns = patterns('',
  5 + url(r'threads/latest/$', feeds.LatestThreadsFeeds()),
  6 + url(r'colab/latest/$', feeds.LatestColabFeeds()),
  7 + url(r'threads/hottest/$', feeds.HottestThreadsFeeds()),
  8 +)
  9 +
... ...
colab/settings.py
... ... @@ -118,6 +118,7 @@ INSTALLED_APPS = (
118 118 # My apps
119 119 'colab.super_archives',
120 120 'colab.api',
  121 + 'colab.rss',
121 122 )
122 123  
123 124 # A sample logging configuration. The only tangible logging
... ...
colab/settings_local-dev.py
... ... @@ -10,9 +10,15 @@ MANAGERS = ADMINS
10 10 DATABASES = {
11 11 'default': {
12 12 'ENGINE': 'django.db.backends.sqlite3',
13   - 'NAME': 'colab.db',
  13 + 'NAME': 'colab.db',
14 14 }
15 15 }
16 16  
17 17 # Make this unique, and don't share it with anybody.
18 18 SECRET_KEY = ')(jksdfhsjkadfhjkh234ns!8fqu-1186h$vuj'
  19 +
  20 +import socks
  21 +SOCKS_TYPE = socks.PROXY_TYPE_SOCKS5
  22 +SOCKS_SERVER = '127.0.0.1'
  23 +SOCKS_PORT = 9050
  24 +
... ...
colab/signup.py
... ... @@ -4,8 +4,8 @@
4 4 from django.conf import settings
5 5 from django.utils.html import strip_tags
6 6 from django.utils.translation import ugettext as _
7   -from django.core.mail import EmailMultiAlternatives
8 7 from django.template.loader import render_to_string
  8 +from django.core.mail import EmailMultiAlternatives, send_mail
9 9  
10 10  
11 11 def send_verification_email(request, user):
... ... @@ -47,4 +47,15 @@ def send_reset_password_email(request, user):
47 47 email_msg.attach_alternative(html_content, 'text/html')
48 48 email_msg.send()
49 49  
50   -
51 50 \ No newline at end of file
  51 +def send_email_lists(user, mailing_lists):
  52 + subject = _(u'Inscrição na lista de discussão')
  53 + from_ = user.email
  54 + to = []
  55 + for list_name in mailing_lists:
  56 + # TODO: The following line needs to be generic. Domain should be stored in settings file
  57 + # or database (perharps read directly from mailman).
  58 + subscribe_addr = list_name + '-subscribe@listas.interlegis.gov.br'
  59 + to.append(subscribe_addr)
  60 +
  61 + send_mail(subject, '', from_, to)
  62 +
... ...
colab/solrutils.py
... ... @@ -178,7 +178,7 @@ def select(query, results_per_page=None, page_number=None, sort=None, fields=Non
178 178 if socks_server:
179 179 import socks
180 180 logging.debug('Socks enabled: %s:%s', settings.SOCKS_SERVER,
181   - settings.SOLR_PORT)
  181 + settings.SOCKS_PORT)
182 182  
183 183 socks.setdefaultproxy(settings.SOCKS_TYPE,
184 184 settings.SOCKS_SERVER,
... ...
colab/super_archives/forms.py
... ... @@ -5,6 +5,7 @@ from django.core.exceptions import ValidationError
5 5 from django.contrib.auth.models import User
6 6 from django.contrib.auth.forms import UserCreationForm as UserCreationForm_
7 7  
  8 +from colab.super_archives.models import MailingList
8 9 from colab.super_archives.validators import UniqueValidator
9 10  
10 11 # XXX: I know that this code does not look nice AT ALL.
... ... @@ -26,7 +27,20 @@ facebook_field = forms.URLField(label=u'Facebook', required=False)
26 27 google_talk_field = forms.EmailField(label=u'Google Talk', required=False)
27 28 webpage_field = forms.URLField(label=u'Página Pessoal/Blog', required=False)
28 29  
29   -
  30 +all_lists = MailingList.objects.all()
  31 +lists_names = []
  32 +for list_ in all_lists:
  33 + choice = (list_.name, list_.name)
  34 + lists_names.append(choice)
  35 +
  36 +lists_field = forms.MultipleChoiceField(
  37 + label=u'Listas',
  38 + required=False,
  39 + widget=forms.CheckboxSelectMultiple,
  40 + choices=lists_names
  41 +)
  42 +
  43 +
30 44 class UserCreationForm(UserCreationForm_):
31 45 first_name = first_name_field
32 46 last_name = last_name_field
... ... @@ -37,8 +51,9 @@ class UserCreationForm(UserCreationForm_):
37 51 facebook = facebook_field
38 52 google_talk = google_talk_field
39 53 webpage = webpage_field
  54 + lists = lists_field
40 55  
41   -
  56 +
42 57 class UserUpdateForm(forms.Form):
43 58 username = username_field
44 59 username.required = False
... ...
colab/super_archives/migrations/0008_add_mailinglist_name_to_url.py 0 → 100644
... ... @@ -0,0 +1,142 @@
  1 +# encoding: utf-8
  2 +import datetime
  3 +from south.db import db
  4 +from south.v2 import DataMigration
  5 +from django.db import models
  6 +
  7 +class Migration(DataMigration):
  8 +
  9 + def forwards(self, orm):
  10 + page_hits = orm.PageHit.objects.all()
  11 + for page_hit in page_hits:
  12 + if page_hit.url_path.startswith('/archives/thread/'):
  13 + token = page_hit.url_path.split('/')[-1]
  14 + threads = orm.Thread.objects.filter(subject_token=token)
  15 + if not len(threads): continue
  16 + thread = threads[0]
  17 + new_url = '/archives/thread/%s/%s' % (thread.mailinglist.name,
  18 + thread.subject_token)
  19 + page_hit.url_path = new_url
  20 + page_hit.save()
  21 +
  22 +
  23 + def backwards(self, orm):
  24 + raise RuntimeError("Cannot reverse this migration.")
  25 +
  26 + models = {
  27 + 'auth.group': {
  28 + 'Meta': {'object_name': 'Group'},
  29 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  30 + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
  31 + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
  32 + },
  33 + 'auth.permission': {
  34 + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
  35 + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  36 + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
  37 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  38 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
  39 + },
  40 + 'auth.user': {
  41 + 'Meta': {'object_name': 'User'},
  42 + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  43 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
  44 + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  45 + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
  46 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  47 + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
  48 + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  49 + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  50 + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  51 + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  52 + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
  53 + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
  54 + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
  55 + },
  56 + 'contenttypes.contenttype': {
  57 + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
  58 + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  59 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  60 + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  61 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
  62 + },
  63 + 'super_archives.emailaddress': {
  64 + 'Meta': {'object_name': 'EmailAddress'},
  65 + 'address': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}),
  66 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  67 + 'md5': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  68 + 'real_name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'blank': 'True'}),
  69 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emails'", 'null': 'True', 'to': "orm['auth.User']"})
  70 + },
  71 + 'super_archives.mailinglist': {
  72 + 'Meta': {'object_name': 'MailingList'},
  73 + 'description': ('django.db.models.fields.TextField', [], {}),
  74 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
  75 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  76 + 'last_imported_index': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  77 + 'logo': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
  78 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'})
  79 + },
  80 + 'super_archives.mailinglistmembership': {
  81 + 'Meta': {'object_name': 'MailingListMembership'},
  82 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  83 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  84 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  85 + },
  86 + 'super_archives.message': {
  87 + 'Meta': {'object_name': 'Message'},
  88 + 'body': ('django.db.models.fields.TextField', [], {'default': "''"}),
  89 + 'from_address': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.EmailAddress']"}),
  90 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  91 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  92 + 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  93 + 'received_time': ('django.db.models.fields.DateTimeField', [], {}),
  94 + 'spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  95 + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '512', 'db_index': 'True'}),
  96 + 'subject_clean': ('django.db.models.fields.CharField', [], {'max_length': '512', 'db_index': 'True'}),
  97 + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Thread']", 'null': 'True'})
  98 + },
  99 + 'super_archives.messagemetadata': {
  100 + 'Message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  101 + 'Meta': {'object_name': 'MessageMetadata'},
  102 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  103 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  104 + 'value': ('django.db.models.fields.TextField', [], {})
  105 + },
  106 + 'super_archives.pagehit': {
  107 + 'Meta': {'object_name': 'PageHit'},
  108 + 'hit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  109 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  110 + 'url_path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '2048', 'db_index': 'True'})
  111 + },
  112 + 'super_archives.thread': {
  113 + 'Meta': {'unique_together': "(('subject_token', 'mailinglist'),)", 'object_name': 'Thread'},
  114 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  115 + 'latest_message': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'unique': 'True', 'null': 'True', 'to': "orm['super_archives.Message']"}),
  116 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  117 + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  118 + 'spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  119 + 'subject_token': ('django.db.models.fields.CharField', [], {'max_length': '512'})
  120 + },
  121 + 'super_archives.userprofile': {
  122 + 'Meta': {'object_name': 'UserProfile'},
  123 + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  124 + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}),
  125 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  126 + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  127 + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  128 + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  129 + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}),
  130 + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  131 + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256'})
  132 + },
  133 + 'super_archives.vote': {
  134 + 'Meta': {'unique_together': "(('user', 'message'),)", 'object_name': 'Vote'},
  135 + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
  136 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  137 + 'message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  138 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  139 + }
  140 + }
  141 +
  142 + complete_apps = ['super_archives']
... ...
colab/super_archives/models.py
... ... @@ -148,7 +148,8 @@ class Thread(models.Model):
148 148  
149 149 # Calculate page_view_score
150 150 try:
151   - url = reverse('thread_view', args=[self.subject_token])
  151 + url = reverse('thread_view', args=[self.mailinglist.name,
  152 + self.subject_token])
152 153 pagehit = PageHit.objects.get(url_path=url)
153 154 page_view_score = pagehit.hit_count * 10
154 155 except (NoReverseMatch, PageHit.DoesNotExist):
... ... @@ -217,7 +218,8 @@ class Message(models.Model):
217 218 @property
218 219 def url(self):
219 220 """Shortcut to get thread url"""
220   - return reverse('thread_view', args=[self.thread.subject_token])
  221 + return reverse('thread_view', args=[self.mailinglist.name,
  222 + self.thread.subject_token])
221 223  
222 224 @property
223 225 def Description(self):
... ...
colab/super_archives/queries.py
... ... @@ -26,15 +26,18 @@ def get_messages_by_voted():
26 26 return messages.order_by('-vote_count', 'received_time')
27 27  
28 28  
29   -def get_first_message_in_thread(thread_token):
30   - return get_messages_by_date().filter(thread__subject_token=thread_token)[0]
31   -
  29 +def get_first_message_in_thread(mailinglist, thread_token):
  30 + query = get_messages_by_date()
  31 + query = query.filter(mailinglist__name=mailinglist)
  32 + query = query.filter(thread__subject_token=thread_token)[0]
  33 + return query
  34 +
32 35  
33 36 def get_latest_threads():
34 37 return Thread.objects.order_by('-latest_message__received_time')
35 38  
36 39  
37   -def get_hotest_threads():
  40 +def get_hottest_threads():
38 41 return Thread.objects.order_by('-score', '-latest_message__received_time')
39 42  
40 43  
... ...
colab/super_archives/templates/message-list.html
... ... @@ -11,8 +11,8 @@
11 11  
12 12 <h4>Ordenar por</h4>
13 13 <ul>
14   - <li {% ifequal order_by "hotest" %} class="selected" title="{% trans "Retirar filtro" %}" {% endifequal %}>
15   - <a href="{% ifequal order_by "hotest" %} {% append_to_get order="",p=1 %} {% else %} {% append_to_get order='hotest',p=1 %} {% endifequal %}">
  14 + <li {% ifequal order_by "hottest" %} class="selected" title="{% trans "Retirar filtro" %}" {% endifequal %}>
  15 + <a href="{% ifequal order_by "hottest" %} {% append_to_get order="",p=1 %} {% else %} {% append_to_get order='hottest',p=1 %} {% endifequal %}">
16 16 Relevância</a></li>
17 17 <li {% ifequal order_by "latest" %} class="selected" title="{% trans "Retirar filtro" %}" {% endifequal %}>
18 18 <a href="{% ifequal order_by "latest" %} {% append_to_get order="",p=1 %} {% else %} {% append_to_get order='latest',p=1 %} {% endifequal %}">
... ...
colab/super_archives/templatetags/append_to_get.py
... ... @@ -37,7 +37,16 @@ class AppendGetNode(template.Node):
37 37 path = context['request'].META['PATH_INFO']
38 38  
39 39 if len(get):
40   - path = '?' + urllib.urlencode(get)
  40 + # Convert all unicode objects in the get dict to
  41 + # str (utf-8 encoded)
  42 + get_utf_encoded = {}
  43 + for (key, value) in get.items():
  44 + if isinstance(value, unicode):
  45 + value = value.encode('utf-8')
  46 + get_utf_encoded.update({key: value})
  47 + get_utf_encoded = dict(get_utf_encoded)
  48 +
  49 + path = '?' + urllib.urlencode(get_utf_encoded)
41 50  
42 51 return path
43 52  
... ...
colab/super_archives/templatetags/form_field.py
... ... @@ -24,40 +24,41 @@ class RenderFormField(template.Node):
24 24 def render(self, context):
25 25 editable = context.get('editable', True)
26 26  
27   - class_ = ''
28   - errors = ''
29   - form_field_tag = ''
  27 + class_ = u''
  28 + errors = u''
  29 + form_field_tag = u''
30 30 try:
31 31 form_field = self.form_field_nocontext.resolve(context)
32 32 except template.VariableDoesNotExist:
33   - return ''
  33 + return u''
34 34  
35 35 if form_field.errors:
36   - class_ += 'error'
  36 + class_ += u'error'
37 37 if form_field.field.required:
38   - class_ += ' required'
  38 + class_ += u' required'
39 39 if form_field.errors:
40   - errors = '<br/>' + form_field.errors.as_text()
  40 + errors = u'<br/>' + form_field.errors.as_text()
41 41  
42 42 try:
43 43 default_value = self.default_value_nocontext.resolve(context)
44 44 except template.VariableDoesNotExist:
45   - default_value = ''
  45 + default_value = u''
46 46  
47 47 if editable:
48   - form_field_tag = '<br/>' + str(form_field)
  48 + form_field_tag = u'<br/>' + unicode(form_field)
49 49 elif isinstance(form_field.field, forms.URLField):
50   - form_field_tag = """<a href="%s" target="_blank">%s</a>""" % (
  50 + form_field_tag = u"""<a href="%s" target="_blank">%s</a>""" % (
51 51 default_value, default_value)
52 52 else:
53 53 form_field_tag = default_value
54 54  
55   - return """<p class="%s">%s: %s %s</p>""" % (
  55 + return u"""<p class="%s">%s: %s %s</p>""" % (
56 56 class_,
57 57 form_field.label_tag(),
58 58 form_field_tag,
59 59 errors
60 60 )
61   -
  61 +
  62 +
62 63 register = template.Library()
63 64 register.tag('render_form_field', render_form_field)
... ...
colab/super_archives/urls.py
... ... @@ -2,7 +2,7 @@ from django.conf.urls.defaults import patterns, include, url
2 2  
3 3 urlpatterns = patterns('',
4 4 # url(r'thread/(?P<thread>\d+)/$', 'super_archives.views.thread', name='thread'),
5   - url(r'thread/(?P<thread_token>[-\w]+)$',
  5 + url(r'thread/(?P<mailinglist>[-\w]+)/(?P<thread_token>[-\w]+)$',
6 6 'colab.super_archives.views.thread', name="thread_view"),
7 7 url(r'thread/$',
8 8 'colab.super_archives.views.list_messages', name='thread_list')
... ...
colab/super_archives/views.py
... ... @@ -8,9 +8,9 @@ from colab.super_archives import queries
8 8 from colab.super_archives.models import MailingList, Thread
9 9  
10 10  
11   -def thread(request, thread_token):
  11 +def thread(request, mailinglist, thread_token):
12 12  
13   - first_message = queries.get_first_message_in_thread(thread_token)
  13 + first_message = queries.get_first_message_in_thread(mailinglist, thread_token)
14 14 order_by = request.GET.get('order')
15 15 if order_by == 'voted':
16 16 msgs_query = queries.get_messages_by_voted()
... ... @@ -18,6 +18,7 @@ def thread(request, thread_token):
18 18 msgs_query = queries.get_messages_by_date()
19 19  
20 20 msgs_query = msgs_query.filter(thread__subject_token=thread_token)
  21 + msgs_query = msgs_query.filter(mailinglist__name=mailinglist)
21 22 emails = msgs_query.exclude(id=first_message.id)
22 23  
23 24 total_votes = first_message.votes_count()
... ... @@ -25,7 +26,8 @@ def thread(request, thread_token):
25 26 total_votes += email.votes_count()
26 27  
27 28 # Update relevance score
28   - thread = Thread.objects.get(subject_token=thread_token)
  29 + query = Thread.objects.filter(mailinglist__name=mailinglist)
  30 + thread = query.get(subject_token=thread_token)
29 31 thread.update_score()
30 32  
31 33 template_data = {
... ... @@ -44,8 +46,8 @@ def list_messages(request):
44 46 selected_list = request.GET.get('list')
45 47  
46 48 order_by = request.GET.get('order')
47   - if order_by == 'hotest':
48   - threads = queries.get_hotest_threads()
  49 + if order_by == 'hottest':
  50 + threads = queries.get_hottest_threads()
49 51 else:
50 52 threads = queries.get_latest_threads()
51 53  
... ...
colab/templates/home.html
... ... @@ -18,7 +18,7 @@
18 18 {% block main-content %}
19 19  
20 20 <div class="span-12 colborder">
21   - <h3>{% trans "Últimas Colaborações:" %}</h3>
  21 + <h3>{% trans "Últimas Colaborações" %}</h3>
22 22 <ul>
23 23 {% for doc in latest_docs %}
24 24 {% include "message-preview.html" %}
... ... @@ -31,7 +31,7 @@
31 31 </div>
32 32  
33 33 <div class="span-11 last">
34   - <h3>{% trans "Distribuição das Colaborações:" %}</h3>
  34 + <h3>{% trans "Distribuição das Colaborações" %}</h3>
35 35 <div id="collabs"></div>
36 36 </div>
37 37  
... ... @@ -39,20 +39,20 @@
39 39 <hr/>
40 40  
41 41 <div class="span-12 colborder">
42   - <h3>{% trans "Discussões Mais Relevantes:" %}</h3>
  42 + <h3>{% trans "Discussões Mais Relevantes" %}</h3>
43 43 <ul>
44   - {% for thread in hotest_threads %}
  44 + {% for thread in hottest_threads %}
45 45 {% include "message-preview.html" with doc=thread.latest_message %}
46 46 {% endfor %}
47 47 </ul>
48 48 <hr class="space"/>
49   - <a class="right" href="{% url thread_list %}?order=hotest">
  49 + <a class="right" href="{% url thread_list %}?order=hottest">
50 50 {% trans "Ver mais discussões relevantes..." %}
51 51 </a>
52 52 </div>
53 53  
54 54 <div class="span-11 last">
55   - <h3>{% trans "Últimas Discussões:" %}</h3>
  55 + <h3>{% trans "Últimas Discussões" %}</h3>
56 56 <ul>
57 57 {% for thread in latest_threads %}
58 58 {% include "message-preview.html" with doc=thread.latest_message %}
... ...
colab/templates/signup-form.html
... ... @@ -54,7 +54,12 @@
54 54 {% render_form_field form.google_talk %}
55 55 {% render_form_field form.webpage %}
56 56 </fieldset>
57   -
  57 +
  58 + <fieldset class="box span-11">
  59 + <legend>Inscrever-se nas Listas</legend>
  60 + {% render_form_field form.lists %}
  61 + </fieldset>
  62 +
58 63 <div class="span-24">
59 64 <input class="right" type="submit" value="Cadastrar"/>
60 65 </div>
... ...
colab/templates/user-profile.html
... ... @@ -9,20 +9,20 @@
9 9 {% block main-content %}
10 10 {% if not user_profile %}
11 11 <span class="notice span-24">
12   - <b>Usuário não cadastrado.</b> Você é dono deste perfil?
  12 + <b>Usuário não cadastrado.</b> Você é dono deste perfil?
13 13 <a href="{% url signup %}">Clique aqui
14 14 e cadastre-se.</a>
15 15 </span>
16 16  
17   - {% else %}
18   -
  17 + {% else %}
  18 +
19 19 {% ifequal request.user.username user_profile.user.username %}
20 20 <span class="success span-24">
21   - Ei, olha você aqui! Quer
  21 + Ei, olha você aqui! Quer
22 22 <a href="{% url user_profile_update request.user %}">editar seu perfil</a>?
23 23 </span>
24 24 {% endifequal %}
25   -
  25 +
26 26 {% endif %}
27 27  
28 28 <div id="user-profile">
... ... @@ -32,12 +32,12 @@
32 32 <img class="avatar" width="120px" heigth="120px"
33 33 src="http://www.gravatar.com/avatar/{{ email_address.md5 }}?s=120&d=identicon" />
34 34 </div>
35   -
  35 +
36 36 <div class="span-20 last">
37 37 <div class="span-10">
38 38 <form action="{% url user_profile_update request.user %}" method='post'>
39 39 {% csrf_token %}
40   -
  40 +
41 41 <h3>Informações Pessoais</h3>
42 42 <ul id="user-info">
43 43 <li>
... ... @@ -53,9 +53,9 @@
53 53 {% render_form_field form.role user_profile.role %}
54 54 </li>
55 55 </ul>
56   -
  56 +
57 57 <hr class="space" />
58   -
  58 +
59 59 <h3>Outras Informações</h3>
60 60 <ul>
61 61 <li>
... ... @@ -71,7 +71,7 @@
71 71 {% render_form_field form.webpage user_profile.webpage %}
72 72 </li>
73 73 </ul>
74   -
  74 +
75 75 <hr class="space"/>
76 76 {% if editable %}
77 77 <span class="span-5">
... ... @@ -80,30 +80,30 @@
80 80 {% endif %}
81 81 </form>
82 82 </div>
83   -
  83 +
84 84 {% if type_count %}
85 85 <div class="span-10 last">
86   - <h3 class="center">{% trans "Colaborações por área" %}</h3>
  86 + <h3 class="center">{% trans "Colaborações por Área" %}</h3>
87 87 <div id="collabs"></div>
88 88 </div>
89 89 {% endif %}
90 90 </div>
91   -
  91 +
92 92 <hr class="space" />
93   -
  93 +
94 94 <div class="span-13">
95   - <h3>{% trans "Últimas mensagens enviadas" %} </h3>
  95 + <h3>{% trans "Últimas Mensagens Enviadas" %} </h3>
96 96 <ul class="colborder">
97 97 {% for doc in emails %}
98 98 {% include "message-preview.html" %}
99 99 {% empty %}
100 100 <li>Não existem mensagens enviadas por este usuário até o momento.</li>
101   - {% endfor %}
  101 + {% endfor %}
102 102 </ul>
103 103 </div>
104   -
  104 +
105 105 <div class="span-11 last">
106   - <h3>{% trans "Participações na comunidade:" %}</h3>
  106 + <h3>{% trans "Participações na Comunidade" %}</h3>
107 107 <ul>
108 108 {% for doc in docs %}
109 109 {% include "message-preview.html" %}
... ... @@ -112,6 +112,6 @@
112 112 {% endfor %}
113 113 </ul>
114 114 </div>
115   -
  115 +
116 116 </div>
117 117 {% endblock %}
... ...
colab/urls.py
... ... @@ -10,6 +10,8 @@ urlpatterns = patterns(&#39;&#39;,
10 10 url(r'^archives/', include('colab.super_archives.urls')),
11 11  
12 12 url(r'^api/', include('colab.api.urls')),
  13 +
  14 + url(r'^rss/', include('colab.rss.urls')),
13 15  
14 16 url(r'^user/(?P<username>[\w@+.-]+)/?$',
15 17 'colab.views.userprofile.by_username', name='user_profile'),
... ... @@ -45,7 +47,7 @@ urlpatterns = patterns(&#39;&#39;,
45 47  
46 48 url(r'^account/logout/$', 'django.contrib.auth.views.logout',
47 49 {'next_page': '/'}, name='logout'),
48   -
  50 +
49 51 # Uncomment the next line to enable the admin:
50 52 url(r'^colab/admin/', include(admin.site.urls)),
51 53 )
... ...
colab/views/other.py
... ... @@ -18,10 +18,10 @@ def home(request):
18 18 """Index page view"""
19 19  
20 20 latest_threads = queries.get_latest_threads()
21   - hotest_threads = queries.get_hotest_threads()
  21 + hottest_threads = queries.get_hottest_threads()
22 22  
23 23 template_data = {
24   - 'hotest_threads': hotest_threads[:6],
  24 + 'hottest_threads': hottest_threads[:6],
25 25 'latest_threads': latest_threads[:6],
26 26 'type_count': solrutils.count_types(sample=1000),
27 27 'latest_docs': solrutils.get_latest_collaborations(6),
... ...
colab/views/signup.py
... ... @@ -60,6 +60,11 @@ def signup(request):
60 60  
61 61 signup_.send_verification_email(request, user)
62 62  
  63 + mailing_lists = form.cleaned_data.get('lists')
  64 + if mailing_lists:
  65 + signup_.send_email_lists(user, mailing_lists)
  66 +
  67 +
63 68 # Check if the user's email have been used previously
64 69 # in the mainling lists to link the user to old messages
65 70 email_addr, created = EmailAddress.objects.get_or_create(address=user.email)
... ...
solr-conf/data-config.xml
... ... @@ -321,7 +321,8 @@
321 321 <field column="UID" template="THREAD_${thread.id}" />
322 322 <field column="getId" template="${thread.name}" />
323 323 <field column="Type" template="thread" />
324   - <field column="path_string" template="/archives/thread/${thread.name}" />
  324 + <field column="path_string"
  325 + template="/archives/thread/${thread.mailinglist}/${thread.name}" />
325 326 <field column="created" name="created"
326 327 dateTimeFormat="yyyy-MM-dd hh:mm:ss" />
327 328 <field column="modified" name="modified"
... ...