Commit b534565bb6f84185e046b1e613039dc0f0f90064
1 parent
0625388d
Exists in
master
and in
39 other branches
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
Showing
24 changed files
with
363 additions
and
71 deletions
Show diff stats
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))); | ... | ... |
| ... | ... | @@ -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 | + | ... | ... |
| ... | ... | @@ -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
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('', |
| 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('', |
| 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" | ... | ... |