Commit 0625388dc18ff6d216c897b0e2b6c3cd09565750

Authored by seocam
1 parent 89b44981

Entrega de numero 5 do Edital ATU-COLAB publicado em 2011


git-svn-id: http://repositorio.interlegis.gov.br/colab/trunk@6017 bee1b3ed-c3eb-0310-9994-b88e04532788
Showing 85 changed files with 6219 additions and 2342 deletions   Show diff stats
MANIFEST.in
... ... @@ -0,0 +1,5 @@
  1 +include colab/templates/*.html
  2 +include colab/super_archives/templates/*.html
  3 +include colab/super_archives/fixtures/initial_data.json
  4 +recursive-include etc *
  5 +recursive-include colab/static *
0 6 \ No newline at end of file
... ...
README.rst
... ... @@ -0,0 +1,106 @@
  1 +Installation instructions for Ubuntu 10.04
  2 +-------------------------------------------
  3 +
  4 +* Install Apache2 with WSGI support:
  5 +
  6 + * apt-get install apache2 libapache2-mod-wsgi
  7 +
  8 +* Install dependencies to compile psycopg2:
  9 +
  10 + * apt-get build-dep python-psycopg2
  11 +
  12 +* Install Python PIP and update it:
  13 +
  14 + * apt-get install python-pip
  15 + * pip install -U pip
  16 +
  17 +* Install python virtualenv:
  18 +
  19 + * pip install virtualenv
  20 +
  21 +* Create a virtualenv for the deploy
  22 +
  23 + * mkdir /usr/local/django/
  24 + * virtualenv /usr/local/django/colab/
  25 +
  26 +* Download the colab src code:
  27 +
  28 + * hg clone https://bitbucket.org/seocam/atu-colab /usr/local/src/colab/
  29 +
  30 +* Install the django site:
  31 +
  32 + * pip install /usr/local/src/colab -E /usr/local/django/colab/
  33 +
  34 +* Configure your database settings in /usr/local/django/colab/lib/python2.6/site-packages/settings_local.py
  35 +
  36 +* Enable the colab site on apache and reload it:
  37 +
  38 + * ln -s /usr/local/django/colab/apache-site/colab /etc/apache2/sites-available
  39 + * a2ensite colab
  40 + * service apache2 restart
  41 +
  42 +
  43 +Configuring server to send emails
  44 +----------------------------------
  45 +
  46 +* Install postfix and mailutils:
  47 +
  48 + * apt-get install mailutils postfix
  49 +
  50 +* Update the file /etc/aliases adding users that should receive root's messages and run the update command:
  51 +
  52 + * newaliases
  53 +
  54 +
  55 +Cron job to import emails
  56 +---------------------------
  57 +
  58 +* Install sshfs:
  59 +
  60 + * apt-get install sshfs autofs
  61 +
  62 +* Create SSH keys. You should use a password but this tutorial won't cover it (if you use you will need to install and configure keychain process to be able to proceed):
  63 +
  64 + * ssh-keygen
  65 +
  66 +* Copy the content of your key (/root/.ssh/id_rsa.pub) to the file /root/.ssh/authorized_keys on the mailinglist server.
  67 +
  68 +* Append the following content to /etc/auto.master file:
  69 +
  70 + * /usr/local/django/colab/mnt /usr/local/django/colab/autofs/listas --timeout=600,--ghost
  71 +
  72 +* Restart autofs:
  73 +
  74 + * service autofs restart
  75 +
  76 +* Link cron script into /etc/cron.d/ folder:
  77 +
  78 + * ln -s /usr/local/django/colab/cron.d/colab_import_emails /etc/cron.d/
  79 +
  80 +* From now on the emails should be imported every minute
  81 +
  82 +
  83 +Cron job to reindex Solr
  84 +-------------------------
  85 +
  86 +* Install wget:
  87 +
  88 + * apt-get install wget
  89 +
  90 +* Link cron script into /etc/cron.d/ folder:
  91 +
  92 + * ln -s /usr/local/django/colab/cron.d/colab_solr_reindex /etc/cron.d/
  93 +
  94 +* From now on delta reindex should run every 10 minutes and full reindex once a day.
  95 +
  96 +
  97 +Updating an installed version
  98 +------------------------------
  99 +
  100 +* Update the source code:
  101 +
  102 + * cd /usr/local/src/colab/
  103 + * hg pull
  104 + * hg up
  105 + * pip install /usr/local/src/colab/ -E /usr/local/django/colab/ -U
  106 + * service apache2 restart
0 107 \ No newline at end of file
... ...
TODO.rst 0 → 100644
... ... @@ -0,0 +1,64 @@
  1 +TODO
  2 +-----
  3 +
  4 +* Sergio: Permitir que usuario atualize nome e sobrenome
  5 +
  6 +* Jean: Pagina Reporte um problema
  7 +* Jean: Pagina Contribua
  8 +
  9 +* Yure: Adicionar data do ultimo import de emails no footer
  10 +* Yure: Detectar links no conteudo e exibi-los como tal
  11 +* Yure: BUG: Display of HTML emails are wrong
  12 +* Yure: Cadastrar usuário em lista pelo formulario de cadastro
  13 +* 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
  15 +
  16 +* Configurar ADMINS no arquivo settings_local.py
  17 +* HTTPS para o trac, subversion e colab
  18 +* Autorizar usuarios a commitar no svn pelo django.contrib.admin
  19 +* Enviar emails para usuarios pedindo que se cadastrem no novo colab
  20 +* Timezones no trac/colab/solr nao estao compativeis
  21 +
  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'.
  25 +* Nome dos usuarios errado nos emails que vem do Solr
  26 +* Adicionar ordering na busca
  27 +* Criar tipo usuario no solr
  28 +* Substituir sistema de cadastro por django-registration
  29 +* Utilizar pysolr para efetuar queries no Solr
  30 +* Melhorar buscas (case insensitive match, palavras com acentos)
  31 +* Indexar nome do repositorio como campo e exibi-lo no titulo dos changesets retornados
  32 +* Criacao de repositorios distribuidos pela interface do colab
  33 +* Link para última msg recebida na thread
  34 +* Fazer thread querysets ter um objeto (most_relevant_message)
  35 +* Implementar enviar email
  36 +* BUG: alguns subjects comecam e terminam com [] fazendo com que a RE de limpeza apague todo o subject.
  37 +* BUG: mensagens importadas por listas erradas
  38 +* Implementar login/signup usando LDAP
  39 +* Claime email address
  40 +* Merge emails dos usuarios
  41 +* Implementar badge system
  42 +* Melhorar filtros
  43 +* Link do thread preview deve enviar para mensagem da thread (anchor) (Útil? discutir com Jean)
  44 +* Tornar todas as strings traduziveis
  45 +* Sugestão de como a divisão do edital seria melhor
  46 +* Indexar anexos da wiki (using Tika http://wiki.apache.org/solr/ExtractingRequestHandler)
  47 +* Filtrar usando calendario (como google analytics)
  48 +* Melhorar relevancia das buscas usando dismax queryparser
  49 +* Chat estilo Gmail
  50 +* Sistema de gerencia de conteúdo
  51 +* Versao mobile
  52 +* Exibir discussões relacionadas na barra da direita das discussões
  53 +* Migração e reorganização do conteúdo do trac/wiki para o novo colab
  54 +* Ao importar mensagem sem subject enviar email avisando o usuario que ele esta enviando um email sem o campos "Assunto"
  55 +* Remover ou ocultar trechos da mensagem que iniciem com ">" assim como o Gmail.
  56 +* Contar page views no trac (ticket, wiki e changeset) e utiliza-los para rankear paginas nas buscas
  57 +* Mostrar highlight nas buscas
  58 +* Sistema de tags para as mensagens
  59 +* Pagina home para cada lista com os mesmo filtros da home atual
  60 +* Permitir que usuario entre e saia de listas ao editar perfil
  61 +* Planet Interlegis (agregador de blogs)
  62 +* Filtros especificos para tipos diferentes na busca
  63 +* Indice criado manualmente. Automatizar:
  64 + * create index super_archives_message_body_idx ON super_archives_message ((substring(body,0,1024)));
... ...
colab/api/handlers.py
1 1  
  2 +from django.core.cache import cache
2 3 from django.db import IntegrityError
3 4 from django.core.exceptions import ObjectDoesNotExist
4 5 from django.contrib.auth.decorators import login_required
... ... @@ -6,7 +7,7 @@ from django.contrib.auth.decorators import login_required
6 7 from piston.utils import rc
7 8 from piston.handler import BaseHandler
8 9  
9   -from super_archives.models import Message
  10 +from colab.super_archives.models import Message, PageHit
10 11  
11 12  
12 13 class VoteHandler(BaseHandler):
... ... @@ -38,5 +39,33 @@ class VoteHandler(BaseHandler):
38 39 return rc.DELETED
39 40  
40 41  
  42 +class CountHandler(BaseHandler):
  43 + allowed_methods = ('POST')
  44 +
  45 + def create(self, request):
  46 + """Add one page view for the given url"""
  47 +
  48 + # If missing the path_info argument we can't do anything
  49 + path_info = request.POST.get('path_info')
  50 + if not path_info:
  51 + return rc.BAD_REQUEST
  52 +
  53 + # Here we cache the user's IP to ensure that the same
  54 + # IP won't hit the same page again for while
  55 + ip_addr = request.META.get('REMOTE_ADDR')
  56 + page_hits_cache = cache.get('page_hits', {})
  57 + duplicate = page_hits_cache.get(path_info, {}).get(ip_addr)
41 58  
  59 + if duplicate:
  60 + return rc.DUPLICATE_ENTRY
  61 + else:
  62 + page_hits_cache.update({path_info: {ip_addr: True }})
  63 + cache.set('page_hits', page_hits_cache)
  64 +
  65 + # Everything ok, so just increment the page count
  66 + page_hit = PageHit.objects.get_or_create(url_path=path_info)[0]
  67 + page_hit.hit_count += 1
  68 + page_hit.save()
  69 +
  70 + return rc.CREATED
42 71  
... ...
colab/api/urls.py
... ... @@ -2,11 +2,13 @@ from django.conf.urls.defaults import patterns, include, url
2 2  
3 3 from piston.resource import Resource
4 4  
5   -from api.handlers import VoteHandler
  5 +from colab.api.handlers import VoteHandler, CountHandler
6 6  
7 7  
8 8 vote_handler = Resource(VoteHandler)
  9 +count_handler = Resource(CountHandler)
9 10  
10 11 urlpatterns = patterns('',
11 12 url(r'message/(?P<message_id>\d+)/vote$', vote_handler),
  13 + url(r'hit/$', count_handler),
12 14 )
13 15 \ No newline at end of file
... ...
colab/django.wsgi
... ... @@ -1,27 +0,0 @@
1   -import sys
2   -import site
3   -import os
4   -
5   -app_path = os.path.abspath(os.path.dirname(__file__))
6   -app_parent_path = os.path.abspath(os.path.join(app_path, os.path.pardir))
7   -
8   -vepath = '/home/seocam/.virtualenvs/colab/lib/python2.6/site-packages'
9   -
10   -prev_sys_path = list(sys.path)
11   -# add the site-packages of our virtualenv as a site dir
12   -site.addsitedir(vepath)
13   -# add the app's directory to the PYTHONPATH
14   -sys.path.append(app_path)
15   -sys.path.append(app_parent_path)
16   -
17   -# reorder sys.path so new directories from the addsitedir show up first
18   -new_sys_path = [p for p in sys.path if p not in prev_sys_path]
19   -for item in new_sys_path:
20   - sys.path.remove(item)
21   -sys.path[:0] = new_sys_path
22   -
23   -# import from down here to pull in possible virtualenv django install
24   -from django.core.handlers.wsgi import WSGIHandler
25   -os.environ['DJANGO_SETTINGS_MODULE'] = 'colab.settings'
26   -application = WSGIHandler()
27   -
colab/settings.py
... ... @@ -5,8 +5,12 @@ import os.path
5 5 DEBUG = True
6 6 TEMPLATE_DEBUG = DEBUG
7 7  
  8 +if DEBUG:
  9 + import logging
  10 + logging.root.setLevel(logging.DEBUG)
  11 +
  12 +
8 13 ADMINS = (
9   - # ('Your Name', 'your_email@example.com'),
10 14 )
11 15  
12 16 PROJECT_PATH = os.path.abspath(os.path.dirname(__file__))
... ... @@ -22,7 +26,7 @@ LOGIN_URL = &#39;/login/&#39;
22 26 # timezone as the operating system.
23 27 # If running in a Windows environment this must be set to the same as your
24 28 # system time zone.
25   -TIME_ZONE = 'America/Chicago'
  29 +TIME_ZONE = 'America/Sao_Paulo'
26 30  
27 31 # Language code for this installation. All choices can be found here:
28 32 # http://www.i18nguy.com/unicode/language-identifiers.html
... ... @@ -39,12 +43,12 @@ USE_I18N = True
39 43 USE_L10N = True
40 44  
41 45 MEDIA_ROOT = os.path.join(PROJECT_PATH, os.pardir, 'site_media/media')
42   -MEDIA_URL = '/site_media/media/'
  46 +MEDIA_URL = '/media/'
43 47  
44 48 STATIC_ROOT = os.path.join(PROJECT_PATH, os.pardir, 'site_media/static')
45   -STATIC_URL = '/site_media/static/'
  49 +STATIC_URL = '/static/'
46 50  
47   -#ADMIN_MEDIA_PREFIX = '/site_media/static/admin/'
  51 +ADMIN_MEDIA_PREFIX = '/static/admin/'
48 52  
49 53 # Additional locations of static files
50 54 STATICFILES_DIRS = (
... ... @@ -66,12 +70,23 @@ TEMPLATE_LOADERS = (
66 70 # 'django.template.loaders.eggs.Loader',
67 71 )
68 72  
  73 +TEMPLATE_CONTEXT_PROCESSORS = (
  74 + 'django.contrib.auth.context_processors.auth',
  75 + 'django.core.context_processors.debug',
  76 + 'django.core.context_processors.i18n',
  77 + 'django.core.context_processors.media',
  78 + 'django.core.context_processors.static',
  79 + 'django.contrib.messages.context_processors.messages',
  80 + 'django.core.context_processors.request',
  81 +)
  82 +
69 83 MIDDLEWARE_CLASSES = (
70 84 'django.middleware.common.CommonMiddleware',
71 85 'django.contrib.sessions.middleware.SessionMiddleware',
72 86 'django.middleware.csrf.CsrfViewMiddleware',
73 87 'django.contrib.auth.middleware.AuthenticationMiddleware',
74 88 'django.contrib.messages.middleware.MessageMiddleware',
  89 + #'debug_toolbar.middleware.DebugToolbarMiddleware',
75 90 )
76 91  
77 92 ROOT_URLCONF = 'colab.urls'
... ... @@ -91,17 +106,18 @@ INSTALLED_APPS = (
91 106 'django.contrib.messages',
92 107 'django.contrib.staticfiles',
93 108 # Uncomment the next line to enable the admin:
94   - # 'django.contrib.admin',
  109 + 'django.contrib.admin',
95 110 # Uncomment the next line to enable admin documentation:
96 111 # 'django.contrib.admindocs',
97 112  
98 113 # Not standard apps
99 114 'south',
100   - 'registration',
  115 + 'cliauth',
  116 + #'debug_toolbar',
101 117  
102 118 # My apps
103   - 'super_archives',
104   - 'api',
  119 + 'colab.super_archives',
  120 + 'colab.api',
105 121 )
106 122  
107 123 # A sample logging configuration. The only tangible logging
... ... @@ -115,7 +131,8 @@ LOGGING = {
115 131 'handlers': {
116 132 'mail_admins': {
117 133 'level': 'ERROR',
118   - 'class': 'django.utils.log.AdminEmailHandler'
  134 + 'class': 'django.utils.log.AdminEmailHandler',
  135 + 'include_html': True,
119 136 }
120 137 },
121 138 'loggers': {
... ... @@ -127,5 +144,18 @@ LOGGING = {
127 144 }
128 145 }
129 146  
  147 +SERVER_EMAIL = '"Colab Interlegis" <noreply@interlegis.leg.br>'
  148 +EMAIL_HOST_USER = SERVER_EMAIL
  149 +
  150 +#SOLR_HOSTNAME = 'solr.interlegis.leg.br'
  151 +SOLR_HOSTNAME = '10.1.2.154'
  152 +SOLR_PORT = '8080'
  153 +SOLR_SELECT_PATH = '/solr/select'
  154 +
  155 +SOLR_COLAB_URI = 'http://colab.interlegis.gov.br'
  156 +SOLR_BASE_QUERY = """
  157 + (Type:changeset OR Type:ticket OR Type:wiki OR Type:thread)
  158 +"""
  159 +
130 160 from settings_local import *
131 161  
... ...
colab/settings_local-dev.py 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +
  2 +DEBUG = True
  3 +TEMPLATE_DEBUG = DEBUG
  4 +
  5 +ADMINS = (
  6 +)
  7 +
  8 +MANAGERS = ADMINS
  9 +
  10 +DATABASES = {
  11 + 'default': {
  12 + 'ENGINE': 'django.db.backends.sqlite3',
  13 + 'NAME': 'colab.db',
  14 + }
  15 +}
  16 +
  17 +# Make this unique, and don't share it with anybody.
  18 +SECRET_KEY = ')(jksdfhsjkadfhjkh234ns!8fqu-1186h$vuj'
... ...
colab/signup.py 0 → 100644
... ... @@ -0,0 +1,50 @@
  1 +#!/usr/bin/env python
  2 +# encoding: utf-8
  3 +
  4 +from django.conf import settings
  5 +from django.utils.html import strip_tags
  6 +from django.utils.translation import ugettext as _
  7 +from django.core.mail import EmailMultiAlternatives
  8 +from django.template.loader import render_to_string
  9 +
  10 +
  11 +def send_verification_email(request, user):
  12 +
  13 + subject = _(u'Colab: Verificação de email')
  14 + from_ = settings.SERVER_EMAIL
  15 + to = user.email
  16 +
  17 + email_data = {
  18 + 'hash': user.profile.verification_hash,
  19 + 'server_name': request.get_host(),
  20 + }
  21 +
  22 + html_content = render_to_string('email_signup-email-confirmation.html',
  23 + email_data)
  24 + text_content = strip_tags(html_content)
  25 + email_msg = EmailMultiAlternatives(subject, text_content, from_, [to])
  26 + email_msg.attach_alternative(html_content, 'text/html')
  27 + email_msg.send()
  28 +
  29 +
  30 +def send_reset_password_email(request, user):
  31 +
  32 + subject = _(u'Altereção de senha do Colab Interlegis')
  33 + from_ = settings.SERVER_EMAIL
  34 + to = user.email
  35 +
  36 + email_data = {
  37 + 'hash': user.profile.verification_hash,
  38 + 'server_name': request.get_host(),
  39 + 'username': user.username,
  40 + }
  41 +
  42 + html_content = render_to_string('email_account-reset-password.html',
  43 + email_data)
  44 + text_content = strip_tags(html_content)
  45 +
  46 + email_msg = EmailMultiAlternatives(subject, text_content, from_, [to])
  47 + email_msg.attach_alternative(html_content, 'text/html')
  48 + email_msg.send()
  49 +
  50 +
0 51 \ No newline at end of file
... ...
colab/socks.py 0 → 100644
... ... @@ -0,0 +1,387 @@
  1 +"""SocksiPy - Python SOCKS module.
  2 +Version 1.00
  3 +
  4 +Copyright 2006 Dan-Haim. All rights reserved.
  5 +
  6 +Redistribution and use in source and binary forms, with or without modification,
  7 +are permitted provided that the following conditions are met:
  8 +1. Redistributions of source code must retain the above copyright notice, this
  9 + list of conditions and the following disclaimer.
  10 +2. Redistributions in binary form must reproduce the above copyright notice,
  11 + this list of conditions and the following disclaimer in the documentation
  12 + and/or other materials provided with the distribution.
  13 +3. Neither the name of Dan Haim nor the names of his contributors may be used
  14 + to endorse or promote products derived from this software without specific
  15 + prior written permission.
  16 +
  17 +THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
  18 +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  19 +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  20 +EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  21 +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  22 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
  23 +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  24 +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  25 +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
  26 +
  27 +
  28 +This module provides a standard socket-like interface for Python
  29 +for tunneling connections through SOCKS proxies.
  30 +
  31 +"""
  32 +
  33 +import socket
  34 +import struct
  35 +
  36 +PROXY_TYPE_SOCKS4 = 1
  37 +PROXY_TYPE_SOCKS5 = 2
  38 +PROXY_TYPE_HTTP = 3
  39 +
  40 +_defaultproxy = None
  41 +_orgsocket = socket.socket
  42 +
  43 +class ProxyError(Exception):
  44 + def __init__(self, value):
  45 + self.value = value
  46 + def __str__(self):
  47 + return repr(self.value)
  48 +
  49 +class GeneralProxyError(ProxyError):
  50 + def __init__(self, value):
  51 + self.value = value
  52 + def __str__(self):
  53 + return repr(self.value)
  54 +
  55 +class Socks5AuthError(ProxyError):
  56 + def __init__(self, value):
  57 + self.value = value
  58 + def __str__(self):
  59 + return repr(self.value)
  60 +
  61 +class Socks5Error(ProxyError):
  62 + def __init__(self, value):
  63 + self.value = value
  64 + def __str__(self):
  65 + return repr(self.value)
  66 +
  67 +class Socks4Error(ProxyError):
  68 + def __init__(self, value):
  69 + self.value = value
  70 + def __str__(self):
  71 + return repr(self.value)
  72 +
  73 +class HTTPError(ProxyError):
  74 + def __init__(self, value):
  75 + self.value = value
  76 + def __str__(self):
  77 + return repr(self.value)
  78 +
  79 +_generalerrors = ("success",
  80 + "invalid data",
  81 + "not connected",
  82 + "not available",
  83 + "bad proxy type",
  84 + "bad input")
  85 +
  86 +_socks5errors = ("succeeded",
  87 + "general SOCKS server failure",
  88 + "connection not allowed by ruleset",
  89 + "Network unreachable",
  90 + "Host unreachable",
  91 + "Connection refused",
  92 + "TTL expired",
  93 + "Command not supported",
  94 + "Address type not supported",
  95 + "Unknown error")
  96 +
  97 +_socks5autherrors = ("succeeded",
  98 + "authentication is required",
  99 + "all offered authentication methods were rejected",
  100 + "unknown username or invalid password",
  101 + "unknown error")
  102 +
  103 +_socks4errors = ("request granted",
  104 + "request rejected or failed",
  105 + "request rejected because SOCKS server cannot connect to identd on the client",
  106 + "request rejected because the client program and identd report different user-ids",
  107 + "unknown error")
  108 +
  109 +def setdefaultproxy(proxytype=None,addr=None,port=None,rdns=True,username=None,password=None):
  110 + """setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
  111 + Sets a default proxy which all further socksocket objects will use,
  112 + unless explicitly changed.
  113 + """
  114 + global _defaultproxy
  115 + _defaultproxy = (proxytype,addr,port,rdns,username,password)
  116 +
  117 +class socksocket(socket.socket):
  118 + """socksocket([family[, type[, proto]]]) -> socket object
  119 +
  120 + Open a SOCKS enabled socket. The parameters are the same as
  121 + those of the standard socket init. In order for SOCKS to work,
  122 + you must specify family=AF_INET, type=SOCK_STREAM and proto=0.
  123 + """
  124 +
  125 + def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None):
  126 + _orgsocket.__init__(self,family,type,proto,_sock)
  127 + if _defaultproxy != None:
  128 + self.__proxy = _defaultproxy
  129 + else:
  130 + self.__proxy = (None, None, None, None, None, None)
  131 + self.__proxysockname = None
  132 + self.__proxypeername = None
  133 +
  134 + def __recvall(self, bytes):
  135 + """__recvall(bytes) -> data
  136 + Receive EXACTLY the number of bytes requested from the socket.
  137 + Blocks until the required number of bytes have been received.
  138 + """
  139 + data = ""
  140 + while len(data) < bytes:
  141 + data = data + self.recv(bytes-len(data))
  142 + return data
  143 +
  144 + def setproxy(self,proxytype=None,addr=None,port=None,rdns=True,username=None,password=None):
  145 + """setproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
  146 + Sets the proxy to be used.
  147 + proxytype - The type of the proxy to be used. Three types
  148 + are supported: PROXY_TYPE_SOCKS4 (including socks4a),
  149 + PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP
  150 + addr - The address of the server (IP or DNS).
  151 + port - The port of the server. Defaults to 1080 for SOCKS
  152 + servers and 8080 for HTTP proxy servers.
  153 + rdns - Should DNS queries be preformed on the remote side
  154 + (rather than the local side). The default is True.
  155 + Note: This has no effect with SOCKS4 servers.
  156 + username - Username to authenticate with to the server.
  157 + The default is no authentication.
  158 + password - Password to authenticate with to the server.
  159 + Only relevant when username is also provided.
  160 + """
  161 + self.__proxy = (proxytype,addr,port,rdns,username,password)
  162 +
  163 + def __negotiatesocks5(self,destaddr,destport):
  164 + """__negotiatesocks5(self,destaddr,destport)
  165 + Negotiates a connection through a SOCKS5 server.
  166 + """
  167 + # First we'll send the authentication packages we support.
  168 + if (self.__proxy[4]!=None) and (self.__proxy[5]!=None):
  169 + # The username/password details were supplied to the
  170 + # setproxy method so we support the USERNAME/PASSWORD
  171 + # authentication (in addition to the standard none).
  172 + self.sendall("\x05\x02\x00\x02")
  173 + else:
  174 + # No username/password were entered, therefore we
  175 + # only support connections with no authentication.
  176 + self.sendall("\x05\x01\x00")
  177 + # We'll receive the server's response to determine which
  178 + # method was selected
  179 + chosenauth = self.__recvall(2)
  180 + if chosenauth[0] != "\x05":
  181 + self.close()
  182 + raise GeneralProxyError((1,_generalerrors[1]))
  183 + # Check the chosen authentication method
  184 + if chosenauth[1] == "\x00":
  185 + # No authentication is required
  186 + pass
  187 + elif chosenauth[1] == "\x02":
  188 + # Okay, we need to perform a basic username/password
  189 + # authentication.
  190 + self.sendall("\x01" + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.proxy[5])) + self.__proxy[5])
  191 + authstat = self.__recvall(2)
  192 + if authstat[0] != "\x01":
  193 + # Bad response
  194 + self.close()
  195 + raise GeneralProxyError((1,_generalerrors[1]))
  196 + if authstat[1] != "\x00":
  197 + # Authentication failed
  198 + self.close()
  199 + raise Socks5AuthError,((3,_socks5autherrors[3]))
  200 + # Authentication succeeded
  201 + else:
  202 + # Reaching here is always bad
  203 + self.close()
  204 + if chosenauth[1] == "\xFF":
  205 + raise Socks5AuthError((2,_socks5autherrors[2]))
  206 + else:
  207 + raise GeneralProxyError((1,_generalerrors[1]))
  208 + # Now we can request the actual connection
  209 + req = "\x05\x01\x00"
  210 + # If the given destination address is an IP address, we'll
  211 + # use the IPv4 address request even if remote resolving was specified.
  212 + try:
  213 + ipaddr = socket.inet_aton(destaddr)
  214 + req = req + "\x01" + ipaddr
  215 + except socket.error:
  216 + # Well it's not an IP number, so it's probably a DNS name.
  217 + if self.__proxy[3]==True:
  218 + # Resolve remotely
  219 + ipaddr = None
  220 + req = req + "\x03" + chr(len(destaddr)) + destaddr
  221 + else:
  222 + # Resolve locally
  223 + ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
  224 + req = req + "\x01" + ipaddr
  225 + req = req + struct.pack(">H",destport)
  226 + self.sendall(req)
  227 + # Get the response
  228 + resp = self.__recvall(4)
  229 + if resp[0] != "\x05":
  230 + self.close()
  231 + raise GeneralProxyError((1,_generalerrors[1]))
  232 + elif resp[1] != "\x00":
  233 + # Connection failed
  234 + self.close()
  235 + if ord(resp[1])<=8:
  236 + raise Socks5Error(ord(resp[1]),_generalerrors[ord(resp[1])])
  237 + else:
  238 + raise Socks5Error(9,_generalerrors[9])
  239 + # Get the bound address/port
  240 + elif resp[3] == "\x01":
  241 + boundaddr = self.__recvall(4)
  242 + elif resp[3] == "\x03":
  243 + resp = resp + self.recv(1)
  244 + boundaddr = self.__recvall(resp[4])
  245 + else:
  246 + self.close()
  247 + raise GeneralProxyError((1,_generalerrors[1]))
  248 + boundport = struct.unpack(">H",self.__recvall(2))[0]
  249 + self.__proxysockname = (boundaddr,boundport)
  250 + if ipaddr != None:
  251 + self.__proxypeername = (socket.inet_ntoa(ipaddr),destport)
  252 + else:
  253 + self.__proxypeername = (destaddr,destport)
  254 +
  255 + def getproxysockname(self):
  256 + """getsockname() -> address info
  257 + Returns the bound IP address and port number at the proxy.
  258 + """
  259 + return self.__proxysockname
  260 +
  261 + def getproxypeername(self):
  262 + """getproxypeername() -> address info
  263 + Returns the IP and port number of the proxy.
  264 + """
  265 + return _orgsocket.getpeername(self)
  266 +
  267 + def getpeername(self):
  268 + """getpeername() -> address info
  269 + Returns the IP address and port number of the destination
  270 + machine (note: getproxypeername returns the proxy)
  271 + """
  272 + return self.__proxypeername
  273 +
  274 + def __negotiatesocks4(self,destaddr,destport):
  275 + """__negotiatesocks4(self,destaddr,destport)
  276 + Negotiates a connection through a SOCKS4 server.
  277 + """
  278 + # Check if the destination address provided is an IP address
  279 + rmtrslv = False
  280 + try:
  281 + ipaddr = socket.inet_aton(destaddr)
  282 + except socket.error:
  283 + # It's a DNS name. Check where it should be resolved.
  284 + if self.__proxy[3]==True:
  285 + ipaddr = "\x00\x00\x00\x01"
  286 + rmtrslv = True
  287 + else:
  288 + ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
  289 + # Construct the request packet
  290 + req = "\x04\x01" + struct.pack(">H",destport) + ipaddr
  291 + # The username parameter is considered userid for SOCKS4
  292 + if self.__proxy[4] != None:
  293 + req = req + self.__proxy[4]
  294 + req = req + "\x00"
  295 + # DNS name if remote resolving is required
  296 + # NOTE: This is actually an extension to the SOCKS4 protocol
  297 + # called SOCKS4A and may not be supported in all cases.
  298 + if rmtrslv==True:
  299 + req = req + destaddr + "\x00"
  300 + self.sendall(req)
  301 + # Get the response from the server
  302 + resp = self.__recvall(8)
  303 + if resp[0] != "\x00":
  304 + # Bad data
  305 + self.close()
  306 + raise GeneralProxyError((1,_generalerrors[1]))
  307 + if resp[1] != "\x5A":
  308 + # Server returned an error
  309 + self.close()
  310 + if ord(resp[1]) in (91,92,93):
  311 + self.close()
  312 + raise Socks4Error((ord(resp[1]),_socks4errors[ord(resp[1])-90]))
  313 + else:
  314 + raise Socks4Error((94,_socks4errors[4]))
  315 + # Get the bound address/port
  316 + self.__proxysockname = (socket.inet_ntoa(resp[4:]),struct.unpack(">H",resp[2:4])[0])
  317 + if rmtrslv != None:
  318 + self.__proxypeername = (socket.inet_ntoa(ipaddr),destport)
  319 + else:
  320 + self.__proxypeername = (destaddr,destport)
  321 +
  322 + def __negotiatehttp(self,destaddr,destport):
  323 + """__negotiatehttp(self,destaddr,destport)
  324 + Negotiates a connection through an HTTP server.
  325 + """
  326 + # If we need to resolve locally, we do this now
  327 + if self.__proxy[3] == False:
  328 + addr = socket.gethostbyname(destaddr)
  329 + else:
  330 + addr = destaddr
  331 + self.sendall("CONNECT " + addr + ":" + str(destport) + " HTTP/1.1\r\n" + "Host: " + destaddr + "\r\n\r\n")
  332 + # We read the response until we get the string "\r\n\r\n"
  333 + resp = self.recv(1)
  334 + while resp.find("\r\n\r\n")==-1:
  335 + resp = resp + self.recv(1)
  336 + # We just need the first line to check if the connection
  337 + # was successful
  338 + statusline = resp.splitlines()[0].split(" ",2)
  339 + if statusline[0] not in ("HTTP/1.0","HTTP/1.1"):
  340 + self.close()
  341 + raise GeneralProxyError((1,_generalerrors[1]))
  342 + try:
  343 + statuscode = int(statusline[1])
  344 + except ValueError:
  345 + self.close()
  346 + raise GeneralProxyError((1,_generalerrors[1]))
  347 + if statuscode != 200:
  348 + self.close()
  349 + raise HTTPError((statuscode,statusline[2]))
  350 + self.__proxysockname = ("0.0.0.0",0)
  351 + self.__proxypeername = (addr,destport)
  352 +
  353 + def connect(self,destpair):
  354 + """connect(self,despair)
  355 + Connects to the specified destination through a proxy.
  356 + destpar - A tuple of the IP/DNS address and the port number.
  357 + (identical to socket's connect).
  358 + To select the proxy server use setproxy().
  359 + """
  360 + # Do a minimal input check first
  361 + if (type(destpair) in (list,tuple)==False) or (len(destpair)<2) or (type(destpair[0])!=str) or (type(destpair[1])!=int):
  362 + raise GeneralProxyError((5,_generalerrors[5]))
  363 + if self.__proxy[0] == PROXY_TYPE_SOCKS5:
  364 + if self.__proxy[2] != None:
  365 + portnum = self.__proxy[2]
  366 + else:
  367 + portnum = 1080
  368 + _orgsocket.connect(self,(self.__proxy[1],portnum))
  369 + self.__negotiatesocks5(destpair[0],destpair[1])
  370 + elif self.__proxy[0] == PROXY_TYPE_SOCKS4:
  371 + if self.__proxy[2] != None:
  372 + portnum = self.__proxy[2]
  373 + else:
  374 + portnum = 1080
  375 + _orgsocket.connect(self,(self.__proxy[1],portnum))
  376 + self.__negotiatesocks4(destpair[0],destpair[1])
  377 + elif self.__proxy[0] == PROXY_TYPE_HTTP:
  378 + if self.__proxy[2] != None:
  379 + portnum = self.__proxy[2]
  380 + else:
  381 + portnum = 8080
  382 + _orgsocket.connect(self,(self.__proxy[1],portnum))
  383 + self.__negotiatehttp(destpair[0],destpair[1])
  384 + elif self.__proxy[0] == None:
  385 + _orgsocket.connect(self,(destpair[0],destpair[1]))
  386 + else:
  387 + raise GeneralProxyError((4,_generalerrors[4]))
... ...
colab/solrutils.py 0 → 100644
... ... @@ -0,0 +1,264 @@
  1 +#!/usr/bin/env python
  2 +# encoding: utf-8
  3 +
  4 +import math
  5 +import json
  6 +import urllib
  7 +import socket
  8 +import logging
  9 +import httplib
  10 +
  11 +from dateutil.parser import parse as parse_timestamp
  12 +
  13 +from django.conf import settings
  14 +
  15 +from colab.super_archives.models import EmailAddress
  16 +
  17 +
  18 +def build_query(user_query, filters=None):
  19 + """Build the query that will be sent to Solr"""
  20 +
  21 + if not user_query:
  22 + user_query = '*'
  23 +
  24 + query = settings.SOLR_BASE_QUERY.strip() + ' AND ' + user_query
  25 + if filters:
  26 + query = "(%s)" % query
  27 +
  28 + for (key, value) in filters.items():
  29 + if value:
  30 + query += " AND %s:%s" % (key, value)
  31 +
  32 + logging.info(query)
  33 + return query.encode('utf-8')
  34 +
  35 +
  36 +def parse_document_timestamps(doc, date_attrs=('modified', 'created')):
  37 + """Converts the `modified' and `created' dates from
  38 + ISO 8601 format to a date time object for the given
  39 + document.
  40 +
  41 + """
  42 +
  43 + for date in date_attrs:
  44 + date_str = doc.get(date)
  45 + try:
  46 + date_obj = parse_timestamp(date_str)
  47 + except ValueError:
  48 + logging.error('Error trying to parse "%s"', date_str)
  49 + date_obj = None
  50 + doc.update({date: date_obj})
  51 +
  52 + return doc
  53 +
  54 +
  55 +def get_document_url(doc):
  56 + """Set the url attribute for a document using the path_string.
  57 + In case the resource comes from an external domain it will
  58 + be prepended to this URL.
  59 +
  60 + """
  61 + doc_type = doc.get('Type')
  62 +
  63 + url = ''
  64 + if doc_type in ('ticket', 'wiki', 'changeset'):
  65 + url += settings.SOLR_COLAB_URI
  66 +
  67 + url += doc.get('path_string', '')
  68 + doc.update({'url': url})
  69 +
  70 + return doc
  71 +
  72 +
  73 +def get_document_from_addr(doc):
  74 + """Get a EmailAddress instance for the given document if
  75 + its available.
  76 +
  77 + """
  78 +
  79 + username = doc.get('Creator')
  80 + from_addresses = EmailAddress.objects.filter(user__username=username)
  81 + if username and from_addresses:
  82 + doc.update({'from_address': from_addresses[0]})
  83 +
  84 +
  85 +def add_attrs_to_doc(doc):
  86 + """Wraps the call of functions that adds or modifies keys
  87 + of the giving doc (which should be a dict).
  88 +
  89 + """
  90 + get_document_url(doc)
  91 + parse_document_timestamps(doc)
  92 + get_document_from_addr(doc)
  93 +
  94 +
  95 +class SolrPaginator(list):
  96 +
  97 + def __init__(self, response_dict, current_page):
  98 + super(SolrPaginator, self).__init__()
  99 +
  100 + responseHeader = response_dict.get('responseHeader', {})
  101 + response = response_dict.get('response', {})
  102 + request_params = responseHeader.get('params', {})
  103 +
  104 + docs = response.get('docs', [])
  105 + self.extend(docs)
  106 +
  107 + self.QTime = int(responseHeader.get('QTime', 1)) / 1000.0
  108 +
  109 + self.per_page = int(request_params.get('rows', 10))
  110 + self.numFound = int(response.get('numFound', 0))
  111 + self.page_num = current_page
  112 +
  113 + self.num_of_pages = int(math.ceil(self.numFound / float(self.per_page)))
  114 +
  115 + self.has_previous = self.page_num > 1
  116 + if self.has_previous:
  117 + self.previous_page_number = self.page_num - 1
  118 + else:
  119 + self.previous_page_number = None
  120 +
  121 + self.has_next = self.page_num < self.num_of_pages
  122 + if self.has_next:
  123 + self.next_page_number = self.page_num + 1
  124 + else:
  125 + self.next_page_number = None
  126 +
  127 + @property
  128 + def last_page(self):
  129 + return self.num_of_pages
  130 +
  131 +
  132 +def select(query, results_per_page=None, page_number=None, sort=None, fields=None, link_attrs=True):
  133 + """Perform a select in a Solr instance using the configuration
  134 + set in settings.py.
  135 +
  136 + """
  137 +
  138 + data = {
  139 + 'q': query,
  140 + 'wt': 'json',
  141 + }
  142 +
  143 + # Number of results per page
  144 + if results_per_page:
  145 + data.update({'rows': results_per_page})
  146 +
  147 + # Page number
  148 + if page_number:
  149 + data.update({
  150 + 'start': (page_number - 1) * results_per_page,
  151 + })
  152 +
  153 + # Sort order
  154 + if sort:
  155 + data.update({
  156 + 'sort': sort,
  157 + })
  158 +
  159 + # Only select those fields
  160 + if fields:
  161 + data.update({
  162 + 'fl': ','.join(fields),
  163 + })
  164 + # First version of this was implemented using urllib2 and was
  165 + # a milion times easier but unfortunatelly urllib2.urlopen
  166 + # does not support http headers. Without setting http headers
  167 + # for charset the solr server tries to decode utf-8 params
  168 + # as ASCII causing it to crash. HTTPConnection deals with
  169 + # encodings automagically.
  170 + solr_conn = httplib.HTTPConnection(settings.SOLR_HOSTNAME,
  171 + settings.SOLR_PORT)
  172 + query_params = urllib.urlencode(data)
  173 + solr_select_uri = settings.SOLR_SELECT_PATH + '?' + query_params
  174 +
  175 + # Socks proxy configuration. Only required for development
  176 + # if the solr server is behind a firewall.
  177 + socks_server = getattr(settings, "SOCKS_SERVER", None)
  178 + if socks_server:
  179 + import socks
  180 + logging.debug('Socks enabled: %s:%s', settings.SOCKS_SERVER,
  181 + settings.SOLR_PORT)
  182 +
  183 + socks.setdefaultproxy(settings.SOCKS_TYPE,
  184 + settings.SOCKS_SERVER,
  185 + settings.SOCKS_PORT)
  186 + socket.socket = socks.socksocket
  187 +
  188 + try:
  189 + solr_conn.request('GET', solr_select_uri)
  190 + solr_response = solr_conn.getresponse()
  191 + except socket.error as err:
  192 + solr_response = None
  193 + logging.exception(err)
  194 +
  195 + if solr_response and solr_response.status == 200:
  196 + #TODO: Log error connecting to solr
  197 + solr_json_resp = solr_response.read()
  198 + solr_dict_resp = json.loads(solr_json_resp)
  199 + else:
  200 + solr_dict_resp = {}
  201 +
  202 + docs = solr_dict_resp.get('response', {}).get("docs", [])
  203 +
  204 + if link_attrs:
  205 + # Loop over all documents adding or linking its information
  206 + # with the data from this app or database
  207 + map(add_attrs_to_doc, docs)
  208 +
  209 + return solr_dict_resp
  210 +
  211 +
  212 +def get_latest_collaborations(number=10, username=None):
  213 + """Get the n documents recently modified that this username
  214 + has helped in somehow.
  215 +
  216 + """
  217 +
  218 + if username:
  219 + filters = {'collaborator': username}
  220 + else:
  221 + filters = None
  222 +
  223 + query = build_query('*', filters)
  224 + solr_response = select(
  225 + query=query,
  226 + results_per_page=number,
  227 + sort='modified desc'
  228 + )
  229 +
  230 + return solr_response.get('response', {}).get('docs', [])
  231 +
  232 +
  233 +def count_types(sample=100, filters=None):
  234 + """Count the type of the last modifications returning the
  235 + results in dict.
  236 +
  237 + Example: {
  238 + 'wiki' 30,
  239 + 'thread': 40,
  240 + 'ticket', 10,
  241 + 'changeset' 20,
  242 + }
  243 +
  244 + """
  245 +
  246 + query = build_query('*', filters)
  247 + solr_response = select(
  248 + query=query,
  249 + results_per_page=sample,
  250 + sort='modified desc',
  251 + link_attrs=False,
  252 + )
  253 +
  254 + docs = solr_response.get('response', {}).get('docs', [])
  255 +
  256 + type_count = {}
  257 + for doc in docs:
  258 + doc_type = doc.get('Type')
  259 + count = type_count.get(doc_type, 0) + 1
  260 + type_count.update({doc_type: count})
  261 +
  262 + return type_count
  263 +
  264 +
... ...
colab/static/css/screen.css
... ... @@ -25,6 +25,9 @@ h4 {
25 25 margin-bottom: 10px;
26 26 }
27 27  
  28 +form {
  29 + display: inline;
  30 +}
28 31  
29 32 #main-content {
30 33 margin-bottom: 20px;
... ... @@ -48,16 +51,24 @@ h4 {
48 51 text-decoration: none;
49 52 }
50 53  
  54 +.preview-message img {
  55 + margin-right: 5px;
  56 +}
  57 +
51 58 /* Header */
52 59  
53 60 #header-searchbox {
54   - width: 150px;
  61 + width: 290px;
  62 +}
  63 +
  64 +#header-menu span {
  65 + line-height: 33px;
55 66 }
56 67  
57 68 /* Thread view */
58 69  
59 70 .plus {
60   - border: 1px dashed #ddd;
  71 + border: 1px dotted #ddd;
61 72 margin: 0;
62 73 }
63 74  
... ... @@ -72,8 +83,13 @@ h4 {
72 83  
73 84 .plus img {
74 85 vertical-align: middle;
75   - height: 40px;
76 86 cursor: pointer;
  87 + margin-top: 8px;
  88 + margin-right: 20px;
  89 +}
  90 +
  91 +.plus img:hover {
  92 + opacity: 0.8;
77 93 }
78 94  
79 95 /* User Profile */
... ... @@ -125,11 +141,32 @@ input[type=&quot;password&quot;] {
125 141 color: #335;
126 142 }
127 143  
  144 +.subject img {
  145 + margin-right: 5px;
  146 +}
  147 +
128 148 .avatar {
129 149 border: 1px solid #ddd;
130 150 padding: 3px;
131 151 }
132 152  
  153 +div.avatar-placeholder {
  154 + border: 1px solid #cccccc;
  155 +}
  156 +
  157 +.avatar-image {
  158 + float: left;
  159 + margin-right: 10px;
  160 + height: 40px;
  161 + padding: 8px 12px 0;
  162 + background-color: white;
  163 +}
  164 +
  165 +label.avatar-placeholder {
  166 + display: block;
  167 + margin-left: 6px;
  168 +}
  169 +
133 170 .tag {
134 171 background-color: #2183b3;
135 172 padding: 2px 4px;
... ... @@ -173,4 +210,16 @@ img.center {
173 210  
174 211 .filters li:before {
175 212 content: "\00BB\0020";
176   -}
177 213 \ No newline at end of file
  214 +}
  215 +
  216 +
  217 +.filters .legend li span {
  218 + display: inline-block;
  219 + width: 25px;
  220 +}
  221 +
  222 +.filters .legend li:before {
  223 + content: '';
  224 +}
  225 +
  226 +
... ...
colab/static/img/COPYRIGHT 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +The icons listed bellow were copied from the Iconic icons package. The icons in this set were originally designed for the Franklin Street WordPress theme and are available under CC Attribution-Share Alike 3.0 license - http://creativecommons.org/licenses/by-sa/3.0/us/
  2 +
  3 +* ticket.png
  4 +* changeset.png
  5 +* thread.png
  6 +* wiki.png
  7 +* x.png
  8 +* plus.png
  9 +
  10 +The full Iconic package can be found here: https://github.com/downloads/somerandomdude/Iconic/iconic.zip
... ...
colab/static/img/changeset.png 0 → 100644

158 Bytes

colab/static/img/favicon.ico 0 → 100644
No preview for this file type
colab/static/img/logo_small.png

2.93 KB | W: | H:

2.95 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
colab/static/img/plus.png 0 → 100644

397 Bytes

colab/static/img/thread.png 0 → 100644

208 Bytes

colab/static/img/thumbs_up.jpg

4.9 KB

colab/static/img/ticket.png 0 → 100644

270 Bytes

colab/static/img/user.png 0 → 100644

442 Bytes

colab/static/img/wiki.png 0 → 100644

202 Bytes

colab/static/img/x.png 0 → 100644

274 Bytes

colab/static/js/base.js
... ... @@ -5,6 +5,7 @@ function vote_callback(msg_id, step) {
5 5 return parseInt(count) + step;
6 6 });
7 7 jQuery('#msg-' + msg_id + ' .minus').toggleClass('hide');
  8 + jQuery('#vote-notification').addClass('hide');
8 9 }
9 10 }
10 11  
... ... @@ -21,6 +22,18 @@ function get_vote_ajax_dict(msg_id, type_) {
21 22 url: "/api/message/" + msg_id + "/vote",
22 23 type: type_,
23 24 success: vote_callback(msg_id, step),
  25 + error: function (jqXHR, textStatus, errorThrown) {
  26 +
  27 + error_msg = '<b>Seu voto não foi computado.</b>'
  28 + if (jqXHR.status === 401) {
  29 + error_msg += ' Você deve estar autenticado para votar.';
  30 + } else {
  31 + error_msg += ' Erro desconhecido ao tentando votar.';
  32 + }
  33 +
  34 + jQuery('#vote-notification').html(error_msg).removeClass('hide');
  35 + scroll(0, 0);
  36 + }
24 37 }
25 38 }
26 39  
... ... @@ -35,7 +48,6 @@ function unvote(msg_id) {
35 48 jQuery(document).ready(function() {
36 49 jQuery('.email_message').each(function() {
37 50 var msg_id = this.getAttribute('id').split('-')[1];
38   - console.debug(msg_id);
39 51 jQuery('.plus img', this).bind('click', function() {
40 52 vote(msg_id);
41 53 });
... ... @@ -44,4 +56,12 @@ jQuery(document).ready(function() {
44 56 return false;
45 57 });
46 58 });
47   -});
48 59 \ No newline at end of file
  60 +});
  61 +
  62 +function pagehit(path_info) {
  63 + jQuery.ajax({
  64 + url: '/api/hit/',
  65 + type: 'POST',
  66 + data: {'path_info': path_info},
  67 + });
  68 +}
... ...
colab/super_archives/admin.py
1 1  
2 2 from django.contrib import admin
3   -from super_archives.models import MailingList, MailingListMembership, \
4   - Message, MessageMetadata, Vote
5   -
6   -admin.site.register(MailingList)
7   -admin.site.register(MailingListMembership)
8   -admin.site.register(Message)
9   -admin.site.register(MessageMetadata)
10   -admin.site.register(Vote)
  3 +from colab.super_archives.models import Message, Thread, UserProfile
  4 +
  5 +class MessageAdmin(admin.ModelAdmin):
  6 + list_filter = ('spam', 'mailinglist', 'received_time', )
  7 + search_fields = (
  8 + 'id',
  9 + 'subject',
  10 + 'subject_clean',
  11 + 'body',
  12 + 'from_address__real_name',
  13 + 'from_address__address',
  14 + 'from_address__user__first_name',
  15 + 'from_address__user__last_name',
  16 + 'from_address__user__username',
  17 + )
  18 + readonly_fields = ('thread', 'from_address', 'mailinglist')
  19 +
  20 +
  21 +class ThreadAdmin(admin.ModelAdmin):
  22 + list_filter = ('spam', 'mailinglist', 'message__received_time',)
  23 + search_fields = (
  24 + 'id',
  25 + 'subject_token',
  26 + 'message__subject',
  27 + 'message__subject_clean',
  28 + 'message__from_address__real_name',
  29 + 'message__from_address__address',
  30 + 'message__from_address__user__first_name',
  31 + 'message__from_address__user__last_name',
  32 + 'message__from_address__user__username',
  33 + )
  34 +
  35 + readonly_fields = (
  36 + 'mailinglist',
  37 + 'subject_token',
  38 + 'latest_message',
  39 + 'score',
  40 + )
  41 +
  42 + fields = (
  43 + 'mailinglist',
  44 + 'subject_token',
  45 + 'latest_message',
  46 + 'score',
  47 + 'spam',
  48 + )
  49 +
  50 +
  51 +admin.site.register(UserProfile)
  52 +admin.site.register(Thread, ThreadAdmin)
  53 +admin.site.register(Message, MessageAdmin)
  54 +
... ...
colab/super_archives/fixtures/initial_data.json 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +[
  2 + {
  3 + "pk": 1,
  4 + "model": "auth.group",
  5 + "fields": {
  6 + "name": "developer",
  7 + "permissions": []
  8 + }
  9 + }
  10 +]
... ...
colab/super_archives/forms.py
1   -# -*- coding: utf8 -*-
  1 +# -*- coding: utf-8 -*-
2 2  
3 3 from django import forms
4 4 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 super_archives.validators import UniqueValidator
  8 +from colab.super_archives.validators import UniqueValidator
9 9  
10 10 # XXX: I know that this code does not look nice AT ALL.
11   -# probably it should be implemented using formsets instead of the hacking
12   -# below. Feel free to improve it! :)
  11 +# probably it should be implemented using formsets instead of
  12 +# the hack below. Feel free to improve it! :)
13 13  
14 14 # User fields
15 15 username_field = UserCreationForm_().fields.get('username')
... ... @@ -21,10 +21,10 @@ email_field = forms.EmailField(validators=[UniqueValidator(User, &#39;email&#39;)])
21 21 institution_field = forms.CharField(max_length=120, label=u'Instituição',
22 22 required=False)
23 23 role_field = forms.CharField(max_length=60, label='Função', required=False)
24   -twitter_field = forms.CharField(label=u'Twitter', required=False)
25   -facebook_field = forms.CharField(label=u'Facebook', required=False)
  24 +twitter_field = forms.URLField(label=u'Twitter', required=False)
  25 +facebook_field = forms.URLField(label=u'Facebook', required=False)
26 26 google_talk_field = forms.EmailField(label=u'Google Talk', required=False)
27   -webpage_field = forms.CharField(label=u'Página Pessoal/Blog', required=False)
  27 +webpage_field = forms.URLField(label=u'Página Pessoal/Blog', required=False)
28 28  
29 29  
30 30 class UserCreationForm(UserCreationForm_):
... ...
colab/super_archives/management/commands/import_emails.py
1 1 #!/usr/bin/env python
2   -# -*- encoding: utf8 -*-
  2 +# -*- encoding: utf-8 -*-
3 3  
4 4 """Import emails from a mailman storage to the django database."""
5 5  
6 6 import os
7 7 import re
  8 +import sys
8 9 import mailbox
9 10 from optparse import make_option
10 11  
... ... @@ -13,8 +14,10 @@ from django.template.defaultfilters import slugify
13 14 from django.core.exceptions import ObjectDoesNotExist
14 15 from django.core.management.base import BaseCommand, CommandError
15 16  
16   -from super_archives.models import MailingList, Message, Thread, EmailAddress
17   -from super_archives.management.commands.message import Message as CustomMessage
  17 +from colab.super_archives.models import MailingList, Message, \
  18 + Thread, EmailAddress
  19 +from colab.super_archives.management.commands.message import Message as \
  20 + CustomMessage
18 21  
19 22  
20 23 class Command(BaseCommand, object):
... ... @@ -35,7 +38,14 @@ class Command(BaseCommand, object):
35 38 help='Path of email archives to be imported. (default: %s)' %
36 39 default_archives_path,
37 40 default=default_archives_path),
38   -
  41 +
  42 + make_option('--exclude-list',
  43 + dest='exclude_lists',
  44 + help=("Mailing list that won't be imported. It can be used many"
  45 + "times for more than one list."),
  46 + action='append',
  47 + default=None),
  48 +
39 49 make_option('--all',
40 50 dest='all',
41 51 help='Import all messages (default: False)',
... ... @@ -81,11 +91,12 @@ class Command(BaseCommand, object):
81 91 key += 1
82 92 yield key-1, mbox[key-1]
83 93  
84   - def get_emails(self, mailinglist_dir, all):
  94 + def get_emails(self, mailinglist_dir, all, exclude_lists):
85 95 """Generator function that get the emails from each mailing
86 96 list dump dirctory. If `all` is set to True all the emails in the
87 97 mbox will be imported if not it will just resume from the last
88   - message previously imported.
  98 + message previously imported. The lists set in `exclude_lists`
  99 + won't be imported.
89 100  
90 101 Yield: A tuple in the form: (mailing list name, email message).
91 102  
... ... @@ -101,6 +112,10 @@ class Command(BaseCommand, object):
101 112 mbox_path = os.path.join(mailinglist_dir, mbox, mbox)
102 113 mailinglist_name = mbox.split('.')[0]
103 114  
  115 + # Check if the mailinglist is set not to be imported
  116 + if exclude_lists and mailinglist_name in exclude_lists:
  117 + continue
  118 +
104 119 # Find the index of the last imported message
105 120 if all:
106 121 n_msgs = 0
... ... @@ -154,10 +169,7 @@ class Command(BaseCommand, object):
154 169 email_addr = self.EMAIL_ADDR_CACHE.get(from_)
155 170 if email_addr is None:
156 171 email_addr = EmailAddress.objects.get_or_create(
157   - address=from_,
158   - real_name=real_name[:64]
159   - )[0]
160   -
  172 + address=from_)[0]
161 173 self.EMAIL_ADDR_CACHE[from_] = email_addr
162 174  
163 175 if not email_addr.real_name and real_name:
... ... @@ -179,16 +191,18 @@ class Command(BaseCommand, object):
179 191 email.save()
180 192  
181 193 @transaction.commit_manually
182   - def import_emails(self, archives_path, all):
  194 + def import_emails(self, archives_path, all, exclude_lists=None):
183 195 """Get emails from the filesystem from the `archives_path`
184 196 and store them into the database. If `all` is set to True all
185   - the filesystem storage will be imported otherwise the importation
186   - will resume from the last message previously imported.
  197 + the filesystem storage will be imported otherwise the
  198 + importation will resume from the last message previously
  199 + imported. The lists set in `exclude_lists` won't be imported.
187 200  
188 201 """
189 202  
190 203 count = 0
191   - for mailinglist_name, msg, index in self.get_emails(archives_path, all):
  204 + email_generator = self.get_emails(archives_path, all, exclude_lists)
  205 + for mailinglist_name, msg, index in email_generator:
192 206 try:
193 207 self.save_email(mailinglist_name, msg, index)
194 208 except:
... ... @@ -206,6 +220,20 @@ class Command(BaseCommand, object):
206 220 def handle(self, *args, **options):
207 221 """Main command method."""
208 222  
  223 + lock_file = '/var/lock/colab/import_emails.lock'
  224 +
  225 + # Already running, so quit
  226 + if os.path.exists(lock_file):
  227 + self.log(("This script is already running. (If your are sure it's "
  228 + "not please delete the lock file in %s')") % lock_file)
  229 + sys.exit(0)
  230 +
  231 + if not os.path.exists(os.path.dirname(lock_file)):
  232 + os.mkdir(os.path.dirname(lock_file), 0755)
  233 +
  234 + run_lock = file(lock_file, 'w')
  235 + run_lock.close()
  236 +
209 237 archives_path = options.get('archives_path')
210 238 self.log('Using archives_path `%s`' % self.default_archives_path)
211 239  
... ... @@ -213,5 +241,8 @@ class Command(BaseCommand, object):
213 241 raise CommandError('archives_path (%s) does not exist' %
214 242 archives_path)
215 243  
216   - self.import_emails(archives_path, options.get('all'))
  244 + self.import_emails(archives_path,
  245 + options.get('all'), options.get('exclude_lists'))
  246 +
  247 + os.remove(lock_file)
217 248  
218 249 \ No newline at end of file
... ...
colab/super_archives/migrations/0001_initial.py 0 → 100644
... ... @@ -0,0 +1,255 @@
  1 +# encoding: utf-8
  2 +import datetime
  3 +from south.db import db
  4 +from south.v2 import SchemaMigration
  5 +from django.db import models
  6 +
  7 +class Migration(SchemaMigration):
  8 +
  9 + def forwards(self, orm):
  10 +
  11 + # Adding model 'PageHit'
  12 + db.create_table('super_archives_pagehit', (
  13 + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
  14 + ('url_path', self.gf('django.db.models.fields.CharField')(unique=True, max_length=2048, db_index=True)),
  15 + ('hit_count', self.gf('django.db.models.fields.IntegerField')(default=0)),
  16 + ))
  17 + db.send_create_signal('super_archives', ['PageHit'])
  18 +
  19 + # Adding model 'EmailAddress'
  20 + db.create_table('super_archives_emailaddress', (
  21 + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
  22 + ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='emails', null=True, to=orm['auth.User'])),
  23 + ('address', self.gf('django.db.models.fields.EmailField')(unique=True, max_length=75)),
  24 + ('real_name', self.gf('django.db.models.fields.CharField')(max_length=64, blank=True)),
  25 + ('md5', self.gf('django.db.models.fields.CharField')(max_length=32, null=True)),
  26 + ))
  27 + db.send_create_signal('super_archives', ['EmailAddress'])
  28 +
  29 + # Adding model 'UserProfile'
  30 + db.create_table('super_archives_userprofile', (
  31 + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
  32 + ('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True)),
  33 + ('institution', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
  34 + ('role', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
  35 + ('twitter', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
  36 + ('facebook', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
  37 + ('google_talk', self.gf('django.db.models.fields.EmailField')(max_length=75, null=True)),
  38 + ('webpage', self.gf('django.db.models.fields.CharField')(max_length=256)),
  39 + ))
  40 + db.send_create_signal('super_archives', ['UserProfile'])
  41 +
  42 + # Adding model 'MailingList'
  43 + db.create_table('super_archives_mailinglist', (
  44 + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
  45 + ('name', self.gf('django.db.models.fields.CharField')(max_length=80)),
  46 + ('email', self.gf('django.db.models.fields.EmailField')(max_length=75)),
  47 + ('description', self.gf('django.db.models.fields.TextField')()),
  48 + ('logo', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
  49 + ('last_imported_index', self.gf('django.db.models.fields.IntegerField')(default=0)),
  50 + ))
  51 + db.send_create_signal('super_archives', ['MailingList'])
  52 +
  53 + # Adding model 'MailingListMembership'
  54 + db.create_table('super_archives_mailinglistmembership', (
  55 + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
  56 + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
  57 + ('mailinglist', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['super_archives.MailingList'])),
  58 + ))
  59 + db.send_create_signal('super_archives', ['MailingListMembership'])
  60 +
  61 + # Adding model 'Thread'
  62 + db.create_table('super_archives_thread', (
  63 + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
  64 + ('subject_token', self.gf('django.db.models.fields.CharField')(max_length=512)),
  65 + ('mailinglist', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['super_archives.MailingList'])),
  66 + ('latest_message', self.gf('django.db.models.fields.related.OneToOneField')(related_name='+', unique=True, null=True, to=orm['super_archives.Message'])),
  67 + ))
  68 + db.send_create_signal('super_archives', ['Thread'])
  69 +
  70 + # Adding unique constraint on 'Thread', fields ['subject_token', 'mailinglist']
  71 + db.create_unique('super_archives_thread', ['subject_token', 'mailinglist_id'])
  72 +
  73 + # Adding model 'Vote'
  74 + db.create_table('super_archives_vote', (
  75 + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
  76 + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
  77 + ('message', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['super_archives.Message'])),
  78 + ))
  79 + db.send_create_signal('super_archives', ['Vote'])
  80 +
  81 + # Adding unique constraint on 'Vote', fields ['user', 'message']
  82 + db.create_unique('super_archives_vote', ['user_id', 'message_id'])
  83 +
  84 + # Adding model 'Message'
  85 + db.create_table('super_archives_message', (
  86 + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
  87 + ('from_address', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['super_archives.EmailAddress'])),
  88 + ('mailinglist', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['super_archives.MailingList'])),
  89 + ('thread', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['super_archives.Thread'], null=True)),
  90 + ('subject', self.gf('django.db.models.fields.CharField')(max_length=512)),
  91 + ('subject_clean', self.gf('django.db.models.fields.CharField')(max_length=512)),
  92 + ('body', self.gf('django.db.models.fields.TextField')(default='')),
  93 + ('received_time', self.gf('django.db.models.fields.DateTimeField')()),
  94 + ('message_id', self.gf('django.db.models.fields.CharField')(max_length=512)),
  95 + ))
  96 + db.send_create_signal('super_archives', ['Message'])
  97 +
  98 + # Adding model 'MessageMetadata'
  99 + db.create_table('super_archives_messagemetadata', (
  100 + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
  101 + ('Message', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['super_archives.Message'])),
  102 + ('name', self.gf('django.db.models.fields.CharField')(max_length=512)),
  103 + ('value', self.gf('django.db.models.fields.TextField')()),
  104 + ))
  105 + db.send_create_signal('super_archives', ['MessageMetadata'])
  106 +
  107 +
  108 + def backwards(self, orm):
  109 +
  110 + # Removing unique constraint on 'Vote', fields ['user', 'message']
  111 + db.delete_unique('super_archives_vote', ['user_id', 'message_id'])
  112 +
  113 + # Removing unique constraint on 'Thread', fields ['subject_token', 'mailinglist']
  114 + db.delete_unique('super_archives_thread', ['subject_token', 'mailinglist_id'])
  115 +
  116 + # Deleting model 'PageHit'
  117 + db.delete_table('super_archives_pagehit')
  118 +
  119 + # Deleting model 'EmailAddress'
  120 + db.delete_table('super_archives_emailaddress')
  121 +
  122 + # Deleting model 'UserProfile'
  123 + db.delete_table('super_archives_userprofile')
  124 +
  125 + # Deleting model 'MailingList'
  126 + db.delete_table('super_archives_mailinglist')
  127 +
  128 + # Deleting model 'MailingListMembership'
  129 + db.delete_table('super_archives_mailinglistmembership')
  130 +
  131 + # Deleting model 'Thread'
  132 + db.delete_table('super_archives_thread')
  133 +
  134 + # Deleting model 'Vote'
  135 + db.delete_table('super_archives_vote')
  136 +
  137 + # Deleting model 'Message'
  138 + db.delete_table('super_archives_message')
  139 +
  140 + # Deleting model 'MessageMetadata'
  141 + db.delete_table('super_archives_messagemetadata')
  142 +
  143 +
  144 + models = {
  145 + 'auth.group': {
  146 + 'Meta': {'object_name': 'Group'},
  147 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  148 + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
  149 + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
  150 + },
  151 + 'auth.permission': {
  152 + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
  153 + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  154 + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
  155 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  156 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
  157 + },
  158 + 'auth.user': {
  159 + 'Meta': {'object_name': 'User'},
  160 + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  161 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
  162 + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  163 + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
  164 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  165 + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
  166 + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  167 + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  168 + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  169 + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  170 + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
  171 + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
  172 + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
  173 + },
  174 + 'contenttypes.contenttype': {
  175 + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
  176 + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  177 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  178 + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  179 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
  180 + },
  181 + 'super_archives.emailaddress': {
  182 + 'Meta': {'object_name': 'EmailAddress'},
  183 + 'address': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}),
  184 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  185 + 'md5': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  186 + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
  187 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emails'", 'null': 'True', 'to': "orm['auth.User']"})
  188 + },
  189 + 'super_archives.mailinglist': {
  190 + 'Meta': {'object_name': 'MailingList'},
  191 + 'description': ('django.db.models.fields.TextField', [], {}),
  192 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
  193 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  194 + 'last_imported_index': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  195 + 'logo': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
  196 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'})
  197 + },
  198 + 'super_archives.mailinglistmembership': {
  199 + 'Meta': {'object_name': 'MailingListMembership'},
  200 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  201 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  202 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  203 + },
  204 + 'super_archives.message': {
  205 + 'Meta': {'object_name': 'Message'},
  206 + 'body': ('django.db.models.fields.TextField', [], {'default': "''"}),
  207 + 'from_address': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.EmailAddress']"}),
  208 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  209 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  210 + 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  211 + 'received_time': ('django.db.models.fields.DateTimeField', [], {}),
  212 + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  213 + 'subject_clean': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  214 + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Thread']", 'null': 'True'})
  215 + },
  216 + 'super_archives.messagemetadata': {
  217 + 'Message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  218 + 'Meta': {'object_name': 'MessageMetadata'},
  219 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  220 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  221 + 'value': ('django.db.models.fields.TextField', [], {})
  222 + },
  223 + 'super_archives.pagehit': {
  224 + 'Meta': {'object_name': 'PageHit'},
  225 + 'hit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  226 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  227 + 'url_path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '2048', 'db_index': 'True'})
  228 + },
  229 + 'super_archives.thread': {
  230 + 'Meta': {'unique_together': "(('subject_token', 'mailinglist'),)", 'object_name': 'Thread'},
  231 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  232 + 'latest_message': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'unique': 'True', 'null': 'True', 'to': "orm['super_archives.Message']"}),
  233 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  234 + 'subject_token': ('django.db.models.fields.CharField', [], {'max_length': '512'})
  235 + },
  236 + 'super_archives.userprofile': {
  237 + 'Meta': {'object_name': 'UserProfile'},
  238 + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  239 + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}),
  240 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  241 + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  242 + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  243 + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  244 + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}),
  245 + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256'})
  246 + },
  247 + 'super_archives.vote': {
  248 + 'Meta': {'unique_together': "(('user', 'message'),)", 'object_name': 'Vote'},
  249 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  250 + 'message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  251 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  252 + }
  253 + }
  254 +
  255 + complete_apps = ['super_archives']
... ...
colab/super_archives/migrations/0002_auto__add_field_userprofile_verification_hash.py 0 → 100644
... ... @@ -0,0 +1,133 @@
  1 +# encoding: utf-8
  2 +import datetime
  3 +from south.db import db
  4 +from south.v2 import SchemaMigration
  5 +from django.db import models
  6 +
  7 +class Migration(SchemaMigration):
  8 +
  9 + def forwards(self, orm):
  10 +
  11 + # Adding field 'UserProfile.verification_hash'
  12 + db.add_column('super_archives_userprofile', 'verification_hash', self.gf('django.db.models.fields.CharField')(max_length=32, null=True), keep_default=False)
  13 +
  14 +
  15 + def backwards(self, orm):
  16 +
  17 + # Deleting field 'UserProfile.verification_hash'
  18 + db.delete_column('super_archives_userprofile', 'verification_hash')
  19 +
  20 +
  21 + models = {
  22 + 'auth.group': {
  23 + 'Meta': {'object_name': 'Group'},
  24 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  25 + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
  26 + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
  27 + },
  28 + 'auth.permission': {
  29 + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
  30 + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  31 + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
  32 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  33 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
  34 + },
  35 + 'auth.user': {
  36 + 'Meta': {'object_name': 'User'},
  37 + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  38 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
  39 + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  40 + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
  41 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  42 + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
  43 + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  44 + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  45 + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  46 + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  47 + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
  48 + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
  49 + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
  50 + },
  51 + 'contenttypes.contenttype': {
  52 + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
  53 + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  54 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  55 + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  56 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
  57 + },
  58 + 'super_archives.emailaddress': {
  59 + 'Meta': {'object_name': 'EmailAddress'},
  60 + 'address': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}),
  61 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  62 + 'md5': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  63 + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
  64 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emails'", 'null': 'True', 'to': "orm['auth.User']"})
  65 + },
  66 + 'super_archives.mailinglist': {
  67 + 'Meta': {'object_name': 'MailingList'},
  68 + 'description': ('django.db.models.fields.TextField', [], {}),
  69 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
  70 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  71 + 'last_imported_index': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  72 + 'logo': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
  73 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'})
  74 + },
  75 + 'super_archives.mailinglistmembership': {
  76 + 'Meta': {'object_name': 'MailingListMembership'},
  77 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  78 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  79 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  80 + },
  81 + 'super_archives.message': {
  82 + 'Meta': {'object_name': 'Message'},
  83 + 'body': ('django.db.models.fields.TextField', [], {'default': "''"}),
  84 + 'from_address': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.EmailAddress']"}),
  85 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  86 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  87 + 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  88 + 'received_time': ('django.db.models.fields.DateTimeField', [], {}),
  89 + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  90 + 'subject_clean': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  91 + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Thread']", 'null': 'True'})
  92 + },
  93 + 'super_archives.messagemetadata': {
  94 + 'Message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  95 + 'Meta': {'object_name': 'MessageMetadata'},
  96 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  97 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  98 + 'value': ('django.db.models.fields.TextField', [], {})
  99 + },
  100 + 'super_archives.pagehit': {
  101 + 'Meta': {'object_name': 'PageHit'},
  102 + 'hit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  103 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  104 + 'url_path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '2048', 'db_index': 'True'})
  105 + },
  106 + 'super_archives.thread': {
  107 + 'Meta': {'unique_together': "(('subject_token', 'mailinglist'),)", 'object_name': 'Thread'},
  108 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  109 + 'latest_message': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'unique': 'True', 'null': 'True', 'to': "orm['super_archives.Message']"}),
  110 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  111 + 'subject_token': ('django.db.models.fields.CharField', [], {'max_length': '512'})
  112 + },
  113 + 'super_archives.userprofile': {
  114 + 'Meta': {'object_name': 'UserProfile'},
  115 + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  116 + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}),
  117 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  118 + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  119 + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  120 + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  121 + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}),
  122 + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  123 + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256'})
  124 + },
  125 + 'super_archives.vote': {
  126 + 'Meta': {'unique_together': "(('user', 'message'),)", 'object_name': 'Vote'},
  127 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  128 + 'message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  129 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  130 + }
  131 + }
  132 +
  133 + complete_apps = ['super_archives']
... ...
colab/super_archives/migrations/0003_auto__add_field_thread_score.py 0 → 100644
... ... @@ -0,0 +1,134 @@
  1 +# encoding: utf-8
  2 +import datetime
  3 +from south.db import db
  4 +from south.v2 import SchemaMigration
  5 +from django.db import models
  6 +
  7 +class Migration(SchemaMigration):
  8 +
  9 + def forwards(self, orm):
  10 +
  11 + # Adding field 'Thread.score'
  12 + db.add_column('super_archives_thread', 'score', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False)
  13 +
  14 +
  15 + def backwards(self, orm):
  16 +
  17 + # Deleting field 'Thread.score'
  18 + db.delete_column('super_archives_thread', 'score')
  19 +
  20 +
  21 + models = {
  22 + 'auth.group': {
  23 + 'Meta': {'object_name': 'Group'},
  24 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  25 + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
  26 + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
  27 + },
  28 + 'auth.permission': {
  29 + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
  30 + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  31 + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
  32 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  33 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
  34 + },
  35 + 'auth.user': {
  36 + 'Meta': {'object_name': 'User'},
  37 + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  38 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
  39 + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  40 + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
  41 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  42 + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
  43 + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  44 + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  45 + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  46 + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  47 + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
  48 + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
  49 + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
  50 + },
  51 + 'contenttypes.contenttype': {
  52 + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
  53 + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  54 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  55 + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  56 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
  57 + },
  58 + 'super_archives.emailaddress': {
  59 + 'Meta': {'object_name': 'EmailAddress'},
  60 + 'address': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}),
  61 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  62 + 'md5': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  63 + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
  64 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emails'", 'null': 'True', 'to': "orm['auth.User']"})
  65 + },
  66 + 'super_archives.mailinglist': {
  67 + 'Meta': {'object_name': 'MailingList'},
  68 + 'description': ('django.db.models.fields.TextField', [], {}),
  69 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
  70 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  71 + 'last_imported_index': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  72 + 'logo': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
  73 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'})
  74 + },
  75 + 'super_archives.mailinglistmembership': {
  76 + 'Meta': {'object_name': 'MailingListMembership'},
  77 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  78 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  79 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  80 + },
  81 + 'super_archives.message': {
  82 + 'Meta': {'object_name': 'Message'},
  83 + 'body': ('django.db.models.fields.TextField', [], {'default': "''"}),
  84 + 'from_address': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.EmailAddress']"}),
  85 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  86 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  87 + 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  88 + 'received_time': ('django.db.models.fields.DateTimeField', [], {}),
  89 + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  90 + 'subject_clean': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  91 + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Thread']", 'null': 'True'})
  92 + },
  93 + 'super_archives.messagemetadata': {
  94 + 'Message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  95 + 'Meta': {'object_name': 'MessageMetadata'},
  96 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  97 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  98 + 'value': ('django.db.models.fields.TextField', [], {})
  99 + },
  100 + 'super_archives.pagehit': {
  101 + 'Meta': {'object_name': 'PageHit'},
  102 + 'hit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  103 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  104 + 'url_path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '2048', 'db_index': 'True'})
  105 + },
  106 + 'super_archives.thread': {
  107 + 'Meta': {'unique_together': "(('subject_token', 'mailinglist'),)", 'object_name': 'Thread'},
  108 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  109 + 'latest_message': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'unique': 'True', 'null': 'True', 'to': "orm['super_archives.Message']"}),
  110 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  111 + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  112 + 'subject_token': ('django.db.models.fields.CharField', [], {'max_length': '512'})
  113 + },
  114 + 'super_archives.userprofile': {
  115 + 'Meta': {'object_name': 'UserProfile'},
  116 + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  117 + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}),
  118 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  119 + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  120 + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  121 + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  122 + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}),
  123 + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  124 + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256'})
  125 + },
  126 + 'super_archives.vote': {
  127 + 'Meta': {'unique_together': "(('user', 'message'),)", 'object_name': 'Vote'},
  128 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  129 + 'message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  130 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  131 + }
  132 + }
  133 +
  134 + complete_apps = ['super_archives']
... ...
colab/super_archives/migrations/0004_auto__add_field_vote_created.py 0 → 100644
... ... @@ -0,0 +1,135 @@
  1 +# encoding: utf-8
  2 +import datetime
  3 +from south.db import db
  4 +from south.v2 import SchemaMigration
  5 +from django.db import models
  6 +
  7 +class Migration(SchemaMigration):
  8 +
  9 + def forwards(self, orm):
  10 +
  11 + # Adding field 'Vote.created'
  12 + db.add_column('super_archives_vote', 'created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, default=datetime.datetime(2012, 1, 19, 18, 8, 46, 813949), blank=True), keep_default=False)
  13 +
  14 +
  15 + def backwards(self, orm):
  16 +
  17 + # Deleting field 'Vote.created'
  18 + db.delete_column('super_archives_vote', 'created')
  19 +
  20 +
  21 + models = {
  22 + 'auth.group': {
  23 + 'Meta': {'object_name': 'Group'},
  24 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  25 + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
  26 + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
  27 + },
  28 + 'auth.permission': {
  29 + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
  30 + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  31 + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
  32 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  33 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
  34 + },
  35 + 'auth.user': {
  36 + 'Meta': {'object_name': 'User'},
  37 + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  38 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
  39 + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  40 + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
  41 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  42 + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
  43 + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  44 + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  45 + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  46 + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  47 + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
  48 + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
  49 + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
  50 + },
  51 + 'contenttypes.contenttype': {
  52 + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
  53 + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  54 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  55 + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  56 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
  57 + },
  58 + 'super_archives.emailaddress': {
  59 + 'Meta': {'object_name': 'EmailAddress'},
  60 + 'address': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}),
  61 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  62 + 'md5': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  63 + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
  64 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emails'", 'null': 'True', 'to': "orm['auth.User']"})
  65 + },
  66 + 'super_archives.mailinglist': {
  67 + 'Meta': {'object_name': 'MailingList'},
  68 + 'description': ('django.db.models.fields.TextField', [], {}),
  69 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
  70 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  71 + 'last_imported_index': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  72 + 'logo': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
  73 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'})
  74 + },
  75 + 'super_archives.mailinglistmembership': {
  76 + 'Meta': {'object_name': 'MailingListMembership'},
  77 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  78 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  79 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  80 + },
  81 + 'super_archives.message': {
  82 + 'Meta': {'object_name': 'Message'},
  83 + 'body': ('django.db.models.fields.TextField', [], {'default': "''"}),
  84 + 'from_address': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.EmailAddress']"}),
  85 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  86 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  87 + 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  88 + 'received_time': ('django.db.models.fields.DateTimeField', [], {}),
  89 + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  90 + 'subject_clean': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  91 + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Thread']", 'null': 'True'})
  92 + },
  93 + 'super_archives.messagemetadata': {
  94 + 'Message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  95 + 'Meta': {'object_name': 'MessageMetadata'},
  96 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  97 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  98 + 'value': ('django.db.models.fields.TextField', [], {})
  99 + },
  100 + 'super_archives.pagehit': {
  101 + 'Meta': {'object_name': 'PageHit'},
  102 + 'hit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  103 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  104 + 'url_path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '2048', 'db_index': 'True'})
  105 + },
  106 + 'super_archives.thread': {
  107 + 'Meta': {'unique_together': "(('subject_token', 'mailinglist'),)", 'object_name': 'Thread'},
  108 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  109 + 'latest_message': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'unique': 'True', 'null': 'True', 'to': "orm['super_archives.Message']"}),
  110 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  111 + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  112 + 'subject_token': ('django.db.models.fields.CharField', [], {'max_length': '512'})
  113 + },
  114 + 'super_archives.userprofile': {
  115 + 'Meta': {'object_name': 'UserProfile'},
  116 + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  117 + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}),
  118 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  119 + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  120 + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  121 + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  122 + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}),
  123 + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  124 + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256'})
  125 + },
  126 + 'super_archives.vote': {
  127 + 'Meta': {'unique_together': "(('user', 'message'),)", 'object_name': 'Vote'},
  128 + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
  129 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  130 + 'message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  131 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  132 + }
  133 + }
  134 +
  135 + complete_apps = ['super_archives']
... ...
colab/super_archives/migrations/0005_auto__add_field_message_spam__add_field_thread_spam.py 0 → 100644
... ... @@ -0,0 +1,143 @@
  1 +# encoding: utf-8
  2 +import datetime
  3 +from south.db import db
  4 +from south.v2 import SchemaMigration
  5 +from django.db import models
  6 +
  7 +class Migration(SchemaMigration):
  8 +
  9 + def forwards(self, orm):
  10 +
  11 + # Adding field 'Message.spam'
  12 + db.add_column('super_archives_message', 'spam', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
  13 +
  14 + # Adding field 'Thread.spam'
  15 + db.add_column('super_archives_thread', 'spam', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
  16 +
  17 +
  18 + def backwards(self, orm):
  19 +
  20 + # Deleting field 'Message.spam'
  21 + db.delete_column('super_archives_message', 'spam')
  22 +
  23 + # Deleting field 'Thread.spam'
  24 + db.delete_column('super_archives_thread', 'spam')
  25 +
  26 +
  27 + models = {
  28 + 'auth.group': {
  29 + 'Meta': {'object_name': 'Group'},
  30 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  31 + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
  32 + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
  33 + },
  34 + 'auth.permission': {
  35 + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
  36 + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  37 + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
  38 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  39 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
  40 + },
  41 + 'auth.user': {
  42 + 'Meta': {'object_name': 'User'},
  43 + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  44 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
  45 + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  46 + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
  47 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  48 + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
  49 + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  50 + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  51 + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  52 + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  53 + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
  54 + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
  55 + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
  56 + },
  57 + 'contenttypes.contenttype': {
  58 + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
  59 + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  60 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  61 + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  62 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
  63 + },
  64 + 'super_archives.emailaddress': {
  65 + 'Meta': {'object_name': 'EmailAddress'},
  66 + 'address': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}),
  67 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  68 + 'md5': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  69 + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
  70 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emails'", 'null': 'True', 'to': "orm['auth.User']"})
  71 + },
  72 + 'super_archives.mailinglist': {
  73 + 'Meta': {'object_name': 'MailingList'},
  74 + 'description': ('django.db.models.fields.TextField', [], {}),
  75 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
  76 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  77 + 'last_imported_index': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  78 + 'logo': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
  79 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'})
  80 + },
  81 + 'super_archives.mailinglistmembership': {
  82 + 'Meta': {'object_name': 'MailingListMembership'},
  83 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  84 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  85 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  86 + },
  87 + 'super_archives.message': {
  88 + 'Meta': {'object_name': 'Message'},
  89 + 'body': ('django.db.models.fields.TextField', [], {'default': "''"}),
  90 + 'from_address': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.EmailAddress']"}),
  91 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  92 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  93 + 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  94 + 'received_time': ('django.db.models.fields.DateTimeField', [], {}),
  95 + 'spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  96 + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  97 + 'subject_clean': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  98 + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Thread']", 'null': 'True'})
  99 + },
  100 + 'super_archives.messagemetadata': {
  101 + 'Message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  102 + 'Meta': {'object_name': 'MessageMetadata'},
  103 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  104 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  105 + 'value': ('django.db.models.fields.TextField', [], {})
  106 + },
  107 + 'super_archives.pagehit': {
  108 + 'Meta': {'object_name': 'PageHit'},
  109 + 'hit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  110 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  111 + 'url_path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '2048', 'db_index': 'True'})
  112 + },
  113 + 'super_archives.thread': {
  114 + 'Meta': {'unique_together': "(('subject_token', 'mailinglist'),)", 'object_name': 'Thread'},
  115 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  116 + 'latest_message': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'unique': 'True', 'null': 'True', 'to': "orm['super_archives.Message']"}),
  117 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  118 + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  119 + 'spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  120 + 'subject_token': ('django.db.models.fields.CharField', [], {'max_length': '512'})
  121 + },
  122 + 'super_archives.userprofile': {
  123 + 'Meta': {'object_name': 'UserProfile'},
  124 + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  125 + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}),
  126 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  127 + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  128 + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  129 + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  130 + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}),
  131 + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  132 + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256'})
  133 + },
  134 + 'super_archives.vote': {
  135 + 'Meta': {'unique_together': "(('user', 'message'),)", 'object_name': 'Vote'},
  136 + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
  137 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  138 + 'message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  139 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  140 + }
  141 + }
  142 +
  143 + complete_apps = ['super_archives']
... ...
colab/super_archives/migrations/0006_auto.py 0 → 100644
... ... @@ -0,0 +1,143 @@
  1 +# encoding: utf-8
  2 +import datetime
  3 +from south.db import db
  4 +from south.v2 import SchemaMigration
  5 +from django.db import models
  6 +
  7 +class Migration(SchemaMigration):
  8 +
  9 + def forwards(self, orm):
  10 +
  11 + # Adding index on 'Message', fields ['subject_clean']
  12 + db.create_index('super_archives_message', ['subject_clean'])
  13 +
  14 + # Adding index on 'Message', fields ['subject']
  15 + db.create_index('super_archives_message', ['subject'])
  16 +
  17 +
  18 + def backwards(self, orm):
  19 +
  20 + # Removing index on 'Message', fields ['subject']
  21 + db.delete_index('super_archives_message', ['subject'])
  22 +
  23 + # Removing index on 'Message', fields ['subject_clean']
  24 + db.delete_index('super_archives_message', ['subject_clean'])
  25 +
  26 +
  27 + models = {
  28 + 'auth.group': {
  29 + 'Meta': {'object_name': 'Group'},
  30 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  31 + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
  32 + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
  33 + },
  34 + 'auth.permission': {
  35 + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
  36 + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  37 + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
  38 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  39 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
  40 + },
  41 + 'auth.user': {
  42 + 'Meta': {'object_name': 'User'},
  43 + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  44 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
  45 + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  46 + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
  47 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  48 + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
  49 + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  50 + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  51 + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  52 + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  53 + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
  54 + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
  55 + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
  56 + },
  57 + 'contenttypes.contenttype': {
  58 + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
  59 + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  60 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  61 + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  62 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
  63 + },
  64 + 'super_archives.emailaddress': {
  65 + 'Meta': {'object_name': 'EmailAddress'},
  66 + 'address': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}),
  67 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  68 + 'md5': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  69 + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
  70 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emails'", 'null': 'True', 'to': "orm['auth.User']"})
  71 + },
  72 + 'super_archives.mailinglist': {
  73 + 'Meta': {'object_name': 'MailingList'},
  74 + 'description': ('django.db.models.fields.TextField', [], {}),
  75 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
  76 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  77 + 'last_imported_index': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  78 + 'logo': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
  79 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'})
  80 + },
  81 + 'super_archives.mailinglistmembership': {
  82 + 'Meta': {'object_name': 'MailingListMembership'},
  83 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  84 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  85 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  86 + },
  87 + 'super_archives.message': {
  88 + 'Meta': {'object_name': 'Message'},
  89 + 'body': ('django.db.models.fields.TextField', [], {'default': "''"}),
  90 + 'from_address': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.EmailAddress']"}),
  91 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  92 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  93 + 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  94 + 'received_time': ('django.db.models.fields.DateTimeField', [], {}),
  95 + 'spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  96 + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '512', 'db_index': 'True'}),
  97 + 'subject_clean': ('django.db.models.fields.CharField', [], {'max_length': '512', 'db_index': 'True'}),
  98 + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Thread']", 'null': 'True'})
  99 + },
  100 + 'super_archives.messagemetadata': {
  101 + 'Message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  102 + 'Meta': {'object_name': 'MessageMetadata'},
  103 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  104 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  105 + 'value': ('django.db.models.fields.TextField', [], {})
  106 + },
  107 + 'super_archives.pagehit': {
  108 + 'Meta': {'object_name': 'PageHit'},
  109 + 'hit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  110 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  111 + 'url_path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '2048', 'db_index': 'True'})
  112 + },
  113 + 'super_archives.thread': {
  114 + 'Meta': {'unique_together': "(('subject_token', 'mailinglist'),)", 'object_name': 'Thread'},
  115 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  116 + 'latest_message': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'unique': 'True', 'null': 'True', 'to': "orm['super_archives.Message']"}),
  117 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  118 + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  119 + 'spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  120 + 'subject_token': ('django.db.models.fields.CharField', [], {'max_length': '512'})
  121 + },
  122 + 'super_archives.userprofile': {
  123 + 'Meta': {'object_name': 'UserProfile'},
  124 + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  125 + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}),
  126 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  127 + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  128 + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  129 + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  130 + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}),
  131 + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  132 + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256'})
  133 + },
  134 + 'super_archives.vote': {
  135 + 'Meta': {'unique_together': "(('user', 'message'),)", 'object_name': 'Vote'},
  136 + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
  137 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  138 + 'message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  139 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  140 + }
  141 + }
  142 +
  143 + complete_apps = ['super_archives']
... ...
colab/super_archives/migrations/0007_auto.py 0 → 100644
... ... @@ -0,0 +1,137 @@
  1 +# encoding: utf-8
  2 +import datetime
  3 +from south.db import db
  4 +from south.v2 import SchemaMigration
  5 +from django.db import models
  6 +
  7 +class Migration(SchemaMigration):
  8 +
  9 + def forwards(self, orm):
  10 +
  11 + # Adding index on 'EmailAddress', fields ['real_name']
  12 + db.create_index('super_archives_emailaddress', ['real_name'])
  13 +
  14 +
  15 + def backwards(self, orm):
  16 +
  17 + # Removing index on 'EmailAddress', fields ['real_name']
  18 + db.delete_index('super_archives_emailaddress', ['real_name'])
  19 +
  20 +
  21 + models = {
  22 + 'auth.group': {
  23 + 'Meta': {'object_name': 'Group'},
  24 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  25 + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
  26 + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
  27 + },
  28 + 'auth.permission': {
  29 + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
  30 + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  31 + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
  32 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  33 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
  34 + },
  35 + 'auth.user': {
  36 + 'Meta': {'object_name': 'User'},
  37 + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  38 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
  39 + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  40 + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
  41 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  42 + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
  43 + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  44 + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  45 + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
  46 + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
  47 + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
  48 + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
  49 + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
  50 + },
  51 + 'contenttypes.contenttype': {
  52 + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
  53 + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  54 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  55 + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
  56 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
  57 + },
  58 + 'super_archives.emailaddress': {
  59 + 'Meta': {'object_name': 'EmailAddress'},
  60 + 'address': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}),
  61 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  62 + 'md5': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  63 + 'real_name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'blank': 'True'}),
  64 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emails'", 'null': 'True', 'to': "orm['auth.User']"})
  65 + },
  66 + 'super_archives.mailinglist': {
  67 + 'Meta': {'object_name': 'MailingList'},
  68 + 'description': ('django.db.models.fields.TextField', [], {}),
  69 + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
  70 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  71 + 'last_imported_index': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  72 + 'logo': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
  73 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'})
  74 + },
  75 + 'super_archives.mailinglistmembership': {
  76 + 'Meta': {'object_name': 'MailingListMembership'},
  77 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  78 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  79 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  80 + },
  81 + 'super_archives.message': {
  82 + 'Meta': {'object_name': 'Message'},
  83 + 'body': ('django.db.models.fields.TextField', [], {'default': "''"}),
  84 + 'from_address': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.EmailAddress']"}),
  85 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  86 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  87 + 'message_id': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  88 + 'received_time': ('django.db.models.fields.DateTimeField', [], {}),
  89 + 'spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  90 + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '512', 'db_index': 'True'}),
  91 + 'subject_clean': ('django.db.models.fields.CharField', [], {'max_length': '512', 'db_index': 'True'}),
  92 + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Thread']", 'null': 'True'})
  93 + },
  94 + 'super_archives.messagemetadata': {
  95 + 'Message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  96 + 'Meta': {'object_name': 'MessageMetadata'},
  97 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  98 + 'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
  99 + 'value': ('django.db.models.fields.TextField', [], {})
  100 + },
  101 + 'super_archives.pagehit': {
  102 + 'Meta': {'object_name': 'PageHit'},
  103 + 'hit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  104 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  105 + 'url_path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '2048', 'db_index': 'True'})
  106 + },
  107 + 'super_archives.thread': {
  108 + 'Meta': {'unique_together': "(('subject_token', 'mailinglist'),)", 'object_name': 'Thread'},
  109 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  110 + 'latest_message': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'+'", 'unique': 'True', 'null': 'True', 'to': "orm['super_archives.Message']"}),
  111 + 'mailinglist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.MailingList']"}),
  112 + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
  113 + 'spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
  114 + 'subject_token': ('django.db.models.fields.CharField', [], {'max_length': '512'})
  115 + },
  116 + 'super_archives.userprofile': {
  117 + 'Meta': {'object_name': 'UserProfile'},
  118 + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  119 + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}),
  120 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  121 + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  122 + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  123 + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
  124 + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}),
  125 + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
  126 + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256'})
  127 + },
  128 + 'super_archives.vote': {
  129 + 'Meta': {'unique_together': "(('user', 'message'),)", 'object_name': 'Vote'},
  130 + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
  131 + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
  132 + 'message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['super_archives.Message']"}),
  133 + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
  134 + }
  135 + }
  136 +
  137 + complete_apps = ['super_archives']
... ...
colab/super_archives/migrations/__init__.py 0 → 100644
colab/super_archives/models.py
1   -# -*- coding: utf8 -*-
  1 +# -*- coding: utf-8 -*-
2 2  
  3 +import datetime
3 4 from hashlib import md5
4 5  
5 6 from django.db import models
6 7 from django.conf import settings
7 8 from django.contrib.auth.models import User
8   -from django.core.urlresolvers import reverse
  9 +from django.core.urlresolvers import reverse, NoReverseMatch
  10 +
  11 +
  12 +class NotSpamManager(models.Manager):
  13 + """Only return objects which are not marked as spam."""
  14 +
  15 + def get_query_set(self):
  16 + return super(NotSpamManager, self).get_query_set().exclude(spam=True)
  17 +
  18 +
  19 +class PageHit(models.Model):
  20 + url_path = models.CharField(max_length=2048, unique=True, db_index=True)
  21 + hit_count = models.IntegerField(default=0)
9 22  
10 23  
11 24 class EmailAddress(models.Model):
12 25 user = models.ForeignKey(User, null=True, related_name='emails')
13 26 address = models.EmailField(unique=True)
14   - real_name = models.CharField(max_length=64, blank=True)
  27 + real_name = models.CharField(max_length=64, blank=True, db_index=True)
15 28 md5 = models.CharField(max_length=32, null=True)
16 29  
17 30 def save(self, *args, **kwargs):
... ... @@ -28,12 +41,14 @@ class EmailAddress(models.Model):
28 41  
29 42 def get_profile_link(self):
30 43 if self.user:
31   - return reverse('colab.views.user_profile_username',
32   - args=[self.user.username])
  44 + return reverse('user_profile', args=[self.user.username])
33 45 else:
34   - return reverse('colab.views.user_profile_emailhash',
  46 + return reverse('colab.views.userprofile.by_emailhash',
35 47 args=[self.md5])
36 48  
  49 + def __unicode__(self):
  50 + return '"%s" <%s>' % (self.get_full_name(), self.address)
  51 +
37 52  
38 53 class UserProfile(models.Model):
39 54 user = models.OneToOneField(User, unique=True)
... ... @@ -43,6 +58,10 @@ class UserProfile(models.Model):
43 58 facebook = models.CharField(max_length=128, null=True)
44 59 google_talk = models.EmailField(null=True)
45 60 webpage = models.CharField(max_length=256)
  61 + verification_hash = models.CharField(max_length=32, null=True)
  62 +
  63 + def __unicode__(self):
  64 + return '%s (%s)' % (self.user.get_full_name(), self.user.username)
46 65  
47 66 # This does the same the same than related_name argument but it also creates
48 67 # a profile in the case it doesn't exist yet.
... ... @@ -69,17 +88,80 @@ class MailingListMembership(models.Model):
69 88  
70 89  
71 90 class Thread(models.Model):
  91 + class Meta:
  92 + unique_together = ('subject_token', 'mailinglist')
  93 +
72 94 subject_token = models.CharField(max_length=512)
73 95 mailinglist = models.ForeignKey(MailingList)
74 96 latest_message = models.OneToOneField('Message', null=True,
75   - related_name='+')
76   - class Meta:
77   - unique_together = ('subject_token', 'mailinglist')
  97 + related_name='+')
  98 + score = models.IntegerField(default=0)
  99 + spam = models.BooleanField(default=False)
  100 +
  101 + all_objects = models.Manager()
  102 + objects = NotSpamManager()
  103 +
  104 + def __unicode__(self):
  105 + return '%s - %s (%s)' % (self.id,
  106 + self.subject_token,
  107 + self.message_set.count())
  108 +
  109 + def update_score(self):
  110 + """Update the relevance score for this thread.
  111 +
  112 + The score is calculated with the following variables:
  113 +
  114 + * vote_weight: 100 - (minus) 1 for each 3 days since
  115 + voted with minimum of 5.
  116 + * replies_weight: 300 - (minus) 1 for each 3 days since
  117 + replied with minimum of 5.
  118 + * page_view_weight: 10.
  119 +
  120 + * vote_score: sum(vote_weight)
  121 + * replies_score: sum(replies_weight)
  122 + * page_view_score: sum(page_view_weight)
  123 +
  124 + * score = (vote_score + replies_score + page_view_score) // 10
  125 + with minimum of 0 and maximum of 5000
  126 +
  127 + """
  128 +
  129 + if not self.subject_token:
  130 + return
  131 +
  132 + # Save this pseudo now to avoid calling the
  133 + # function N times in the loops below
  134 + now = datetime.datetime.now()
  135 + days_ago = lambda date: (now - date).days
  136 + get_score = lambda weight, created: \
  137 + max(weight - (days_ago(created) // 3), 5)
  138 +
  139 + vote_score = 0
  140 + replies_score = 0
  141 + for msg in self.message_set.all():
  142 + # Calculate replies_score
  143 + replies_score += get_score(300, msg.received_time)
  144 +
  145 + # Calculate vote_score
  146 + for vote in msg.vote_set.all():
  147 + vote_score += get_score(100, vote.created)
  148 +
  149 + # Calculate page_view_score
  150 + try:
  151 + url = reverse('thread_view', args=[self.subject_token])
  152 + pagehit = PageHit.objects.get(url_path=url)
  153 + page_view_score = pagehit.hit_count * 10
  154 + except (NoReverseMatch, PageHit.DoesNotExist):
  155 + page_view_score = 0
  156 +
  157 + self.score = (page_view_score + vote_score + replies_score) // 10
  158 + self.save()
78 159  
79 160  
80 161 class Vote(models.Model):
81 162 user = models.ForeignKey(User)
82 163 message = models.ForeignKey('Message')
  164 + created = models.DateTimeField(auto_now_add=True)
83 165  
84 166 class Meta:
85 167 unique_together = ('user', 'message')
... ... @@ -91,20 +173,26 @@ class Vote(models.Model):
91 173  
92 174 class Message(models.Model):
93 175  
94   - from_address = models.ForeignKey(EmailAddress)
  176 + from_address = models.ForeignKey(EmailAddress, db_index=True)
95 177 mailinglist = models.ForeignKey(MailingList)
96   - thread = models.ForeignKey(Thread, null=True)
  178 + thread = models.ForeignKey(Thread, null=True, db_index=True)
97 179 # RFC 2822 recommends to use 78 chars + CRLF (so 80 chars) for
98 180 # the max_length of a subject but most of implementations
99 181 # goes for 256. We use 512 just in case.
100   - subject = models.CharField(max_length=512)
101   - subject_clean = models.CharField(max_length=512)
  182 + subject = models.CharField(max_length=512, db_index=True)
  183 + subject_clean = models.CharField(max_length=512, db_index=True)
102 184 body = models.TextField(default='')
103 185 received_time = models.DateTimeField()
104 186 message_id = models.CharField(max_length=512)
  187 + spam = models.BooleanField(default=False)
105 188  
  189 + all_objects = models.Manager()
  190 + objects = NotSpamManager()
  191 +
106 192 def __unicode__(self):
107   - return 'Email Message Id: %s' % self.id
  193 + return '(%s) %s: %s' % (self.id,
  194 + self.from_address.get_full_name(),
  195 + self.subject_clean)
108 196  
109 197 def vote_list(self):
110 198 """Return a list of user that voted in this message."""
... ... @@ -125,7 +213,27 @@ class Message(models.Model):
125 213 message=self,
126 214 user=user
127 215 ).delete()
128   -
  216 +
  217 + @property
  218 + def url(self):
  219 + """Shortcut to get thread url"""
  220 + return reverse('thread_view', args=[self.thread.subject_token])
  221 +
  222 + @property
  223 + def Description(self):
  224 + """Alias to self.body"""
  225 + return self.body
  226 +
  227 + @property
  228 + def Title(self):
  229 + """Alias to self.subject_clean"""
  230 + return self.subject_clean
  231 +
  232 + @property
  233 + def modified(self):
  234 + """Alias to self.modified"""
  235 + return self.received_time
  236 +
129 237  
130 238 class MessageMetadata(models.Model):
131 239 Message = models.ForeignKey(Message)
... ... @@ -137,4 +245,4 @@ class MessageMetadata(models.Model):
137 245 def __unicode__(self):
138 246 return 'Email Message Id: %s - %s: %s' % (self.Message.id,
139 247 self.name, self.value)
140   -
  248 +
... ...
colab/super_archives/queries.py
1 1  
2   -from super_archives.models import Thread, Vote, Message
  2 +from colab.super_archives.models import Thread, Vote, Message, PageHit
3 3  
4 4  
5 5 def get_messages_by_date():
... ... @@ -34,30 +34,14 @@ def get_latest_threads():
34 34 return Thread.objects.order_by('-latest_message__received_time')
35 35  
36 36  
37   -def get_voted_threads():
38   - """Query for the most voted threads sorting by the sum of votes
39   - and latest messages received.
40   -
41   - NOTE: This implementation has serious performance issues on
42   - MySQL databases but it performes quite well on PostgreSQL.
43   -
44   - """
45   -
46   - sql = """
47   - SELECT
48   - count(sav.id)
49   - FROM
50   - super_archives_message AS sam
51   - JOIN super_archives_vote AS sav
52   - ON sav.message_id = sam.id
53   - WHERE
54   - super_archives_thread.id = sam.thread_id
55   - """
  37 +def get_hotest_threads():
  38 + return Thread.objects.order_by('-score', '-latest_message__received_time')
56 39  
57   - threads = Thread.objects.extra(
58   - select={
59   - 'vote_count': sql
60   - }
61   - )
62   - return threads.order_by('-vote_count', '-latest_message__received_time')
  40 +
  41 +def get_page_hits(path_info):
  42 + pagehit = PageHit.objects.filter(url_path=path_info)
63 43  
  44 + if pagehit:
  45 + return pagehit[0].hit_count
  46 + return 0
  47 +
... ...
colab/super_archives/templates/message-list.html
1 1 {% extends "base.html" %}
  2 +{% load i18n %}
2 3 {% load append_to_get %}
3 4 {% block main-content %}
4 5 <div id="message-list">
... ... @@ -10,27 +11,30 @@
10 11  
11 12 <h4>Ordenar por</h4>
12 13 <ul>
13   - <li><a href="{% append_to_get order='latest',p=1 %}">Relevância</a></li>
14   - <li><a href="{% append_to_get order='latest',p=1 %}">Atividade recente</a></li>
15   - <li><a href="{% append_to_get order='voted',p=1 %}">Mais votadas</a></li>
  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 %}">
  16 + Relevância</a></li>
  17 + <li {% ifequal order_by "latest" %} class="selected" title="{% trans "Retirar filtro" %}" {% endifequal %}>
  18 + <a href="{% ifequal order_by "latest" %} {% append_to_get order="",p=1 %} {% else %} {% append_to_get order='latest',p=1 %} {% endifequal %}">
  19 + Atividade recente</a></li>
16 20 </ul>
17 21  
18 22 <hr class="space" />
19 23  
20 24 <h4>Listas</h4>
21 25 <ul>
22   - {% for list in lists %}
23   - <li><a href="{% append_to_get list=list.name,p=1 %}">{{ list.name }}</a></li>
24   - {% endfor %}
  26 + {% for list in lists %}
  27 + <li {% ifequal list.name selected_list %} class="selected" title="{% trans "Retirar filtro" %}" {% endifequal %}>
  28 + <a href="{% ifnotequal list.name selected_list %} {% append_to_get list=list.name,p=1 %} {% else %} {% append_to_get list="",p=1 %}
  29 + {% endifnotequal %}">{{ list.name }}</a></li>
  30 + {% endfor %}
25 31 </ul>
26 32 </div>
27 33  
28 34 <div class="span-17 prepend-1 last">
29 35 <ul>
30 36 {% for thread in threads.object_list %}
31   - {% with thread.latest_message as email %}
32   - {% include "message-preview.html" %}
33   - {% endwith %}
  37 + {% include "message-preview.html" with doc=thread.latest_message %}
34 38 {% empty %}
35 39 <br/><br/>
36 40 <span class="span-18 center large">
... ... @@ -61,4 +65,4 @@
61 65 </div>
62 66 </div>
63 67  
64   -{% endblock %}
65 68 \ No newline at end of file
  69 +{% endblock %}
... ...
colab/super_archives/templates/message-preview.html
1 1 {% load i18n %}
2   -{% with email.from_address as from %}
3   -{% if email.subject_clean %}
  2 +
  3 +{% if doc.Title %}
4 4 <li class="preview-message">
5   - <a href="{% url super_archives.views.list_messages %}?list={{ email.thread.mailinglist.name }}">
6   - <div class="left">
7   - <span class="tag">{{ email.mailinglist }}</span>
8   - </div>
  5 + {% if doc.Type %}
  6 + <img alt="{{ doc.Type }}" title="{{ doc.Type }}"
  7 + src="{{ STATIC_URL }}img/{{ doc.Type }}.png" />
  8 + {% endif %}
  9 +
  10 + {% if doc.mailinglist %}
  11 + <a href="{% url super_archives.views.list_messages %}?list={{ doc.mailinglist }}">
  12 + <span class="tag">{{ doc.mailinglist }}</span>
9 13 </a>
10   - <div class="subject">
11   - <a href="{% url super_archives.views.thread email.thread.subject_token %}">
12   - {{ email.subject_clean }}
  14 + {% endif %}
  15 +
  16 + <span class="subject">
  17 + <a href="{{ doc.url }}"
  18 + {% if
  19 + title="{% filter striptags|truncatewords:50 %}
  20 + {{ doc.Description|escape }}
  21 + {% endfilter %}">
  22 + {{ doc.Title }}
13 23 </a>
14   - </div>
  24 + </span>
  25 +
  26 + <span class="quiet">-
  27 + {{ doc.Description|striptags }}
  28 + </span>
  29 +
15 30 <div class="quiet">
16 31 <span class="left">
17   - {% trans "por" %}
18   - <a href="{{ from.get_profile_link }}">
19   - {% firstof from.get_full_name "Anônimo" %}
  32 + {% trans "criado por" %}
  33 + {% if doc.from_address.get_full_name %}
  34 + <a href="{{ doc.from_address.get_profile_link }}">
  35 + {{ doc.from_address.get_full_name }}
20 36 </a>
21   - </span>
  37 + {% else %}
  38 + {% firstof doc.Creator _("anônimo") %}
  39 + {% endif %}
  40 + </span>
  41 +
22 42 <span class="right">
23   - {{ email.received_time|timesince }} {% trans "atrás" %}
  43 + {{ doc.modified|timesince }}
  44 + {% trans "atrás" %}
24 45 </span>
25 46 </div>
26 47 </li>
27 48 {% endif %}
28   -{% endwith %}
29 49 \ No newline at end of file
... ...
colab/super_archives/templates/message-thread.html
... ... @@ -2,9 +2,11 @@
2 2 {% load i18n %}
3 3 {% load append_to_get %}
4 4 {% block main-content %}
  5 + <div id="vote-notification" class="error hide"></div>
  6 +
5 7 <h2>{{ first_msg.subject_clean }}</h2>
6 8 <hr/>
7   - <div class="span-19 border">
  9 + <div class="span-17 border">
8 10 <ul>
9 11 {% for email in emails %}
10 12 <li>
... ... @@ -21,7 +23,7 @@
21 23  
22 24 <div class="plus">
23 25 <span>{{ email.votes_count }}</span>
24   - <img class="right" src="{{ STATIC_URL }}img/thumbs_up.jpg">
  26 + <img title="{% trans 'Votar' %}" class="right" src="{{ STATIC_URL }}img/plus.png">
25 27 </div>
26 28  
27 29 <p class="minus {% if not user in email.vote_list %}hide{% endif %}">
... ... @@ -29,7 +31,7 @@
29 31 </p>
30 32 </div>
31 33  
32   - <div class="span-15">
  34 + <div class="span-13">
33 35 <pre>{{ email.body }}</pre>
34 36 </div>
35 37 </div>
... ... @@ -40,11 +42,36 @@
40 42 {% endfor %}
41 43 </ul>
42 44 </div>
43   - <div class="span-4 filters last">
44   - <h4>Ordenar por</h4>
  45 + <div class="span-6 filters last">
  46 + <h4><b>{% trans "Ordenar por" %}:</b></h4>
  47 + <ul>
  48 + <li><a href="{% append_to_get order='voted' %}">{% trans "Votos" %}</a></li>
  49 + <li><a href="{% append_to_get order='date' %}">{% trans "Data" %}</a></li>
  50 + </ul>
  51 +
  52 + <hr class="space"/>
  53 +
  54 + <h4><b>{% trans "Estatísticas:" %}</b></h4>
  55 +
45 56 <ul>
46   - <li><a href="{% append_to_get order='voted' %}">Votos</a></li>
47   - <li><a href="{% append_to_get order='date' %}">Data</a></li>
  57 + <li class="quiet">{% trans "iniciada à" %}
  58 + <h4>{{ first_msg.received_time|timesince }} {% trans "atrás" %}</h4>
  59 + </li>
  60 + <li class="quiet">{% trans "visualizada" %}
  61 + <h4>{{ pagehits }} {% trans "vezes" %}</h4>
  62 + </li>
  63 + <li class="quiet">{% trans "respondida" %}
  64 + <h4>{{ emails|length }} {% trans "vezes" %}</h4>
  65 + </li>
  66 + <li class="quiet">{% trans "votada" %}
  67 + <h4>{{ total_votes }} {% trans "vezes" %}</h4>
  68 + </li>
48 69 </ul>
  70 +
49 71 </div>
50   -{% endblock %}
51 72 \ No newline at end of file
  73 +
  74 + <script type="text/javascript" charset="utf-8">
  75 + pagehit("{{ request.path_info }}");
  76 + </script>
  77 +
  78 +{% endblock %}
... ...
colab/super_archives/templatetags/append_to_get.py
  1 +
  2 +import urllib
1 3 from django import template
2 4  
3 5 register = template.Library()
... ... @@ -33,15 +35,10 @@ class AppendGetNode(template.Node):
33 35 get[key] = self.dict_pairs[key].resolve(context)
34 36  
35 37 path = context['request'].META['PATH_INFO']
36   -
37   - #print "&".join(["%s=%s" % (key, value)
38   - # for (key, value) in get.items()
39   - # if value])
40   -
  38 +
41 39 if len(get):
42   - path += "?%s" % "&".join(["%s=%s" % (key, value)
43   - for (key, value) in get.items()
44   - if value])
  40 + path = '?' + urllib.urlencode(get)
  41 +
45 42 return path
46 43  
47 44 @register.tag()
... ...
colab/super_archives/templatetags/form_field.py
1 1 from django import template
  2 +from django import forms
2 3  
3 4 def render_form_field(parser, token):
4 5 variables = token.split_contents()
... ... @@ -9,7 +10,7 @@ def render_form_field(parser, token):
9 10 elif len(variables) == 3:
10 11 tag_name, form_field, default_value = variables
11 12 else:
12   - raise TemplateSyntaxError
  13 + raise template.TemplateSyntaxError
13 14  
14 15 return RenderFormField(form_field, default_value)
15 16  
... ... @@ -26,7 +27,10 @@ class RenderFormField(template.Node):
26 27 class_ = ''
27 28 errors = ''
28 29 form_field_tag = ''
29   - form_field = self.form_field_nocontext.resolve(context)
  30 + try:
  31 + form_field = self.form_field_nocontext.resolve(context)
  32 + except template.VariableDoesNotExist:
  33 + return ''
30 34  
31 35 if form_field.errors:
32 36 class_ += 'error'
... ... @@ -42,6 +46,9 @@ class RenderFormField(template.Node):
42 46  
43 47 if editable:
44 48 form_field_tag = '<br/>' + str(form_field)
  49 + elif isinstance(form_field.field, forms.URLField):
  50 + form_field_tag = """<a href="%s" target="_blank">%s</a>""" % (
  51 + default_value, default_value)
45 52 else:
46 53 form_field_tag = default_value
47 54  
... ... @@ -53,4 +60,4 @@ class RenderFormField(template.Node):
53 60 )
54 61  
55 62 register = template.Library()
56   -register.tag('render_form_field', render_form_field)
57 63 \ No newline at end of file
  64 +register.tag('render_form_field', render_form_field)
... ...
colab/super_archives/urls.py
... ... @@ -2,6 +2,8 @@ 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]+)$', 'super_archives.views.thread'),
6   - url(r'thread/$', 'super_archives.views.list_messages')
  5 + url(r'thread/(?P<thread_token>[-\w]+)$',
  6 + 'colab.super_archives.views.thread', name="thread_view"),
  7 + url(r'thread/$',
  8 + 'colab.super_archives.views.list_messages', name='thread_list')
7 9 )
... ...
colab/super_archives/validators.py
1   -# -*- coding: utf8 -*-
  1 +# -*- coding: utf-8 -*-
2 2  
3 3 from django.core.exceptions import ValidationError
4 4  
... ...
colab/super_archives/views.py
1   -# -*- coding: utf8 -*-
  1 +# -*- coding: utf-8 -*-
2 2  
3 3 from django.template import RequestContext
4 4 from django.core.paginator import Paginator
5 5 from django.shortcuts import render_to_response, get_list_or_404
6 6  
7   -from super_archives import queries
8   -from super_archives.models import MailingList
  7 +from colab.super_archives import queries
  8 +from colab.super_archives.models import MailingList, Thread
9 9  
10 10  
11 11 def thread(request, thread_token):
12 12  
13 13 first_message = queries.get_first_message_in_thread(thread_token)
14 14 order_by = request.GET.get('order')
15   - if order_by == 'date':
16   - msgs_query = queries.get_messages_by_date()
17   - else:
  15 + if order_by == 'voted':
18 16 msgs_query = queries.get_messages_by_voted()
  17 + else:
  18 + msgs_query = queries.get_messages_by_date()
19 19  
20 20 msgs_query = msgs_query.filter(thread__subject_token=thread_token)
21 21 emails = msgs_query.exclude(id=first_message.id)
  22 +
  23 + total_votes = first_message.votes_count()
  24 + for email in emails:
  25 + total_votes += email.votes_count()
  26 +
  27 + # Update relevance score
  28 + thread = Thread.objects.get(subject_token=thread_token)
  29 + thread.update_score()
  30 +
22 31 template_data = {
23   - 'request': request,
24 32 'first_msg': first_message,
25 33 'emails': [first_message] + list(emails),
  34 + 'pagehits': queries.get_page_hits(request.path_info),
  35 + 'total_votes': total_votes,
26 36 }
  37 +
27 38 return render_to_response('message-thread.html', template_data,
28 39 RequestContext(request))
29 40  
30 41  
31 42 def list_messages(request):
32 43  
  44 + selected_list = request.GET.get('list')
  45 +
33 46 order_by = request.GET.get('order')
34   - if order_by == 'voted':
35   - threads = queries.get_voted_threads()
  47 + if order_by == 'hotest':
  48 + threads = queries.get_hotest_threads()
36 49 else:
37 50 threads = queries.get_latest_threads()
38 51  
... ... @@ -50,7 +63,8 @@ def list_messages(request):
50 63 'lists': lists,
51 64 'n_results': paginator.count,
52 65 'threads': threads,
53   - 'request': request,
  66 + 'selected_list': selected_list,
  67 + 'order_by': order_by,
54 68 }
55 69 return render_to_response('message-list.html', template_data,
56   - RequestContext(request))
57 70 \ No newline at end of file
  71 + RequestContext(request))
... ...
colab/templates/404.html 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<h1>Not found. Keep searching! :)</h1>
0 2 \ No newline at end of file
... ...
colab/templates/500.html 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<h1>Ooopz... something went wrong!</h1>
0 2 \ No newline at end of file
... ...
colab/templates/account_change_password.html 0 → 100644
... ... @@ -0,0 +1,24 @@
  1 +{% extends "base.html" %}
  2 +{% load i18n %}
  3 +{% load form_field %}
  4 +
  5 +{% block main-content %}
  6 +
  7 +{% if form.errors %}
  8 + <div class="alert">
  9 + <b>Por favor, corrija os erros abaixo e tente novamente.</b>
  10 + </div>
  11 +{% endif %}
  12 +
  13 +<form method="POST" action=".">
  14 + {% csrf_token %}
  15 +
  16 + <fieldset class="span-24 box">
  17 + {% render_form_field form.old_password %}
  18 + {% render_form_field form.new_password1 %}
  19 + {% render_form_field form.new_password2 %}
  20 + <input type="submit" value="{% trans 'Alterar senha' %}"/>
  21 + </fieldset>
  22 +
  23 +</form>
  24 +{% endblock %}
0 25 \ No newline at end of file
... ...
colab/templates/account_message.html 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +{% extends "base.html" %}
  2 +{% load i18n %}
  3 +
  4 +{% block main-content %}
  5 +
  6 +<span class="span-24 {{ msg_css_class }}">
  7 + {% trans msg %}
  8 +</span>
  9 +
  10 +{% endblock %}
0 11 \ No newline at end of file
... ...
colab/templates/account_request_reset_password.html 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +{% extends "base.html" %}
  2 +{% load i18n %}
  3 +
  4 +{% block main-content %}
  5 +<h2>{% trans "Esqueci minha senha" %}</h2>
  6 +
  7 +<form method="POST" action="{% url request_reset_password %}">
  8 + {% csrf_token %}
  9 +
  10 + <fieldset class="span-24 center box">
  11 + <label>{% trans "Usuário" %}:</label>
  12 + <input name="username"/>
  13 + <input type="submit" value="{% trans 'Enviar nova senha' %}"/>
  14 + </fieldset>
  15 +
  16 +</form>
  17 +
  18 +{% endblock %}
0 19 \ No newline at end of file
... ...
colab/templates/base.html
  1 +{% load i18n %}
1 2 <html>
2 3 <head>
3   - <!-- Framework CSS -->
4   - <link rel="stylesheet" href="{{ STATIC_URL }}css/blueprint/screen.css"
5   - type="text/css" media="screen, projection" />
6   - <link rel="stylesheet" href="{{ STATIC_URL }}css/blueprint/print.css"
7   - type="text/css" media="print" />
8   - <!--[if IE]><link rel="stylesheet"
9   - href="{{ STATIC_URL }}css/blueprint/ie.css"
10   - type="text/css" media="screen, projection" /><![endif]-->
11   -
12   - <link rel="stylesheet" href="{{ STATIC_URL }}css/screen.css"
  4 + <!-- Framework CSS -->
  5 + <link rel="stylesheet" href="{{ STATIC_URL }}css/blueprint/screen.css"
  6 + type="text/css" media="screen, projection" />
  7 + <link rel="stylesheet" href="{{ STATIC_URL }}css/blueprint/print.css"
  8 + type="text/css" media="print" />
  9 + <!--[if IE]><link rel="stylesheet"
  10 + href="{{ STATIC_URL }}css/blueprint/ie.css"
  11 + type="text/css" media="screen, projection" /><![endif]-->
  12 +
  13 + <link rel="stylesheet" href="{{ STATIC_URL }}css/screen.css"
13 14 type="text/css" media="screen" charset="utf-8"/>
14 15  
15 16 <script type="text/javascript" src="{{ STATIC_URL }}js/jquery-1.7.min.js"></script>
16 17 <script type="text/javascript" src="{{ STATIC_URL }}js/base.js"></script>
17   -
  18 +
18 19 {% block head_js %}
19 20 {% endblock %}
  21 + {% block head_css %}
  22 + <!-- css used to show the selected item on filters -->
  23 + <style type="text/css">
  24 + .selected {
  25 + font-weight: bold;
  26 + }
  27 +
  28 + .selected a {
  29 + text-decoration: none;
  30 + color: #000;
  31 + background-image: url('{{ STATIC_URL }}img/x.png');
  32 + background-repeat: no-repeat;
  33 + background-position: 0 3px;
  34 + padding-left: 16px;
  35 + }
  36 +
  37 + .selected a:hover {
  38 + text-decoration: underline;
  39 + }
  40 +
  41 + li.selected:before {
  42 + content: '';
  43 + }
  44 + </style>
  45 +
  46 + {% endblock %}
20 47 </head>
21 48  
22 49 <body class="container">
... ... @@ -24,55 +51,66 @@
24 51 <div id="top-menu" class="right">
25 52 {% if not user.is_authenticated %}
26 53 <span class="colborder">
27   - <a href="{% url colab.views.signup %}">Cadastre-se</a>
  54 + <a href="{% url signup %}">{% trans "Cadastre-se" %}</a>
28 55 </span>
29 56 <span>
30   - <a href="{% url django.contrib.auth.views.login %}">Acessar</a>
  57 + <a href="{% url login %}?next={{ request.path }}">{% trans "Acessar" %}</a>
31 58 </span>
32 59 {% else %}
33 60 <span class="colborder">
34   - <a href="{% url colab.views.user_profile_username user.username %}">Meu Perfil</a>
  61 + {% trans "autenticado como" %} <b>{{ user.username }}</b>
  62 + </span>
  63 + <span class="colborder">
  64 + <a href="{% url user_profile user.username %}">
  65 + {% trans "Meu Perfil" %}
  66 + </a>
35 67 </span>
36 68 <span>
37   - <a href="{% url django.contrib.auth.views.logout %}">Sair</a>
  69 + <a href="{% url logout %}">
  70 + {% trans "Sair" %}
  71 + </a>
38 72 </span>
39 73 {% endif %}
40 74 </div>
41   -
  75 +
42 76 {% block header %}
43 77 <h1><span class="hide">COLAB</span>
44 78 <a href="/">
45 79 <img src="{{ STATIC_URL }}img/logo_small.png" alt="Colab" /></a>
46 80 </h1>
47 81 {% endblock %}
48   -
49   - <div id="header-menu" class="span-24">
  82 +
  83 + <div id="header-menu" class="span-24">
50 84  
51 85 <span class="colborder">
52   - <a href="#">Iniciar Discussão</a>
53   - </span>
54   - <span class="colborder">
55   - <a href="{% url super_archives.views.list_messages %}">Discussões</a>
  86 + <a href="{% url thread_list %}">Discussões</a>
56 87 </span>
57 88 <span class="colborder">
58   - <a href="http://colab.interlegis.gov.br/wiki">Wiki</a>
  89 + <a href="http://colab.interlegis.gov.br/wiki" target="_blank">Wiki</a>
59 90 </span>
60 91 <span class="colborder">
61   - <a href="#">Contribua</a>
  92 + <a href="http://listas.interlegis.gov.br/mailman/listinfo/"
  93 + target="_blank">Contribua</a>
62 94 </span>
63 95 <span class="colborder">
64   - <a href="#">Reporte um problema</a>
  96 + <a href="http://colab.interlegis.leg.br/newticket"
  97 + target="_blank">Reporte um problema</a>
65 98 </span>
66   - <span class="colborder">
67   - <a href="#">Treco</a>
  99 + <span>
  100 + <a href="#">Mensageiro</a>
68 101 </span>
69 102  
70   - <input id="header-searchbox" type="text" placeholder="Pesquise aqui..."/>
71   - <input type="button" value="Buscar"></input>
  103 + <span class="right">
  104 + <form action="/search/" method="GET">
  105 + <input name="q" id="header-searchbox" value="{{ request.GET.q }}"
  106 + type="text" placeholder="Pesquise aqui..." />
  107 + <input type="submit" value="Buscar"></input>
  108 + </form>
  109 + </span>
72 110 </div>
73 111 </div>
74   -
75   - <hr/>
  112 +
  113 + <hr/>
76 114  
77 115 <div id="main-content" class="span-24">
78 116 {% block main-content %} {% endblock %}
... ... @@ -82,11 +120,11 @@
82 120  
83 121 <div id="footer" class="span-24 center">
84 122 {% block footer %}
85   - <p>O conteúdo deste site está publicado sob a licença <a
86   - href="http://creativecommons.org/licenses/by-nc-sa/2.0/br/">Creative
  123 + <p>O conteúdo deste site está publicado sob a licença <a
  124 + href="http://creativecommons.org/licenses/by-nc-sa/2.0/br/">Creative
87 125 Commons - atribuição e não-comercial</a>
88 126 </p>
89 127 {% endblock %}
90 128 </div>
91 129 </body>
92   -</html>
93 130 \ No newline at end of file
  131 +</html>
... ...
colab/templates/email_account-reset-password.html 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +{% load i18n %}
  2 +
  3 +{% blocktrans %}
  4 + Este email foi enviado para confirmar a solicitação de troca de senha
  5 + para o usuário <b>{{ username }}</b> do Colab Interlegis. Caso você não
  6 + tenha realizado a solicitação por favor ignore esta mensagem.
  7 +{% endblocktrans %}
  8 +
  9 +<br/>
  10 +<br/>
  11 +
  12 +{% blocktrans %}
  13 + Para realizar a troca de senha acesse o link abaixo:
  14 +{% endblocktrans %}
  15 +<br/>
  16 +
  17 +<a href="http://{{ server_name }}{% url reset_password hash %}">
  18 + http://{{ server_name }}{% url reset_password hash %}
  19 +</a>
0 20 \ No newline at end of file
... ...
colab/templates/email_signup-email-confirmation.html 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +{% load i18n %}
  2 +
  3 +Bem-vindo ao Colab!
  4 +
  5 +Para ativar sua conta por favor valide seu email acessando o link seguinte:
  6 +
  7 +<a href="http://{{ server_name }}{% url email_verification hash %}">
  8 + http://{{ server_name }}{% url email_verification hash %}
  9 +</a>
0 10 \ No newline at end of file
... ...
colab/templates/home.html
1 1 {% extends "base.html" %}
2 2 {% load i18n %}
3 3  
4   -{% block header %}
  4 +{% block head_js %}
  5 + {% include "pizza-chart.html" with chart_div="collabs" chart_width=410 chart_height=330 %}
  6 +{% endblock %}
  7 +
  8 +{% block header %}
5 9 <div id="header-logo" class="span-24">
6 10 <img class="center" src="{{ STATIC_URL }}img/logo.png" />
7 11 </div>
... ... @@ -12,34 +16,52 @@
12 16 {% endblock %}
13 17  
14 18 {% block main-content %}
15   - <h3 class="span-13">{% trans "Últimas Menssagens:" %}</h3>
16   - <h3 class="span-11 last">{% trans "Discussões mais votadas:" %}</h3>
17   -
18   - <ul id="latest_emails" class="span-12 colborder">
19   - {% for thread in latest_threads %}
20   - {% with thread.latest_message as email %}
21   - {% include "message-preview.html" %}
22   - {% endwith %}
23   - {% endfor %}
24   - </ul>
25   -
26   - <ul id="hotest_emails" class="span-11 last">
27   - {% for thread in hotest_threads %}
28   - {% with thread.latest_message as email %}
29   - {% include "message-preview.html" %}
30   - {% endwith %}
31   - {% endfor %}
32   - </ul>
33   -
  19 +
  20 + <div class="span-12 colborder">
  21 + <h3>{% trans "Últimas Colaborações:" %}</h3>
  22 + <ul>
  23 + {% for doc in latest_docs %}
  24 + {% include "message-preview.html" %}
  25 + {% endfor %}
  26 + </ul>
  27 + <hr class="space"/>
  28 + <a class="right" href="{% url search %}?o=modified+desc">
  29 + {% trans "Ver mais colaborações..." %}
  30 + </a>
  31 + </div>
  32 +
  33 + <div class="span-11 last">
  34 + <h3>{% trans "Distribuição das Colaborações:" %}</h3>
  35 + <div id="collabs"></div>
  36 + </div>
  37 +
34 38 <hr class="space"/>
35   - <div class="span-13">
36   - <a class="right append-1" href="{% url super_archives.views.list_messages %}">
37   - {% trans "Ver mais mensagens..." %}
  39 + <hr/>
  40 +
  41 + <div class="span-12 colborder">
  42 + <h3>{% trans "Discussões Mais Relevantes:" %}</h3>
  43 + <ul>
  44 + {% for thread in hotest_threads %}
  45 + {% include "message-preview.html" with doc=thread.latest_message %}
  46 + {% endfor %}
  47 + </ul>
  48 + <hr class="space"/>
  49 + <a class="right" href="{% url thread_list %}?order=hotest">
  50 + {% trans "Ver mais discussões relevantes..." %}
38 51 </a>
39 52 </div>
  53 +
40 54 <div class="span-11 last">
41   - <a class="right" href="{% url super_archives.views.list_messages %}?order=hotest">
42   - {% trans "Ver mais mensagens..." %}
  55 + <h3>{% trans "Últimas Discussões:" %}</h3>
  56 + <ul>
  57 + {% for thread in latest_threads %}
  58 + {% include "message-preview.html" with doc=thread.latest_message %}
  59 + {% endfor %}
  60 + </ul>
  61 + <hr class="space"/>
  62 + <a class="right" href="{% url thread_list %}">
  63 + {% trans "Ver mais discussões..." %}
43 64 </a>
44 65 </div>
  66 +
45 67 {% endblock %}
... ...
colab/templates/login.html
... ... @@ -10,8 +10,8 @@
10 10 {% endif %}
11 11  
12 12 <div id="login-form-wrapper">
13   - <form class="span-24" method="post" action="{% url django.contrib.auth.views.login %}">
14   - {% csrf_token %}
  13 + <form class="span-24" method="POST" action="{% url login %}">
  14 + {% csrf_token %}
15 15  
16 16 <fieldset class="span-10 box">
17 17 <legend>Login</legend>
... ... @@ -27,8 +27,9 @@
27 27 </p>
28 28  
29 29 <input type="submit" value="Login" />
30   - {% url colab.views.user_profile_empty as user_profile_url %}
31   - <input type="hidden" name="next" value="{% firstof next_page user_profile_url %}" />
  30 + <a class="right" href="{% url request_reset_password %}">Esqueci minha senha</a>
  31 +
  32 + <input type="hidden" name="next" value="{% firstof next '/' %}" />
32 33 </fieldset>
33 34  
34 35 <fieldset class="span-12 box last">
... ... @@ -39,8 +40,8 @@
39 40 comunidade Interlegis clique no link abaixo e comece a colaborar!</p>
40 41  
41 42 <a class="span-11 center large"
42   - href="{% url colab.views.signup %}">Cadastre-se</a>
  43 + href="{% url signup %}">Cadastre-se</a>
43 44 </fieldset>
44 45 </form>
45 46 </div>
46   -{% endblock %}
47 47 \ No newline at end of file
  48 +{% endblock %}
... ...
colab/templates/pizza-chart.html 0 → 100644
... ... @@ -0,0 +1,45 @@
  1 +{% load i18n %}
  2 +
  3 +{% if type_count %}
  4 + <!--Load the AJAX API-->
  5 + <script type="text/javascript" src="https://www.google.com/jsapi"></script>
  6 + <script type="text/javascript">
  7 +
  8 + // Load the Visualization API and the piechart package.
  9 + google.load('visualization', '1.0', {'packages':['corechart']});
  10 +
  11 + // Set a callback to run when the Google Visualization API is loaded.
  12 + google.setOnLoadCallback(drawChart);
  13 +
  14 + // Callback that creates and populates a data table,
  15 + // instantiates the pie chart, passes in the data and
  16 + // draws it.
  17 + function drawChart() {
  18 +
  19 + // Create the data table.
  20 + var data = new google.visualization.DataTable();
  21 + data.addColumn('string', 'Topping');
  22 + data.addColumn('number', 'Slices');
  23 + data.addRows([
  24 + ['{% trans "Emails"%}', {% firstof type_count.thread '0' %}],
  25 + ['{% trans "Tiquetes"%}', {% firstof type_count.ticket '0' %}],
  26 + ['{% trans "Wiki"%}', {% firstof type_count.wiki '0' %}],
  27 + ['{% trans "Código"%}', {% firstof type_count.changeset '0' %}],
  28 + ]);
  29 +
  30 + // Set chart options
  31 + var options = {'width': {{ chart_width }},
  32 + 'height': {{ chart_height }},
  33 + 'legend': 'bottom',
  34 + chartArea: {
  35 + top:10,
  36 + height:"85%",
  37 + },
  38 + };
  39 +
  40 + // Instantiate and draw our chart, passing in some options.
  41 + var chart = new google.visualization.PieChart(document.getElementById('{{ chart_div }}'));
  42 + chart.draw(data, options);
  43 + }
  44 + </script>
  45 +{% endif %}
... ...
colab/templates/search.html 0 → 100644
... ... @@ -0,0 +1,78 @@
  1 +{% extends "base.html" %}
  2 +{% load i18n %}
  3 +{% load append_to_get %}
  4 +{% block main-content %}
  5 + <h2 class="span-6">{% trans "Busca" %}</h2>
  6 + <span class="right quiet">
  7 + {{ docs.numFound }} {% trans "documentos encontrados em" %}
  8 + {{ docs.QTime|floatformat:3 }} {% trans "segundos" %}
  9 + </span>
  10 +
  11 + <hr/>
  12 +
  13 + <div class="span-5 border filters">
  14 + <h3>Filtros</h3>
  15 +
  16 + <h4>{% trans "Tipos" %}</h4>
  17 +
  18 + <ul class="legend">
  19 + <li {% ifequal type "wiki" %} class="selected" title="{% trans "Retirar Filtro" %}" {% endifequal %}>
  20 + <span>
  21 + <img src="{{ STATIC_URL }}img/wiki.png">
  22 + </span>
  23 + <a href="{% ifnotequal type "wiki" %} {% append_to_get type='wiki' %} {% else %} {% append_to_get type="" %} {% endifnotequal %}">{% trans "Wiki" %}</a>
  24 + </li>
  25 + <li {% ifequal type "thread" %} class="selected" title="{% trans "Retirar filtro" %}" {% endifequal %}>
  26 + <span>
  27 + <img src="{{ STATIC_URL }}img/thread.png">
  28 + </span>
  29 + <a href="{% ifnotequal type "thread" %} {% append_to_get type='thread' %} {% else %} {% append_to_get type="" %} {% endifnotequal %}">{% trans "Discussão" %}</a>
  30 + </li>
  31 + <li {% ifequal type "ticket" %} class="selected" title="{% trans "Retirar filtro" %}" {% endifequal %}>
  32 + <span>
  33 + <img src="{{ STATIC_URL }}img/ticket.png">
  34 + </span>
  35 + <a href="{% ifnotequal type "ticket" %} {% append_to_get type='ticket' %} {% else %} {% append_to_get type="" %} {% endifnotequal %}">{% trans "Tiquete" %}</a>
  36 + </li>
  37 + <li {% ifequal type "changeset" %} class="selected" title="{% trans "Retirar filtro" %}" {% endifequal %}>
  38 + <span>
  39 + <img src="{{ STATIC_URL }}img/changeset.png">
  40 + </span>
  41 + <a href="{% ifnotequal type "changeset" %} {% append_to_get type='changeset' %} {% else %} {% append_to_get type="" %} {% endifnotequal %}">{% trans "Changeset" %}</a>
  42 + </li>
  43 + </ul>
  44 + </div>
  45 +
  46 + <div class="span-17 prepend-1 last">
  47 + <ul>
  48 + {% for doc in docs %}
  49 + {% include "message-preview.html" %}
  50 + {% empty %}
  51 + <li class="center">
  52 + {% trans "Sem resultados para a busca." %}
  53 + </li>
  54 + {% endfor %}
  55 + </ul>
  56 +
  57 + <hr class="space"/>
  58 +
  59 + {% if docs.numFound %}
  60 + <div class="pagination center">
  61 + <span>
  62 + {% if docs.has_previous %}
  63 + <a href="{% append_to_get p=docs.previous_page_number %}">{% trans "Anterior" %}</a>
  64 + {% endif %}
  65 +
  66 + <span>
  67 + {% trans "Página" %} {{ docs.page_num }} {% trans "de" %} {{ docs.num_of_pages }}
  68 + </span>
  69 +
  70 + {% if docs.has_next %}
  71 + <a href="{% append_to_get p=docs.next_page_number %}">{% trans "Próxima" %}</a>
  72 + {% endif %}
  73 + </span>
  74 + </div>
  75 + {% endif %}
  76 + </div>
  77 +
  78 +{% endblock %}
... ...
colab/templates/signup-form.html
1 1 {% extends "base.html" %}
2 2 {% load form_field %}
  3 +{% load i18n %}
3 4 {% block main-content %}
4 5  
5 6 <h2>Cadastre-se</h2>
... ... @@ -8,6 +9,17 @@
8 9 <div class="alert"><b>Por favor, corrija os erros abaixo e tente novamente.</b></div>
9 10 {% endif %}
10 11  
  12 +<div class="avatar-placeholder box">
  13 + <label class="avatar-placeholder">Avatar</label >
  14 + <div class="avatar-image avatar">
  15 + <img src="{{ STATIC_URL }}img/user.png" alt="user"/>
  16 + </div>
  17 + <p>
  18 + {% trans "Adicione um avatar à sua conta utilizando" %} <a href="http://pt.gravatar.com/" target="_blank">Gravatar</a>.
  19 + </p>
  20 +</div>
  21 +
  22 +
11 23 <p class="required span-24 last">
12 24 <label>Campos Obrigatórios</label>
13 25 </p>
... ... @@ -47,4 +59,4 @@
47 59 <input class="right" type="submit" value="Cadastrar"/>
48 60 </div>
49 61 </form>
50   -{% endblock %}
51 62 \ No newline at end of file
  63 +{% endblock %}
... ...
colab/templates/user-profile.html
1 1 {% extends "base.html" %}
  2 +{% load i18n %}
2 3 {% load form_field %}
3 4  
4 5 {% block head_js %}
5   - <!--Load the AJAX API-->
6   - <script type="text/javascript" src="https://www.google.com/jsapi"></script>
7   - <script type="text/javascript">
8   -
9   - // Load the Visualization API and the piechart package.
10   - google.load('visualization', '1.0', {'packages':['corechart']});
11   -
12   - // Set a callback to run when the Google Visualization API is loaded.
13   - google.setOnLoadCallback(drawChart);
14   -
15   - // Callback that creates and populates a data table,
16   - // instantiates the pie chart, passes in the data and
17   - // draws it.
18   - function drawChart() {
19   -
20   - // Create the data table.
21   - var data = new google.visualization.DataTable();
22   - data.addColumn('string', 'Topping');
23   - data.addColumn('number', 'Slices');
24   - data.addRows([
25   - ['Escolhida pelo autor', 21],
26   - ['Bem avaliadas', 50],
27   - ['Outras', 230],
28   - ]);
29   -
30   - // Set chart options
31   - var options = {'width': 350,
32   - 'height': 200,
33   - 'legend': 'bottom',
34   - chartArea: {
35   - top:10,
36   - width:"90%",
37   - height:"75%"},
38   - };
39   -
40   - // Instantiate and draw our chart, passing in some options.
41   - var chart = new google.visualization.PieChart(document.getElementById('chart_div'));
42   - chart.draw(data, options);
43   - }
44   - </script>
  6 + {% include "pizza-chart.html" with chart_div="collabs" chart_width=390 chart_height=230 %}
45 7 {% endblock %}
46 8  
47   -
48 9 {% block main-content %}
49 10 {% if not user_profile %}
50 11 <span class="notice span-24">
51 12 <b>Usuário não cadastrado.</b> Você é dono deste perfil?
52   - <a href="{% url colab.views.signup %}">Clique aqui
  13 + <a href="{% url signup %}">Clique aqui
53 14 e cadastre-se.</a>
54 15 </span>
  16 +
  17 + {% else %}
  18 +
  19 + {% ifequal request.user.username user_profile.user.username %}
  20 + <span class="success span-24">
  21 + Ei, olha você aqui! Quer
  22 + <a href="{% url user_profile_update request.user %}">editar seu perfil</a>?
  23 + </span>
  24 + {% endifequal %}
  25 +
55 26 {% endif %}
56 27  
57 28 <div id="user-profile">
... ... @@ -63,13 +34,18 @@
63 34 </div>
64 35  
65 36 <div class="span-20 last">
66   - <div class="span-11">
67   - <form action='.' method='post'>
  37 + <div class="span-10">
  38 + <form action="{% url user_profile_update request.user %}" method='post'>
68 39 {% csrf_token %}
69 40  
70 41 <h3>Informações Pessoais</h3>
71 42 <ul id="user-info">
72   - <li>{{ form.username.label_tag }}: {{ user_profile.user.username }}</li>
  43 + <li>
  44 + {{ form.username.label_tag }}: {{ user_profile.user.username }}
  45 + {% ifequal request.user.username user_profile.user.username %}
  46 + (<a href="{% url change_password %}">{% trans "alterar senha" %}</a>)
  47 + {% endifequal %}
  48 + </li>
73 49 <li>
74 50 {% render_form_field form.institution user_profile.institution %}
75 51 </li>
... ... @@ -105,21 +81,37 @@
105 81 </form>
106 82 </div>
107 83  
108   - <div class="span-9 last">
109   - <h3 class="center">Avaliação das Menssagens</h3>
110   - <div id="chart_div"></div>
  84 + {% if type_count %}
  85 + <div class="span-10 last">
  86 + <h3 class="center">{% trans "Colaborações por área" %}</h3>
  87 + <div id="collabs"></div>
111 88 </div>
  89 + {% endif %}
112 90 </div>
113 91  
114 92 <hr class="space" />
115 93  
116   - <h3>Últimas mensagens enviadas</h3>
117   - <ul>
118   - {% for email in emails %}
119   - {% include "message-preview.html" %}
120   - {% empty %}
121   - <li>Não existem mensagens enviadas por este usuário até o momento.</li>
122   - {% endfor %}
123   - </ul>
  94 + <div class="span-13">
  95 + <h3>{% trans "Últimas mensagens enviadas" %} </h3>
  96 + <ul class="colborder">
  97 + {% for doc in emails %}
  98 + {% include "message-preview.html" %}
  99 + {% empty %}
  100 + <li>Não existem mensagens enviadas por este usuário até o momento.</li>
  101 + {% endfor %}
  102 + </ul>
  103 + </div>
  104 +
  105 + <div class="span-11 last">
  106 + <h3>{% trans "Participações na comunidade:" %}</h3>
  107 + <ul>
  108 + {% for doc in docs %}
  109 + {% include "message-preview.html" %}
  110 + {% empty %}
  111 + <li>Sem colaborações deste usuário até o momento.</li>
  112 + {% endfor %}
  113 + </ul>
  114 + </div>
  115 +
124 116 </div>
125   -{% endblock %}
126 117 \ No newline at end of file
  118 +{% endblock %}
... ...
colab/urls.py
... ... @@ -5,31 +5,48 @@ from django.contrib import admin
5 5 admin.autodiscover()
6 6  
7 7 urlpatterns = patterns('',
8   - url(r'^$', 'colab.views.home', name='home'),
  8 + url(r'^$', 'colab.views.other.home', name='home'),
9 9  
10 10 url(r'^archives/', include('colab.super_archives.urls')),
11 11  
12 12 url(r'^api/', include('colab.api.urls')),
13 13  
  14 + url(r'^user/(?P<username>[\w@+.-]+)/?$',
  15 + 'colab.views.userprofile.by_username', name='user_profile'),
  16 +
  17 + url(r'^user/$', 'colab.views.userprofile.by_request_user',
  18 + name='user_profile_by_request_user'),
  19 +
14 20 url(r'^user/hash/(?P<emailhash>[\w]+)$',
15   - 'colab.views.user_profile_emailhash'),
16   -
17   - url(r'^user/(?P<username>[\w]+)/?$', 'colab.views.user_profile_username'),
  21 + 'colab.views.userprofile.by_emailhash'),
18 22  
19 23 url(r'^user/(?P<username>[\w]+)/edit/?$',
20   - 'colab.views.user_profile_edit'),
  24 + 'colab.views.userprofile.update', name='user_profile_update'),
  25 +
  26 + url(r'^search/$', 'colab.views.other.search', name='search'),
21 27  
22   - url(r'^user/$',
23   - 'colab.views.user_profile_empty'),
  28 + url(r'^account/$', 'colab.views.signup.signup', name='signup'),
24 29  
25   - url(r'^signup/$', 'colab.views.signup'),
  30 + url(r'^account/changepassword/$', 'colab.views.signup.change_password',
  31 + name='change_password'),
26 32  
27   - url(r'^login/$', 'django.contrib.auth.views.login',
28   - {'template_name': 'login.html'}),
  33 + url(r'^account/resetpassword/$',
  34 + 'colab.views.signup.request_reset_password',
  35 + name='request_reset_password'),
  36 +
  37 + url(r'^account/reset_password/(?P<hash>[\w]{32})/$',
  38 + 'colab.views.signup.reset_password', name='reset_password'),
29 39  
30   - url(r'^logout/$', 'django.contrib.auth.views.logout',
31   - {'next_page': '/'}),
  40 + url(r'^signup/verify/(?P<hash>[\w]{32})/$',
  41 + 'colab.views.signup.verify_email', name='email_verification'),
  42 +
  43 + url(r'^account/login/$', 'django.contrib.auth.views.login',
  44 + {'template_name': 'login.html'}, name='login'),
  45 +
  46 + url(r'^account/logout/$', 'django.contrib.auth.views.logout',
  47 + {'next_page': '/'}, name='logout'),
32 48  
33 49 # Uncomment the next line to enable the admin:
34   - #url(r'^admin/', include(admin.site.urls)),
  50 + url(r'^colab/admin/', include(admin.site.urls)),
35 51 )
  52 +
... ...
colab/views.py
... ... @@ -1,159 +0,0 @@
1   -
2   -from django.template import RequestContext
3   -from django.contrib.auth.models import User
4   -from django.forms.models import model_to_dict
5   -from django.contrib.auth.decorators import login_required
6   -from django.shortcuts import render_to_response, get_object_or_404, redirect
7   -
8   -from super_archives import queries
9   -from super_archives.forms import UserCreationForm, UserUpdateForm
10   -from super_archives.models import Message, Thread, UserProfile, EmailAddress
11   -
12   -
13   -def home(request):
14   - """Index page view"""
15   -
16   - latest_threads = queries.get_latest_threads()
17   - hotest_threads = queries.get_voted_threads()
18   -
19   - template_data = {
20   - 'hotest_threads': hotest_threads[:6],
21   - 'latest_threads': latest_threads[:6],
22   - }
23   - return render_to_response('home.html', template_data,
24   - context_instance=RequestContext(request))
25   -
26   -
27   -def signup(request):
28   -
29   - # If the request method is GET just return the form
30   - if request.method == 'GET':
31   - form = UserCreationForm()
32   - return render_to_response('signup-form.html', {'form': form},
33   - RequestContext(request))
34   -
35   - # If the request method is POST try to store data
36   - form = UserCreationForm(request.POST)
37   -
38   - # If there is validation errors give the form back to the user
39   - if not form.is_valid():
40   - return render_to_response('signup-form.html', {'form': form},
41   - RequestContext(request))
42   -
43   - user = User(
44   - username=form.cleaned_data.get('username'),
45   - email=form.cleaned_data.get('email'),
46   - first_name=form.cleaned_data.get('first_name'),
47   - last_name=form.cleaned_data.get('last_name'),
48   - )
49   - user.set_password(form.cleaned_data.get('password'))
50   - user.save()
51   -
52   - profile = UserProfile(
53   - user=user,
54   - institution=form.cleaned_data.get('institution'),
55   - role=form.cleaned_data.get('role'),
56   - twitter=form.cleaned_data.get('twitter'),
57   - facebook=form.cleaned_data.get('facebook'),
58   - google_talk=form.cleaned_data.get('google_talk'),
59   - webpage=form.cleaned_data.get('webpage'),
60   - )
61   - profile.save()
62   -
63   - # Check if the user's email have been used previously
64   - # in the mainling lists to link the user to old messages
65   - email_addr, created = EmailAddress.objects.get_or_create(address=user.email)
66   - if created:
67   - email_addr.real_name = user.get_full_name()
68   -
69   - email_addr.user = user
70   - email_addr.save()
71   -
72   - return redirect('colab.views.user_profile_username', user.username)
73   -
74   -
75   -def user_profile(request, user, email_address=None, editable=False, form=None):
76   -
77   - if form is None:
78   - form = UserCreationForm()
79   -
80   - if user:
81   - email_addresses = user.emails.all()
82   - else:
83   - email_addresses = [email_address]
84   -
85   - if not email_address:
86   - email_address = email_addresses[0]
87   -
88   - email_addresses_ids = tuple([str(addr.id) for addr in email_addresses])
89   -
90   - query = """
91   - SELECT
92   - *
93   - FROM
94   - super_archives_message
95   - WHERE
96   - from_address_id IN (%(ids)s)
97   - GROUP BY
98   - thread_id
99   - ORDER BY
100   - received_time DESC
101   - LIMIT 10;
102   - """ % {'ids': ','.join(email_addresses_ids)}
103   -
104   - emails = Message.objects.raw(query)
105   - n_sent = Message.objects.filter(from_address__in=email_addresses).count()
106   -
107   - if user:
108   - profile = user.profile
109   - else:
110   - profile = None
111   -
112   - template_data = {
113   - 'user_profile': profile,
114   - 'email_address': email_address,
115   - 'emails': emails or [],
116   - 'form': form,
117   - 'editable': editable,
118   - }
119   - return render_to_response('user-profile.html', template_data,
120   - RequestContext(request))
121   -
122   -
123   -@login_required
124   -def user_profile_empty(request):
125   - return user_profile(request, request.user)
126   -
127   -
128   -def user_profile_username(request, username):
129   - user = get_object_or_404(User, username=username)
130   - return user_profile(request, user)
131   -
132   -
133   -def user_profile_emailhash(request, emailhash):
134   - email_addr = get_object_or_404(EmailAddress, md5=emailhash)
135   - return user_profile(request, email_addr.user, email_addr)
136   -
137   -
138   -@login_required
139   -def user_profile_edit(request, username):
140   - profile = get_object_or_404(UserProfile, user__username=username)
141   - form = UserUpdateForm(initial=model_to_dict(profile))
142   -
143   - if request.method == "GET":
144   - return user_profile(request, profile.user, editable=True, form=form)
145   -
146   - form = UserUpdateForm(request.POST)
147   - if not form.is_valid():
148   - return user_profile(request, profile.user, editable=True, form=form)
149   -
150   - profile.institution = form.cleaned_data.get('institution')
151   - profile.role = form.cleaned_data.get('role')
152   - profile.twitter = form.cleaned_data.get('twitter')
153   - profile.facebook = form.cleaned_data.get('facebook')
154   - profile.google_talk = form.cleaned_data.get('google_talk')
155   - profile.webpage = form.cleaned_data.get('webpage')
156   - profile.save()
157   -
158   - return redirect('colab.views.user_profile_username', profile.user.username)
159   -
160 0 \ No newline at end of file
colab/views/__init__.py 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +#!/usr/bin/env python
  2 +# encoding: utf-8
... ...
colab/views/other.py 0 → 100644
... ... @@ -0,0 +1,61 @@
  1 +#!/usr/bin/env python
  2 +# encoding: utf-8
  3 +"""
  4 +other.py
  5 +
  6 +Created by Sergio Campos on 2012-01-10.
  7 +"""
  8 +
  9 +from django.template import RequestContext
  10 +from django.shortcuts import render_to_response
  11 +from django.utils.translation import ugettext as _
  12 +
  13 +from colab import solrutils
  14 +from colab.super_archives import queries
  15 +
  16 +
  17 +def home(request):
  18 + """Index page view"""
  19 +
  20 + latest_threads = queries.get_latest_threads()
  21 + hotest_threads = queries.get_hotest_threads()
  22 +
  23 + template_data = {
  24 + 'hotest_threads': hotest_threads[:6],
  25 + 'latest_threads': latest_threads[:6],
  26 + 'type_count': solrutils.count_types(sample=1000),
  27 + 'latest_docs': solrutils.get_latest_collaborations(6),
  28 + }
  29 + return render_to_response('home.html', template_data,
  30 + context_instance=RequestContext(request))
  31 +
  32 +
  33 +def search(request):
  34 + if request.method == 'GET':
  35 + query = request.GET.get('q')
  36 + sort = request.GET.get('o')
  37 + type_ = request.GET.get('type')
  38 + page_number = int(request.GET.get('p', 1))
  39 + results_per_page = int(request.GET.get('per_page', 16))
  40 +
  41 + filters = {
  42 + 'Type': type_,
  43 + }
  44 +
  45 + query = solrutils.build_query(query, filters)
  46 +
  47 + # Query Solr for results
  48 + solr_dict_resp = solrutils.select(query, results_per_page,
  49 + page_number, sort)
  50 +
  51 + docs = solrutils.SolrPaginator(solr_dict_resp, page_number)
  52 +
  53 + template_data = {
  54 + 'docs': docs,
  55 + 'anonymous': _(u'anônimo'),
  56 + 'q': query,
  57 + 'type': type_,
  58 + }
  59 +
  60 + return render_to_response('search.html', template_data,
  61 + RequestContext(request))
... ...
colab/views/signup.py 0 → 100644
... ... @@ -0,0 +1,218 @@
  1 +#!/usr/bin/env python
  2 +# encoding: utf-8
  3 +"""
  4 +signup.py
  5 +
  6 +Created by Sergio Campos on 2012-01-10.
  7 +"""
  8 +
  9 +import uuid
  10 +from colab import signup as signup_
  11 +
  12 +from django.template import RequestContext
  13 +from django.contrib.auth.models import User
  14 +from django.utils.translation import ugettext as _
  15 +from django.contrib.auth.decorators import login_required
  16 +from django.contrib.auth.forms import SetPasswordForm, PasswordChangeForm
  17 +from django.shortcuts import render_to_response, redirect, get_object_or_404
  18 +
  19 +from colab.super_archives.forms import UserCreationForm
  20 +from colab.super_archives.models import UserProfile, EmailAddress
  21 +
  22 +
  23 +def signup(request):
  24 +
  25 + # If the request method is GET just return the form
  26 + if request.method == 'GET':
  27 + form = UserCreationForm()
  28 + return render_to_response('signup-form.html', {'form': form},
  29 + RequestContext(request))
  30 +
  31 + # If the request method is POST try to store data
  32 + form = UserCreationForm(request.POST)
  33 +
  34 + # If there is validation errors give the form back to the user
  35 + if not form.is_valid():
  36 + return render_to_response('signup-form.html', {'form': form},
  37 + RequestContext(request))
  38 +
  39 + user = User(
  40 + username=form.cleaned_data.get('username'),
  41 + email=form.cleaned_data.get('email'),
  42 + first_name=form.cleaned_data.get('first_name'),
  43 + last_name=form.cleaned_data.get('last_name'),
  44 + is_active=False,
  45 + )
  46 + user.set_password(form.cleaned_data.get('password1'))
  47 + user.save()
  48 +
  49 + profile = UserProfile(
  50 + user=user,
  51 + institution=form.cleaned_data.get('institution'),
  52 + role=form.cleaned_data.get('role'),
  53 + twitter=form.cleaned_data.get('twitter'),
  54 + facebook=form.cleaned_data.get('facebook'),
  55 + google_talk=form.cleaned_data.get('google_talk'),
  56 + webpage=form.cleaned_data.get('webpage'),
  57 + verification_hash=uuid.uuid4().get_hex(),
  58 + )
  59 + profile.save()
  60 +
  61 + signup_.send_verification_email(request, user)
  62 +
  63 + # Check if the user's email have been used previously
  64 + # in the mainling lists to link the user to old messages
  65 + email_addr, created = EmailAddress.objects.get_or_create(address=user.email)
  66 + if created:
  67 + email_addr.real_name = user.get_full_name()
  68 +
  69 + email_addr.user = user
  70 + email_addr.save()
  71 +
  72 + template_data = {
  73 + 'msg': _((u'Cadastro efetuado com sucesso. Por favor acesse seu '
  74 + u'endereço de email para validá-lo.')),
  75 + 'msg_css_class': 'success',
  76 + }
  77 +
  78 + return render_to_response('account_message.html', template_data,
  79 + RequestContext(request))
  80 +
  81 +
  82 +def verify_email(request, hash):
  83 + """Verify hash and activate user's account"""
  84 +
  85 + profile = get_object_or_404(UserProfile, verification_hash=hash)
  86 +
  87 + profile.verification_hash = 'verified'
  88 + profile.save()
  89 +
  90 + profile.user.is_active = True
  91 + profile.user.save()
  92 +
  93 + template_data = {
  94 + 'msg': _(u'Endereço de e-mail validado corretamente.'),
  95 + 'msg_css_class': 'success',
  96 + }
  97 +
  98 + return render_to_response('account_message.html', template_data,
  99 + RequestContext(request))
  100 +
  101 +
  102 +def request_reset_password(request):
  103 + """Request a password reset.
  104 +
  105 + In case request method is GET it will display the password reset
  106 + form. Otherwise we'll look for a username in the POST request to
  107 + have its password reset. This user will receive a link where he
  108 + will be allowed to change his password.
  109 +
  110 + """
  111 +
  112 + if request.method == 'GET':
  113 + return render_to_response('account_request_reset_password.html', {},
  114 + RequestContext(request))
  115 +
  116 + username = request.POST.get('username')
  117 +
  118 + try:
  119 + user = User.objects.get(username=username)
  120 + except User.DoesNotExist:
  121 + user = None
  122 +
  123 + if user and user.is_active:
  124 + profile = user.profile
  125 + profile.verification_hash = uuid.uuid4().get_hex()
  126 + profile.save()
  127 +
  128 + signup_.send_reset_password_email(request, user)
  129 +
  130 + msg = _((u'Para sua segurança, dentro de alguns instantes você '
  131 + u'receberá um email solicitando a confirmação do pedido '
  132 + u'de troca de senha. Por favor aguarde.'))
  133 +
  134 + template_data = {
  135 + 'msg': msg,
  136 + 'msg_css_class': 'info',
  137 + }
  138 +
  139 + return render_to_response('account_message.html', template_data,
  140 + RequestContext(request))
  141 +
  142 +
  143 +def reset_password(request, hash):
  144 + """Perform a password change.
  145 +
  146 + If the request method is set to GET and the hash matches a form
  147 + will be displayed to the user allowing the password change.
  148 + If the request method is POST the user password will be changed
  149 + to the newly set data.
  150 +
  151 + """
  152 +
  153 + profile = get_object_or_404(UserProfile, verification_hash=hash)
  154 + user = profile.user
  155 +
  156 + form = SetPasswordForm(profile.user)
  157 +
  158 + template_data = {
  159 + 'form': form,
  160 + 'hash': hash,
  161 + }
  162 +
  163 + if request.method == 'GET':
  164 + return render_to_response('account_change_password.html',
  165 + template_data, RequestContext(request))
  166 +
  167 +
  168 + form = SetPasswordForm(user, request.POST)
  169 + template_data.update({'form': form})
  170 +
  171 + if not form.is_valid():
  172 + return render_to_response('account_change_password.html',
  173 + template_data, RequestContext(request))
  174 +
  175 + profile.verification_hash = 'verified'
  176 + profile.save()
  177 +
  178 + user.set_password(form.cleaned_data.get('new_password1'))
  179 + user.save()
  180 +
  181 + template_data.update({
  182 + 'msg': _(u'Senha alterada com sucesso!'),
  183 + 'msg_css_class': 'success',
  184 + })
  185 + return render_to_response('account_message.html', template_data,
  186 + RequestContext(request))
  187 +
  188 +@login_required
  189 +def change_password(request):
  190 + """Change a password for an authenticated user."""
  191 +
  192 + form = PasswordChangeForm(request.user)
  193 +
  194 + template_data = {
  195 + 'form': form
  196 + }
  197 +
  198 + if request.method == 'GET':
  199 + return render_to_response('account_change_password.html',
  200 + template_data, RequestContext(request))
  201 +
  202 + form = PasswordChangeForm(request.user, request.POST)
  203 + template_data.update({'form': form})
  204 +
  205 + if not form.is_valid():
  206 + return render_to_response('account_change_password.html',
  207 + template_data, RequestContext(request))
  208 +
  209 + request.user.set_password(form.cleaned_data.get('new_password1'))
  210 + request.user.save()
  211 +
  212 + template_data.update({
  213 + 'msg': _(u'Senha alterada com sucesso!'),
  214 + 'msg_css_class': 'success',
  215 + })
  216 + return render_to_response('account_message.html', template_data,
  217 + RequestContext(request))
  218 +
... ...
colab/views/userprofile.py 0 → 100644
... ... @@ -0,0 +1,116 @@
  1 +#!/usr/bin/env python
  2 +# encoding: utf-8
  3 +"""
  4 +userprofile.py
  5 +
  6 +Created by Sergio Campos on 2012-01-10.
  7 +"""
  8 +
  9 +from django.template import RequestContext
  10 +from django.contrib.auth.models import User
  11 +from django.forms.models import model_to_dict
  12 +from django.contrib.auth.decorators import login_required
  13 +from django.shortcuts import render_to_response, get_object_or_404, redirect
  14 +
  15 +from colab import solrutils
  16 +from colab.super_archives.forms import UserCreationForm, UserUpdateForm
  17 +from colab.super_archives.models import Message, UserProfile, EmailAddress
  18 +
  19 +
  20 +def read(request, user, email_address=None, editable=False, form=None):
  21 +
  22 + if form is None:
  23 + form = UserCreationForm()
  24 +
  25 + if user:
  26 + email_addresses = user.emails.all()
  27 + profile = user.profile
  28 + last_modified_docs = solrutils.get_latest_collaborations(
  29 + username=user.username
  30 + )
  31 +
  32 + type_count = solrutils.count_types(
  33 + filters={'collaborator': user.username}
  34 + )
  35 +
  36 + else:
  37 + email_addresses = [email_address]
  38 + profile = None
  39 + last_modified_docs = []
  40 + type_count = {}
  41 +
  42 + if not email_address and email_addresses:
  43 + email_address = email_addresses[0]
  44 +
  45 + email_addresses_ids = tuple([str(addr.id) for addr in email_addresses])
  46 +
  47 + query = """
  48 + SELECT
  49 + *
  50 + FROM
  51 + super_archives_message JOIN (
  52 + SELECT id
  53 + FROM super_archives_message
  54 + WHERE from_address_id IN (%(ids)s)
  55 + GROUP BY thread_id, id
  56 + ) AS subquery
  57 + ON subquery.id = super_archives_message.id
  58 + ORDER BY
  59 + received_time DESC
  60 + LIMIT 10;
  61 +
  62 + """ % {'ids': ','.join(email_addresses_ids)}
  63 +
  64 + emails = Message.objects.raw(query)
  65 + #n_sent = Message.objects.filter(from_address__in=email_addresses).count()
  66 +
  67 + template_data = {
  68 + 'user_profile': profile,
  69 + 'email_address': email_address,
  70 + 'emails': emails or [],
  71 + 'form': form,
  72 + 'editable': editable,
  73 + 'type_count': type_count,
  74 + 'docs': last_modified_docs,
  75 + }
  76 +
  77 + return render_to_response('user-profile.html', template_data,
  78 + RequestContext(request))
  79 +
  80 +
  81 +@login_required
  82 +def by_request_user(request):
  83 + return read(request, request.user)
  84 +
  85 +
  86 +def by_username(request, username):
  87 + user = get_object_or_404(User, username=username)
  88 + return read(request, user)
  89 +
  90 +
  91 +def by_emailhash(request, emailhash):
  92 + email_addr = get_object_or_404(EmailAddress, md5=emailhash)
  93 + return read(request, email_addr.user, email_addr)
  94 +
  95 +
  96 +@login_required
  97 +def update(request, username):
  98 + profile = get_object_or_404(UserProfile, user__username=username)
  99 + form = UserUpdateForm(initial=model_to_dict(profile))
  100 +
  101 + if request.method == "GET":
  102 + return read(request, profile.user, editable=True, form=form)
  103 +
  104 + form = UserUpdateForm(request.POST)
  105 + if not form.is_valid():
  106 + return read(request, profile.user, editable=True, form=form)
  107 +
  108 + profile.institution = form.cleaned_data.get('institution')
  109 + profile.role = form.cleaned_data.get('role')
  110 + profile.twitter = form.cleaned_data.get('twitter')
  111 + profile.facebook = form.cleaned_data.get('facebook')
  112 + profile.google_talk = form.cleaned_data.get('google_talk')
  113 + profile.webpage = form.cleaned_data.get('webpage')
  114 + profile.save()
  115 +
  116 + return redirect('user_profile', profile.user.username)
... ...
etc/apache2/sites-available/colab 0 → 100644
... ... @@ -0,0 +1,64 @@
  1 +<VirtualHost *:80>
  2 + ServerName colab01a.interlegis.leg.br
  3 + ServerAlias colab.interlegis.leg.br
  4 + ServerAlias colab.interlegis.gov.br
  5 +
  6 + <Proxy *>
  7 + Order deny,allow
  8 + Allow from all
  9 + </Proxy>
  10 +
  11 + RewriteEngine On
  12 + RewriteRule ^/admin(.*) http://colab-backend.interlegis.leg.br/admin$1 [P]
  13 + RewriteRule ^/wiki(.*) http://colab-backend.interlegis.leg.br/wiki$1 [P]
  14 + RewriteRule ^/changeset(.*) http://colab-backend.interlegis.leg.br/changeset$1 [P]
  15 + RewriteRule ^/newticket(.*) http://colab-backend.interlegis.leg.br/newticket$1 [P]
  16 + RewriteRule ^/ticket(.*) http://colab-backend.interlegis.leg.br/ticket$1 [P]
  17 + RewriteRule ^/chrome(.*) http://colab-backend.interlegis.leg.br/chrome$1 [P]
  18 + RewriteRule ^/timeline(.*) http://colab-backend.interlegis.leg.br/timeline$1 [P]
  19 + RewriteRule ^/roadmap(.*) http://colab-backend.interlegis.leg.br/roadmap$1 [P]
  20 + RewriteRule ^/browser(.*) http://colab-backend.interlegis.leg.br/browser$1 [P]
  21 + RewriteRule ^/report(.*) http://colab-backend.interlegis.leg.br/report$1 [P]
  22 + RewriteRule ^/tags(.*) http://colab-backend.interlegis.leg.br/tags$1 [P]
  23 + RewriteRule ^/query(.*) http://colab-backend.interlegis.leg.br/query$1 [P]
  24 + RewriteRule ^/about(.*) http://colab-backend.interlegis.leg.br/about$1 [P]
  25 + RewriteRule ^/prefs(.*) http://colab-backend.interlegis.leg.br/prefs$1 [P]
  26 + RewriteRule ^/login(.*) http://colab-backend.interlegis.leg.br/login$1 [P]
  27 + RewriteRule ^/logout(.*) http://colab-backend.interlegis.leg.br/logout$1 [P]
  28 + RewriteRule ^/attachment(.*) http://colab-backend.interlegis.leg.br/attachment$1 [P]
  29 + RewriteRule ^/raw-attachment(.*) http://colab-backend.interlegis.leg.br/raw-attachment$1 [P]
  30 +
  31 + WSGIDaemonProcess colab user=www-data group=www-data threads=25
  32 + WSGIProcessGroup colab
  33 +
  34 + Alias /static/admin/ /usr/local/django/colab/lib/python2.6/site-packages/django/contrib/admin/media/
  35 + <Directory /usr/local/django/colab/lib/python2.6/site-packages/django/contrib/admin/media/>
  36 + Options -Indexes
  37 + Order deny,allow
  38 + Allow from all
  39 + AllowOverride None
  40 + </Directory>
  41 +
  42 + Alias /static/ /usr/local/django/colab/static/
  43 + <Directory /usr/local/django/colab/static/>
  44 + Options -Indexes
  45 + Order deny,allow
  46 + Allow from all
  47 + AllowOverride None
  48 + </Directory>
  49 +
  50 + WSGIScriptAlias / /usr/local/django/colab/wsgi/colab.wsgi
  51 + <Directory /usr/local/django/colab/wsgi/>
  52 + Order deny,allow
  53 + Allow from all
  54 + AllowOverride None
  55 + </Directory>
  56 +
  57 + ErrorLog /var/log/apache2/error.log
  58 +
  59 + # Possible values include: debug, info, notice, warn, error, crit,
  60 + # alert, emerg.
  61 + LogLevel warn
  62 +
  63 + CustomLog /var/log/apache2/access.log combined
  64 +</VirtualHost>
... ...
etc/apache2/wsgi/colab.wsgi 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +import site
  2 +import os
  3 +
  4 +cwd_path = os.path.abspath(os.path.dirname(__file__))
  5 +virtualenv_path = os.path.join(cwd_path, '../lib/python2.6/site-packages')
  6 +site.addsitedir(virtualenv_path)
  7 +
  8 +from django.core.handlers.wsgi import WSGIHandler
  9 +os.environ['DJANGO_SETTINGS_MODULE'] = 'colab.settings'
  10 +application = WSGIHandler()
  11 +
  12 +
... ...
etc/autofs/listas 0 → 100644
... ... @@ -0,0 +1 @@
  1 +archives -fstype=fuse,rw,nodev,users,noatime :sshfs\#root@listas.interlegis.gov.br\:/var/lib/mailman/archives/private
... ...
etc/cron.d/colab_import_emails 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +PATH="/usr/local/django/colab/bin"
  2 +* * * * * root DJANGO_SETTINGS_MODULE="colab.settings" django-admin.py import_emails --archives_path=/usr/local/django/colab/mnt/archives/ --exclude-list=saberes-divulgacao --exclude-list=pml --exclude-list=mailman --exclude-list=lexml-anuncios > /tmp/colab-import-emails-last.log
... ...
etc/cron.d/colab_solr_reindex 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +PATH="/usr/bin"
  2 +*/1 * * * * root wget "http://solr.interlegis.leg.br:8080/solr/dataimport?wt=json&indent=true&command=delta-import" -q -O - >> /tmp/solr-delta-import.log
  3 +4 6 * * * root wget "http://solr.interlegis.leg.br:8080/solr/dataimport?wt=json&indent=true&command=full-import" -q -O - >> /tmp/solr-full-import.log
... ...
requirements.txt
1 1 Django==1.3.1
2 2 South==0.7.3
3   -django-registration==0.7
4 3 django-piston==0.2.3
5   -pytz
  4 +pytz==2011n
6 5 chardet==1.0.1
  6 +python-dateutil==1.5
  7 +django-cliauth==0.9
... ...
setup.py
... ... @@ -0,0 +1,49 @@
  1 +#!/usr/bin/env python
  2 +
  3 +from setuptools import setup, find_packages
  4 +from setupext import Data_Files, install_Data_Files
  5 +
  6 +setup(name='colab',
  7 + version='0.1-dev',
  8 + author='Sergio Oliveira',
  9 + author_email='seocam@seocam.com',
  10 + url='https://bitbucket.org/seocam/atu-colab',
  11 + packages=find_packages(),
  12 + package_data={'colab': ['templates/*.html',
  13 + 'super_archives/templates/*.html',
  14 + 'super_archives/fixtures/initial_data.json']},
  15 + data_files=[
  16 + Data_Files(base_dir='install_data',
  17 + copy_to='static',
  18 + template=['recursive-include colab/static *'],
  19 + preserve_path=1,
  20 + strip_dirs=2),
  21 + Data_Files(base_dir='install_data',
  22 + copy_to='wsgi',
  23 + template=['include etc/apache2/wsgi/colab.wsgi'],
  24 + preserve_path=0),
  25 + Data_Files(base_dir='install_data',
  26 + copy_to='apache-site',
  27 + template=['include etc/apache2/sites-available/colab'],
  28 + preserve_path=0),
  29 + Data_Files(base_dir='install_data',
  30 + copy_to='autofs',
  31 + template=['include etc/autofs/listas'],
  32 + preserve_path=0),
  33 + Data_Files(base_dir='install_data',
  34 + copy_to='cron.d',
  35 + template=['include etc/cron.d/*'],
  36 + preserve_path=0),
  37 + ],
  38 + install_requires=(
  39 + 'distribute',
  40 + 'Django==1.3.1',
  41 + 'South==0.7.3',
  42 + 'django-piston==0.2.3',
  43 + 'django-cliauth==0.9',
  44 + 'pytz==2011n',
  45 + 'chardet==1.0.1',
  46 + 'python-dateutil==1.5',
  47 + 'psycopg2==2.4.4'),
  48 + cmdclass = {"install_data": install_Data_Files},
  49 +)
... ...
setupext/__init__.py 0 → 100755
... ... @@ -0,0 +1,15 @@
  1 +"Package providing extensions to the standard distutils"
  2 +
  3 +from install_data import Data_Files, install_Data_Files
  4 +
  5 +from distutils.command.bdist_wininst import bdist_wininst
  6 +
  7 +# When building a windows installer, put some more text into
  8 +# the long description
  9 +class wininst_request_delete(bdist_wininst):
  10 + add_text = "\nIf you have installed earlier versions of this package, please remove them through 'Add/Remove Programs' before installing this release."
  11 +
  12 + def get_inidata(self):
  13 + m = self.distribution.metadata
  14 + m.long_description = m.long_description + self.add_text
  15 + return bdist_wininst.get_inidata(self)
... ...
setupext/install_data.py 0 → 100755
... ... @@ -0,0 +1,197 @@
  1 +# -*- coding: utf-8 -*-
  2 +"""install_data.py
  3 +
  4 +Provides a more sophisticated facility to install data files
  5 +than distutils' install_data does.
  6 +You can specify your files as a template like in MANIFEST.in
  7 +and you have more control over the copy process.
  8 +
  9 +Copyright 2000 by Rene Liebscher, Germany.
  10 +
  11 +Permission is hereby granted, free of charge, to any person obtaining
  12 +a copy of this software and associated documentation files (the
  13 +"Software"), to deal in the Software without restriction, including
  14 +without limitation the rights to use, copy, modify, merge, publish,
  15 +distribute, sublicense, and/or sell copies of the Software, and to
  16 +permit persons to whom the Software is furnished to do so, subject to
  17 +the following conditions:
  18 +
  19 +The above copyright notice and this permission notice shall be included
  20 +in all copies or substantial portions of the Software.
  21 +
  22 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  25 +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  26 +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  27 +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  28 +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29 +
  30 +"""
  31 +
  32 +# created 2000/08/01, Rene Liebscher <R.Liebscher@gmx.de>
  33 +# modified 2000/12/18, Martin v. Löwis <loewis@informatik.hu-berlin.de>
  34 +
  35 +###########################################################################
  36 +# import some modules we need
  37 +
  38 +import os,sys,string
  39 +from types import StringType,TupleType,ListType
  40 +from distutils.util import change_root
  41 +from distutils.filelist import FileList
  42 +from distutils.command.install_data import install_data
  43 +
  44 +###########################################################################
  45 +# a container class for our more sophisticated install mechanism
  46 +
  47 +class Data_Files:
  48 + """ container for list of data files.
  49 + supports alternate base_dirs e.g. 'install_lib','install_header',...
  50 + supports a directory where to copy files
  51 + supports templates as in MANIFEST.in
  52 + supports preserving of paths in filenames
  53 + eg. foo/xyz is copied to base_dir/foo/xyz
  54 + supports stripping of leading dirs of source paths
  55 + eg. foo/bar1/xyz, foo/bar2/abc can be copied to bar1/xyz, bar2/abc
  56 + """
  57 +
  58 + def __init__(self,base_dir=None,files=None,copy_to=None,template=None,preserve_path=0,strip_dirs=0):
  59 + self.base_dir = base_dir
  60 + self.files = files
  61 + self.copy_to = copy_to
  62 + if template is not None:
  63 + t = []
  64 + for item in template:
  65 + item = string.strip(item)
  66 + if not item:continue
  67 + t.append(item)
  68 + template = t
  69 + self.template = template
  70 + self.preserve_path = preserve_path
  71 + self.strip_dirs = strip_dirs
  72 + self.finalized = 0
  73 +
  74 + def warn (self, msg):
  75 + sys.stderr.write ("warning: %s: %s\n" %
  76 + ("install_data", msg))
  77 +
  78 + def debug_print (self, msg):
  79 + """Print 'msg' to stdout if the global DEBUG (taken from the
  80 + DISTUTILS_DEBUG environment variable) flag is true.
  81 + """
  82 + from distutils.core import DEBUG
  83 + if DEBUG:
  84 + print msg
  85 +
  86 +
  87 + def finalize(self):
  88 + """ complete the files list by processing the given template """
  89 + if self.finalized:
  90 + return
  91 + if self.files == None:
  92 + self.files = []
  93 + if self.template != None:
  94 + if type(self.template) == StringType:
  95 + self.template = string.split(self.template,";")
  96 + filelist = FileList(self.warn,self.debug_print)
  97 + for line in self.template:
  98 + filelist.process_template_line(string.strip(line))
  99 + filelist.sort()
  100 + filelist.remove_duplicates()
  101 + self.files.extend(filelist.files)
  102 + self.finalized = 1
  103 +
  104 +# end class Data_Files
  105 +
  106 +###########################################################################
  107 +# a more sophisticated install routine than distutils install_data
  108 +
  109 +class install_Data_Files (install_data):
  110 +
  111 + def check_data(self,d):
  112 + """ check if data are in new format, if not create a suitable object.
  113 + returns finalized data object
  114 + """
  115 + if not isinstance(d, Data_Files):
  116 + self.warn(("old-style data files list found "
  117 + "-- please convert to Data_Files instance"))
  118 + if type(d) is TupleType:
  119 + if len(d) != 2 or not (type(d[1]) is ListType):
  120 + raise DistutilsSetupError, \
  121 + ("each element of 'data_files' option must be an "
  122 + "Data File instance, a string or 2-tuple (string,[strings])")
  123 + d = Data_Files(copy_to=d[0],files=d[1])
  124 + else:
  125 + if not (type(d) is StringType):
  126 + raise DistutilsSetupError, \
  127 + ("each element of 'data_files' option must be an "
  128 + "Data File instance, a string or 2-tuple (string,[strings])")
  129 + d = Data_Files(files=[d])
  130 + d.finalize()
  131 + return d
  132 +
  133 + def run(self):
  134 + self.outfiles = []
  135 + install_cmd = self.get_finalized_command('install')
  136 +
  137 + for d in self.data_files:
  138 + d = self.check_data(d)
  139 +
  140 + install_dir = self.install_dir
  141 + # alternative base dir given => overwrite install_dir
  142 + if d.base_dir != None:
  143 + install_dir = getattr(install_cmd,d.base_dir)
  144 +
  145 + # copy to an other directory
  146 + if d.copy_to != None:
  147 + if not os.path.isabs(d.copy_to):
  148 + # relatiev path to install_dir
  149 + dir = os.path.join(install_dir, d.copy_to)
  150 + elif install_cmd.root:
  151 + # absolute path and alternative root set
  152 + dir = change_root(self.root,d.copy_to)
  153 + else:
  154 + # absolute path
  155 + dir = d.copy_to
  156 + else:
  157 + # simply copy to install_dir
  158 + dir = install_dir
  159 + # warn if necceassary
  160 + self.warn("setup script did not provide a directory to copy files to "
  161 + " -- installing right in '%s'" % install_dir)
  162 +
  163 + dir=os.path.normpath(dir)
  164 + # create path
  165 + self.mkpath(dir)
  166 +
  167 + # copy all files
  168 + for src in d.files:
  169 + if d.strip_dirs > 0:
  170 + dst = string.join(string.split(os.path.normcase(src),os.sep)[d.strip_dirs:],os.sep)
  171 + else:
  172 + dst = src
  173 + if d.preserve_path:
  174 + # preserve path in filename
  175 + self.mkpath(os.path.dirname(os.path.join(dir,dst)))
  176 + out = self.copy_file(src, os.path.join(dir,dst))
  177 + else:
  178 + out = self.copy_file(src, dir)
  179 + if type(out) is TupleType:
  180 + out = out[0]
  181 + self.outfiles.append(out)
  182 +
  183 + return self.outfiles
  184 +
  185 + def get_inputs (self):
  186 + inputs = []
  187 + for d in self.data_files:
  188 + d = self.check_data(d)
  189 + inputs.append(d.files)
  190 + return inputs
  191 +
  192 + def get_outputs (self):
  193 + return self.outfiles
  194 +
  195 +
  196 +###########################################################################
  197 +
... ...
solr-conf/README 0 → 100644
... ... @@ -0,0 +1,46 @@
  1 +Installation instructions for Ubuntu 10.04
  2 +-------------------------------------------
  3 +
  4 +* Install Java, tomcat, JDBC Postgres drivers (Ubuntu partner repositories must be enabled):
  5 +sudo apt-get install sun-java6-bin tomcat6 libpg-java
  6 +
  7 +* Download Solr 3.3 and extract it:
  8 +wget http://ftp.unicamp.br/pub/apache/lucene/solr/3.3.0/apache-solr-3.3.0.tgz
  9 +tar xzf apache-solr-3.3.0.tgz
  10 +
  11 +* Create the directory /var/local/lib/solr/ and give the right permissions:
  12 +sudo mkdir -p /var/local/lib/solr/
  13 +sudo chown tomcat6:tomcat6 /var/local/lib/solr/
  14 +
  15 +* Copy the solr home example to /usr/local/share/:
  16 +sudo cp -R apache-solr-3.3.0/example/solr /usr/local/share/
  17 +
  18 +* Create a folder for libs in the solr home:
  19 +sudo mkdir /usr/local/share/solr/lib/
  20 +
  21 +* Copy Solr libs to libs folder:
  22 +sudo cp apache-solr-3.3.0/dist/*.jar /usr/local/share/solr/lib/
  23 +
  24 +* Copy Solr distribution to solr home:
  25 +sudo cp apache-solr-3.3.0/dist/apache-solr-3.3.0.war /usr/local/share/solr/
  26 +
  27 +* Link the JDBC Postgres drivers into the Solr installation:
  28 +sudo ln -s /usr/share/java/postgresql-jdbc3-8.4.jar /usr/local/share/solr/lib/
  29 +
  30 +* Link configurations to /etc
  31 +sudo ln -s /usr/local/share/solr/conf/ /etc/solr
  32 +
  33 +* Copy the configuration files from this folder into /etc/solr/
  34 +
  35 +* Link the solr-tomcat.xml file in the Tomcat configuration:
  36 +sudo ln -s /etc/solr/solr-tomcat.xml /etc/tomcat6/Catalina/localhost/solr.xml
  37 +
  38 +* Check data-config.xml to make sure all information to connect to the databases are right
  39 +
  40 +* Create a dataimport.properties on /etc/solr and give write access to tomcat6:
  41 +sudo touch /etc/solr/dataimport.properties
  42 +sudo chown tomcat6:tomcat6 /etc/solr/dataimport.properties
  43 +
  44 +* Restart tomcat:
  45 +sudo /etc/init.d/tomcat6 restart
  46 +
... ...
solr-conf/data-config.xml 0 → 100644
... ... @@ -0,0 +1,337 @@
  1 +<dataConfig>
  2 + <dataSource name="trac"
  3 + type="JdbcDataSource"
  4 + driver="org.postgresql.Driver"
  5 + url="jdbc:postgresql://bdinterlegis.interlegis.leg.br/trac_colab"
  6 + user="colab" />
  7 + <dataSource name="colab"
  8 + type="JdbcDataSource"
  9 + driver="org.postgresql.Driver"
  10 + url="jdbc:postgresql://bdinterlegis.interlegis.leg.br/colab"
  11 + user="colab" />
  12 +
  13 + <document>
  14 +
  15 + <entity name="wiki"
  16 + dataSource="trac"
  17 + transformer="TemplateTransformer,DateFormatTransformer"
  18 + query="SELECT
  19 + name,
  20 + TIMESTAMP 'epoch' + (max(time)/1000000) * INTERVAL '1s' AS modified,
  21 + max(version) AS version
  22 + FROM wiki GROUP BY name"
  23 + deltaQuery="
  24 + SELECT DISTINCT
  25 + name
  26 + FROM
  27 + wiki
  28 + WHERE
  29 + time > (EXTRACT(
  30 + epoch FROM TIMESTAMP '${dataimporter.wiki.last_index_time}'
  31 + ) * 1000000)"
  32 + deltaImportQuery="
  33 + SELECT
  34 + name,
  35 + TIMESTAMP 'epoch' + (max(time)/1000000) * INTERVAL '1s' AS modified,
  36 + max(version) AS version
  37 + FROM
  38 + wiki
  39 + WHERE
  40 + name = '${dataimporter.delta.id}'
  41 + GROUP BY name">
  42 +
  43 + <entity name="wiki_creation"
  44 + dataSource="trac"
  45 + query="SELECT
  46 + author AS Creator,
  47 + TIMESTAMP 'epoch' + (time/1000000) * INTERVAL '1s' AS created
  48 + FROM
  49 + wiki
  50 + WHERE
  51 + name = '${wiki.name}'
  52 + AND version = 1" />
  53 +
  54 + <entity name="wiki_collaborators"
  55 + dataSource="trac"
  56 + query="SELECT DISTINCT
  57 + author AS collaborator
  58 + FROM
  59 + wiki
  60 + WHERE
  61 + name = '${wiki.name}'
  62 + AND author != ''" />
  63 +
  64 + <entity name="content"
  65 + dataSource="trac"
  66 + query="SELECT
  67 + text AS content
  68 + FROM
  69 + wiki
  70 + WHERE
  71 + name = '${wiki.name}'
  72 + AND version = '${wiki.version}'" />
  73 +
  74 + <field column="UID" template="WIKI_${wiki.name}" />
  75 + <field column="getId" template="${wiki.name}" />
  76 + <field column="Type" template="wiki" />
  77 + <field column="Title" template="${wiki.name}" />
  78 + <field column="created" name="created"
  79 + dateTimeFormat="yyyy-MM-dd hh:mm:ss"/>
  80 + <field column="modified" name="modified"
  81 + dateTimeFormat="yyyy-MM-dd hh:mm:ss"/>
  82 + <field column="path_string" template="/wiki/${wiki.name}" />
  83 + </entity>
  84 +
  85 + <entity name="ticket"
  86 + dataSource="trac"
  87 + transformer="TemplateTransformer,DateFormatTransformer"
  88 + pk="id"
  89 + deltaQuery="
  90 + SELECT
  91 + id
  92 + FROM
  93 + ticket
  94 + WHERE
  95 + time > (EXTRACT(
  96 + epoch FROM TIMESTAMP '${dataimporter.ticket.last_index_time}'
  97 + ) * 1000000)"
  98 + query="SELECT
  99 + id,
  100 + summary,
  101 + description AS Description,
  102 + milestone,
  103 + priority,
  104 + component,
  105 + version,
  106 + severity,
  107 + reporter,
  108 + owner,
  109 + status,
  110 + TIMESTAMP 'epoch' + (time/1000000)* INTERVAL '1s' AS created,
  111 + TIMESTAMP 'epoch' + (changetime/1000000) * INTERVAL '1s' AS modified
  112 + FROM
  113 + ticket">
  114 +
  115 + <entity name="ticket_collaborator"
  116 + dataSource="trac"
  117 + query="SELECT
  118 + reporter AS collaborator
  119 + FROM
  120 + ticket
  121 + WHERE
  122 + id = ${ticket.id}
  123 +
  124 + UNION
  125 +
  126 + SELECT
  127 + owner AS collaborator
  128 + FROM
  129 + ticket
  130 + WHERE
  131 + id = ${ticket.id}
  132 +
  133 + UNION
  134 +
  135 + SELECT DISTINCT
  136 + author AS collaborator
  137 + FROM
  138 + ticket_change
  139 + WHERE
  140 + ticket = ${ticket.id}" />
  141 +
  142 + <entity name="ticket_keywords"
  143 + dataSource="trac"
  144 + query="SELECT DISTINCT
  145 + REGEXP_SPLIT_TO_TABLE(keywords, ',|\\s') AS keyword
  146 + FROM
  147 + ticket
  148 + WHERE
  149 + id = ${ticket.id} AND
  150 + keywords != ''" />
  151 +
  152 + <entity name="ticket_comments"
  153 + dataSource="trac"
  154 + query="SELECT
  155 + newvalue AS comment
  156 + FROM
  157 + ticket_change
  158 + WHERE
  159 + ticket = ${ticket.id}
  160 + AND field = 'comment'" />
  161 +
  162 + <field column="UID" template="TICKET_${ticket.id}" />
  163 + <field column="getId" template="${ticket.id}" />
  164 + <field column="Type" template="ticket" />
  165 + <field column="path_string" template="/ticket/${ticket.id}" />
  166 + <field column="Title"
  167 + template="#${ticket.id} (${ticket.status}) - ${ticket.summary}" />
  168 + <field column="Creator" template="${ticket.reporter}" />
  169 + <field column="created" name="created"
  170 + dateTimeFormat="yyyy-MM-dd hh:mm:ss"/>
  171 + <field column="modified" name="modified"
  172 + dateTimeFormat="yyyy-MM-dd hh:mm:ss"/>
  173 + </entity>
  174 +
  175 + <entity name="changeset"
  176 + dataSource="trac"
  177 + transformer="TemplateTransformer,DateFormatTransformer"
  178 + pk="rev"
  179 + deltaQuery="
  180 + SELECT
  181 + rev
  182 + FROM
  183 + revision
  184 + WHERE
  185 + time > (EXTRACT(
  186 + epoch FROM TIMESTAMP '${dataimporter.changeset.last_index_time}'
  187 + ) * 1000000)"
  188 +
  189 + query="SELECT
  190 + rev AS revision,
  191 + author AS Creator,
  192 + author AS collaborator,
  193 + repos.value AS repos_name,
  194 + TIMESTAMP 'epoch' + (time/1000000) * INTERVAL '1s' AS created,
  195 + TIMESTAMP 'epoch' + (time/1000000) * INTERVAL '1s' AS modified,
  196 + message
  197 + FROM
  198 + revision AS rev JOIN
  199 + repository AS repos
  200 + ON rev.repos = repos.id AND
  201 + repos.name = 'name' AND repos.value != ''">
  202 +
  203 + <field column="UID" template="CHANGESET_${changeset.revision}" />
  204 + <field column="getId" template="${changeset.revision}" />
  205 + <field column="Type" template="changeset" />
  206 + <field column="path_string"
  207 + template="/changeset/${changeset.revision}/${changeset.repos_name}"
  208 + />
  209 + <field column="Title"
  210 + template="[${changeset.revision}] - ${changeset.message}" />
  211 + <field column="created" name="created"
  212 + dateTimeFormat="yyyy-MM-dd hh:mm:ss"/>
  213 + <field column="modified" name="modified"
  214 + dateTimeFormat="yyyy-MM-dd hh:mm:ss"/>
  215 + </entity>
  216 +
  217 + <entity name="thread"
  218 + dataSource="colab"
  219 + transformer="TemplateTransformer,DateFormatTransformer"
  220 + deltaQuery="
  221 + SELECT
  222 + thread_id AS id
  223 + FROM
  224 + super_archives_message
  225 + GROUP BY
  226 + thread_id
  227 + HAVING
  228 + max(received_time) > '${dataimporter.thread.last_index_time}'"
  229 + deltaImportQuery="SELECT
  230 + sam.thread_id AS id,
  231 + sat.subject_token AS name,
  232 + sat.latest_message_id,
  233 + saml.name AS mailinglist,
  234 + array_to_string(array_agg(sam.body), ' ') AS content
  235 + FROM
  236 + super_archives_message AS sam
  237 + JOIN super_archives_thread AS sat
  238 + ON sat.id = sam.thread_id
  239 + JOIN super_archives_mailinglist AS saml
  240 + ON sat.mailinglist_id = saml.id
  241 + WHERE
  242 + sat.id = '${dataimporter.delta.id}'
  243 + GROUP BY
  244 + sam.thread_id,
  245 + sat.subject_token,
  246 + sat.latest_message_id,
  247 + saml.name"
  248 +
  249 + query="SELECT
  250 + sam.thread_id AS id,
  251 + sat.subject_token AS name,
  252 + sat.latest_message_id,
  253 + saml.name AS mailinglist,
  254 + array_to_string(array_agg(sam.body), ' ') AS content
  255 + FROM
  256 + super_archives_message AS sam
  257 + JOIN super_archives_thread AS sat
  258 + ON sat.id = sam.thread_id
  259 + JOIN super_archives_mailinglist AS saml
  260 + ON sat.mailinglist_id = saml.id
  261 + WHERE
  262 + sat.spam IS NOT True
  263 + GROUP BY
  264 + sam.thread_id,
  265 + sat.subject_token,
  266 + sat.latest_message_id,
  267 + saml.name">
  268 +
  269 + <!--
  270 + Check about "DISTINCT ON" here:
  271 + http://archives.postgresql.org/pgsql-general/2002-06/msg01330.php
  272 + -->
  273 + <entity name="first_message"
  274 + dataSource="colab"
  275 + transformer="TemplateTransformer"
  276 + query="SELECT DISTINCT ON (sam.thread_id)
  277 + sam.body AS Description,
  278 + sam.received_time AS created,
  279 + sam.subject_clean AS subject,
  280 + saea.real_name AS creator_real_name,
  281 + saea.md5 AS creator_email_md5,
  282 + au.username AS Creator
  283 + FROM
  284 + super_archives_message AS sam
  285 + JOIN super_archives_emailaddress AS saea
  286 + ON sam.from_address_id = saea.id
  287 + LEFT JOIN auth_user AS au
  288 + ON au.id = saea.user_id
  289 + WHERE
  290 + sam.thread_id = ${thread.id}
  291 + ORDER BY
  292 + sam.thread_id,
  293 + sam.received_time">
  294 + <field column="Title" template="${first_message.subject}" />
  295 + <field column="creator_profile_uri"
  296 + template="/user/hash/${first_message.creator_email_md5}" />
  297 + </entity>
  298 +
  299 + <entity name="latest_message"
  300 + dataSource="colab"
  301 + query="SELECT
  302 + received_time AS modified
  303 + FROM
  304 + super_archives_message
  305 + WHERE
  306 + id = ${thread.latest_message_id}" />
  307 +
  308 + <entity name="thread_collaborators"
  309 + dataSource="colab"
  310 + query="SELECT DISTINCT
  311 + au.username AS collaborator
  312 + FROM
  313 + super_archives_message AS sam
  314 + JOIN super_archives_emailaddress AS saea
  315 + ON sam.from_address_id = saea.id
  316 + JOIN auth_user AS au
  317 + ON au.id = saea.user_id
  318 + WHERE
  319 + thread_id = ${thread.id}" />
  320 +
  321 + <field column="UID" template="THREAD_${thread.id}" />
  322 + <field column="getId" template="${thread.name}" />
  323 + <field column="Type" template="thread" />
  324 + <field column="path_string" template="/archives/thread/${thread.name}" />
  325 + <field column="created" name="created"
  326 + dateTimeFormat="yyyy-MM-dd hh:mm:ss" />
  327 + <field column="modified" name="modified"
  328 + dateTimeFormat="yyyy-MM-dd hh:mm:ss" />
  329 + </entity>
  330 + </document>
  331 +
  332 +</dataConfig>
  333 +
  334 +<!--
  335 +vim: ts=2 sw=2 ss=2 expandtab:
  336 +-->
  337 +
... ...
solr-conf/schema.xml 0 → 100644
... ... @@ -0,0 +1,612 @@
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +<!--
  3 + Licensed to the Apache Software Foundation (ASF) under one or more
  4 + contributor license agreements. See the NOTICE file distributed with
  5 + this work for additional information regarding copyright ownership.
  6 + The ASF licenses this file to You under the Apache License, Version 2.0
  7 + (the "License"); you may not use this file except in compliance with
  8 + the License. You may obtain a copy of the License at
  9 +
  10 + http://www.apache.org/licenses/LICENSE-2.0
  11 +
  12 + Unless required by applicable law or agreed to in writing, software
  13 + distributed under the License is distributed on an "AS IS" BASIS,
  14 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15 + See the License for the specific language governing permissions and
  16 + limitations under the License.
  17 +-->
  18 +
  19 +<!--
  20 + This is the Solr schema file. This file should be named "schema.xml" and
  21 + should be in the conf directory under the solr home
  22 + (i.e. ./solr/conf/schema.xml by default)
  23 + or located where the classloader for the Solr webapp can find it.
  24 +
  25 + This example schema is the recommended starting point for users.
  26 + It should be kept correct and concise, usable out-of-the-box.
  27 +
  28 + For more information, on how to customize this file, please see
  29 + http://wiki.apache.org/solr/SchemaXml
  30 +-->
  31 +
  32 +<schema name="solr-instance" version="1.4">
  33 + <!-- attribute "name" is the name of this schema and is only used for display purposes.
  34 + Applications should change this to reflect the nature of the search collection.
  35 + version="1.4" is Solr's version number for the schema syntax and semantics. It should
  36 + not normally be changed by applications.
  37 + 1.0: multiValued attribute did not exist, all fields are multiValued by nature
  38 + 1.1: multiValued attribute introduced, false by default
  39 + 1.2: omitTermFreqAndPositions attribute introduced, true by default except for text fields.
  40 + 1.3: removed optional field compress feature
  41 + 1.4: default auto-phrase (QueryParser feature) to off
  42 + -->
  43 +
  44 + <types>
  45 + <!-- field type definitions. The "name" attribute is
  46 + just a label to be used by field definitions. The "class"
  47 + attribute and any other attributes determine the real
  48 + behavior of the fieldType.
  49 + Class names starting with "solr" refer to java classes in the
  50 + org.apache.solr.analysis package.
  51 + -->
  52 +
  53 + <!-- The StrField type is not analyzed, but indexed/stored verbatim. -->
  54 + <fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
  55 +
  56 + <!-- boolean type: "true" or "false" -->
  57 + <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true" omitNorms="true"/>
  58 + <!--Binary data type. The data should be sent/retrieved in as Base64 encoded Strings -->
  59 + <fieldtype name="binary" class="solr.BinaryField"/>
  60 +
  61 + <!-- The optional sortMissingLast and sortMissingFirst attributes are
  62 + currently supported on types that are sorted internally as strings.
  63 + This includes "string","boolean","sint","slong","sfloat","sdouble","pdate"
  64 + - If sortMissingLast="true", then a sort on this field will cause documents
  65 + without the field to come after documents with the field,
  66 + regardless of the requested sort order (asc or desc).
  67 + - If sortMissingFirst="true", then a sort on this field will cause documents
  68 + without the field to come before documents with the field,
  69 + regardless of the requested sort order.
  70 + - If sortMissingLast="false" and sortMissingFirst="false" (the default),
  71 + then default lucene sorting will be used which places docs without the
  72 + field first in an ascending sort and last in a descending sort.
  73 + -->
  74 +
  75 + <!--
  76 + Default numeric field types. For faster range queries, consider the tint/tfloat/tlong/tdouble types.
  77 + -->
  78 + <fieldType name="int" class="solr.TrieIntField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
  79 + <fieldType name="float" class="solr.TrieFloatField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
  80 + <fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
  81 + <fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
  82 +
  83 + <!-- BBB support for existing schemas based on collective.solr -->
  84 + <fieldType name="integer" class="solr.TrieIntField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
  85 +
  86 + <!--
  87 + Numeric field types that index each value at various levels of precision
  88 + to accelerate range queries when the number of values between the range
  89 + endpoints is large. See the javadoc for NumericRangeQuery for internal
  90 + implementation details.
  91 +
  92 + Smaller precisionStep values (specified in bits) will lead to more tokens
  93 + indexed per value, slightly larger index size, and faster range queries.
  94 + A precisionStep of 0 disables indexing at different precision levels.
  95 + -->
  96 + <fieldType name="tint" class="solr.TrieIntField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
  97 + <fieldType name="tfloat" class="solr.TrieFloatField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
  98 + <fieldType name="tlong" class="solr.TrieLongField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
  99 + <fieldType name="tdouble" class="solr.TrieDoubleField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
  100 +
  101 + <!-- The format for this date field is of the form 1995-12-31T23:59:59Z, and
  102 + is a more restricted form of the canonical representation of dateTime
  103 + http://www.w3.org/TR/xmlschema-2/#dateTime
  104 + The trailing "Z" designates UTC time and is mandatory.
  105 + Optional fractional seconds are allowed: 1995-12-31T23:59:59.999Z
  106 + All other components are mandatory.
  107 +
  108 + Expressions can also be used to denote calculations that should be
  109 + performed relative to "NOW" to determine the value, ie...
  110 +
  111 + NOW/HOUR
  112 + ... Round to the start of the current hour
  113 + NOW-1DAY
  114 + ... Exactly 1 day prior to now
  115 + NOW/DAY+6MONTHS+3DAYS
  116 + ... 6 months and 3 days in the future from the start of
  117 + the current day
  118 +
  119 + Consult the DateField javadocs for more information.
  120 +
  121 + Note: For faster range queries, consider the tdate type
  122 + -->
  123 + <fieldType name="date" class="solr.TrieDateField" omitNorms="true" precisionStep="0" positionIncrementGap="0"/>
  124 +
  125 + <!-- A Trie based date field for faster date range queries and date faceting. -->
  126 + <fieldType name="tdate" class="solr.TrieDateField" omitNorms="true" precisionStep="6" positionIncrementGap="0"/>
  127 +
  128 + <!-- The "RandomSortField" is not used to store or search any
  129 + data. You can declare fields of this type it in your schema
  130 + to generate pseudo-random orderings of your docs for sorting
  131 + purposes. The ordering is generated based on the field name
  132 + and the version of the index, As long as the index version
  133 + remains unchanged, and the same field name is reused,
  134 + the ordering of the docs will be consistent.
  135 + If you want different psuedo-random orderings of documents,
  136 + for the same version of the index, use a dynamicField and
  137 + change the name
  138 + -->
  139 + <fieldType name="random" class="solr.RandomSortField" indexed="true" />
  140 +
  141 + <!-- solr.TextField allows the specification of custom text analyzers
  142 + specified as a tokenizer and a list of token filters. Different
  143 + analyzers may be specified for indexing and querying.
  144 +
  145 + The optional positionIncrementGap puts space between multiple fields of
  146 + this type on the same document, with the purpose of preventing false phrase
  147 + matching across fields.
  148 +
  149 + For more info on customizing your analyzer chain, please see
  150 + http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters
  151 + -->
  152 +
  153 + <!-- One can also specify an existing Analyzer class that has a
  154 + default constructor via the class attribute on the analyzer element
  155 + <fieldType name="text_greek" class="solr.TextField">
  156 + <analyzer class="org.apache.lucene.analysis.el.GreekAnalyzer"/>
  157 + </fieldType>
  158 + -->
  159 +
  160 + <fieldType name="text_ptbr" class="solr.TextField">
  161 + <analyzer class="org.apache.lucene.analysis.br.BrazilianAnalyzer">
  162 + <filter class="solr.LowerCaseFilterFactory"/>
  163 + <filter class="solr.BrazilianStemFilterFactory"/>
  164 + </analyzer>
  165 + </fieldType>
  166 +
  167 + <!-- A text field that only splits on whitespace for exact matching of words -->
  168 + <fieldType name="text_ws" class="solr.TextField" positionIncrementGap="100">
  169 + <analyzer>
  170 + <tokenizer class="solr.WhitespaceTokenizerFactory"/>
  171 + </analyzer>
  172 + </fieldType>
  173 +
  174 + <!-- A general text field that has reasonable, generic
  175 + cross-language defaults: it tokenizes with StandardTokenizer,
  176 + removes stop words from case-insensitive "stopwords.txt"
  177 + (empty by default), and down cases. At query time only, it
  178 + also applies synonyms. -->
  179 + <fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
  180 + <analyzer type="index">
  181 + <tokenizer class="solr.StandardTokenizerFactory"/>
  182 + <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
  183 + <!-- in this example, we will only use synonyms at query time
  184 + <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
  185 + -->
  186 + <filter class="solr.LowerCaseFilterFactory"/>
  187 + </analyzer>
  188 + <analyzer type="query">
  189 + <tokenizer class="solr.StandardTokenizerFactory"/>
  190 + <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
  191 + <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
  192 + <filter class="solr.LowerCaseFilterFactory"/>
  193 + </analyzer>
  194 + </fieldType>
  195 +
  196 + <!-- A text field with defaults appropriate for English: it
  197 + tokenizes with StandardTokenizer, removes English stop words
  198 + (stopwords_en.txt), down cases, protects words from protwords.txt, and
  199 + finally applies Porter's stemming. The query time analyzer
  200 + also applies synonyms from synonyms.txt. -->
  201 + <fieldType name="text_en" class="solr.TextField" positionIncrementGap="100">
  202 + <analyzer type="index">
  203 + <tokenizer class="solr.StandardTokenizerFactory"/>
  204 + <!-- in this example, we will only use synonyms at query time
  205 + <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
  206 + -->
  207 + <!-- Case insensitive stop word removal.
  208 + add enablePositionIncrements=true in both the index and query
  209 + analyzers to leave a 'gap' for more accurate phrase queries.
  210 + -->
  211 + <filter class="solr.StopFilterFactory"
  212 + ignoreCase="true"
  213 + words="stopwords_en.txt"
  214 + enablePositionIncrements="true"
  215 + />
  216 + <filter class="solr.LowerCaseFilterFactory"/>
  217 + <filter class="solr.EnglishPossessiveFilterFactory"/>
  218 + <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
  219 + <!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
  220 + <filter class="solr.EnglishMinimalStemFilterFactory"/>
  221 + -->
  222 + <filter class="solr.PorterStemFilterFactory"/>
  223 + </analyzer>
  224 + <analyzer type="query">
  225 + <tokenizer class="solr.StandardTokenizerFactory"/>
  226 + <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
  227 + <filter class="solr.StopFilterFactory"
  228 + ignoreCase="true"
  229 + words="stopwords_en.txt"
  230 + enablePositionIncrements="true"
  231 + />
  232 + <filter class="solr.LowerCaseFilterFactory"/>
  233 + <filter class="solr.EnglishPossessiveFilterFactory"/>
  234 + <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
  235 + <!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
  236 + <filter class="solr.EnglishMinimalStemFilterFactory"/>
  237 + -->
  238 + <filter class="solr.PorterStemFilterFactory"/>
  239 + </analyzer>
  240 + </fieldType>
  241 +
  242 + <!-- A text field with defaults appropriate for English, plus
  243 + aggressive word-splitting and autophrase features enabled.
  244 + This field is just like text_en, except it adds
  245 + WordDelimiterFilter to enable splitting and matching of
  246 + words on case-change, alpha numeric boundaries, and
  247 + non-alphanumeric chars. This means certain compound word
  248 + cases will work, for example query "wi fi" will match
  249 + document "WiFi" or "wi-fi". However, other cases will still
  250 + not match, for example if the query is "wifi" and the
  251 + document is "wi fi" or if the query is "wi-fi" and the
  252 + document is "wifi".
  253 + -->
  254 + <fieldType name="text_en_splitting" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="true">
  255 + <analyzer type="index">
  256 + <tokenizer class="solr.WhitespaceTokenizerFactory"/>
  257 + <!-- in this example, we will only use synonyms at query time
  258 + <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
  259 + -->
  260 + <!-- Case insensitive stop word removal.
  261 + add enablePositionIncrements=true in both the index and query
  262 + analyzers to leave a 'gap' for more accurate phrase queries.
  263 + -->
  264 + <filter class="solr.StopFilterFactory"
  265 + ignoreCase="true"
  266 + words="stopwords_en.txt"
  267 + enablePositionIncrements="true"
  268 + />
  269 + <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1"/>
  270 + <filter class="solr.LowerCaseFilterFactory"/>
  271 + <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
  272 + <filter class="solr.PorterStemFilterFactory"/>
  273 + </analyzer>
  274 + <analyzer type="query">
  275 + <tokenizer class="solr.WhitespaceTokenizerFactory"/>
  276 + <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
  277 + <filter class="solr.StopFilterFactory"
  278 + ignoreCase="true"
  279 + words="stopwords_en.txt"
  280 + enablePositionIncrements="true"
  281 + />
  282 + <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
  283 + <filter class="solr.LowerCaseFilterFactory"/>
  284 + <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
  285 + <filter class="solr.PorterStemFilterFactory"/>
  286 + </analyzer>
  287 + </fieldType>
  288 +
  289 + <!-- Less flexible matching, but less false matches. Probably not ideal for product names,
  290 + but may be good for SKUs. Can insert dashes in the wrong place and still match. -->
  291 + <fieldType name="text_en_splitting_tight" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="true">
  292 + <analyzer>
  293 + <tokenizer class="solr.WhitespaceTokenizerFactory"/>
  294 + <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="false"/>
  295 + <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords_en.txt"/>
  296 + <filter class="solr.WordDelimiterFilterFactory" generateWordParts="0" generateNumberParts="0" catenateWords="1" catenateNumbers="1" catenateAll="0"/>
  297 + <filter class="solr.LowerCaseFilterFactory"/>
  298 + <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
  299 + <filter class="solr.EnglishMinimalStemFilterFactory"/>
  300 + <!-- this filter can remove any duplicate tokens that appear at the same position - sometimes
  301 + possible with WordDelimiterFilter in conjuncton with stemming. -->
  302 + <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
  303 + </analyzer>
  304 + </fieldType>
  305 +
  306 + <!-- Just like text_general except it reverses the characters of
  307 + each token, to enable more efficient leading wildcard queries. -->
  308 + <fieldType name="text_general_rev" class="solr.TextField" positionIncrementGap="100">
  309 + <analyzer type="index">
  310 + <tokenizer class="solr.StandardTokenizerFactory"/>
  311 + <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
  312 + <filter class="solr.LowerCaseFilterFactory"/>
  313 + <filter class="solr.ReversedWildcardFilterFactory" withOriginal="true"
  314 + maxPosAsterisk="3" maxPosQuestion="2" maxFractionAsterisk="0.33"/>
  315 + </analyzer>
  316 + <analyzer type="query">
  317 + <tokenizer class="solr.StandardTokenizerFactory"/>
  318 + <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
  319 + <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
  320 + <filter class="solr.LowerCaseFilterFactory"/>
  321 + </analyzer>
  322 + </fieldType>
  323 +
  324 + <!-- charFilter + WhitespaceTokenizer -->
  325 + <!--
  326 + <fieldType name="text_char_norm" class="solr.TextField" positionIncrementGap="100" >
  327 + <analyzer>
  328 + <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/>
  329 + <tokenizer class="solr.WhitespaceTokenizerFactory"/>
  330 + </analyzer>
  331 + </fieldType>
  332 + -->
  333 +
  334 + <!-- This is an example of using the KeywordTokenizer along
  335 + With various TokenFilterFactories to produce a sortable field
  336 + that does not include some properties of the source text
  337 + -->
  338 + <fieldType name="alphaOnlySort" class="solr.TextField" sortMissingLast="true" omitNorms="true">
  339 + <analyzer>
  340 + <!-- KeywordTokenizer does no actual tokenizing, so the entire
  341 + input string is preserved as a single token
  342 + -->
  343 + <tokenizer class="solr.KeywordTokenizerFactory"/>
  344 + <!-- The LowerCase TokenFilter does what you expect, which can be
  345 + when you want your sorting to be case insensitive
  346 + -->
  347 + <filter class="solr.LowerCaseFilterFactory" />
  348 + <!-- The TrimFilter removes any leading or trailing whitespace -->
  349 + <filter class="solr.TrimFilterFactory" />
  350 + <!-- The PatternReplaceFilter gives you the flexibility to use
  351 + Java Regular expression to replace any sequence of characters
  352 + matching a pattern with an arbitrary replacement string,
  353 + which may include back references to portions of the original
  354 + string matched by the pattern.
  355 +
  356 + See the Java Regular Expression documentation for more
  357 + information on pattern and replacement string syntax.
  358 +
  359 + http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/package-summary.html
  360 + -->
  361 + <filter class="solr.PatternReplaceFilterFactory"
  362 + pattern="([^a-z])" replacement="" replace="all"
  363 + />
  364 + </analyzer>
  365 + </fieldType>
  366 +
  367 + <fieldtype name="phonetic" stored="false" indexed="true" class="solr.TextField" >
  368 + <analyzer>
  369 + <tokenizer class="solr.StandardTokenizerFactory"/>
  370 + <filter class="solr.DoubleMetaphoneFilterFactory" inject="false"/>
  371 + </analyzer>
  372 + </fieldtype>
  373 +
  374 + <fieldtype name="payloads" stored="false" indexed="true" class="solr.TextField" >
  375 + <analyzer>
  376 + <tokenizer class="solr.WhitespaceTokenizerFactory"/>
  377 + <!--
  378 + The DelimitedPayloadTokenFilter can put payloads on tokens... for example,
  379 + a token of "foo|1.4" would be indexed as "foo" with a payload of 1.4f
  380 + Attributes of the DelimitedPayloadTokenFilterFactory :
  381 + "delimiter" - a one character delimiter. Default is | (pipe)
  382 + "encoder" - how to encode the following value into a playload
  383 + float -> org.apache.lucene.analysis.payloads.FloatEncoder,
  384 + integer -> o.a.l.a.p.IntegerEncoder
  385 + identity -> o.a.l.a.p.IdentityEncoder
  386 + Fully Qualified class name implementing PayloadEncoder, Encoder must have a no arg constructor.
  387 + -->
  388 + <filter class="solr.DelimitedPayloadTokenFilterFactory" encoder="float"/>
  389 + </analyzer>
  390 + </fieldtype>
  391 +
  392 + <!-- lowercases the entire field value, keeping it as a single token. -->
  393 + <fieldType name="lowercase" class="solr.TextField" positionIncrementGap="100">
  394 + <analyzer>
  395 + <tokenizer class="solr.KeywordTokenizerFactory"/>
  396 + <filter class="solr.LowerCaseFilterFactory" />
  397 + </analyzer>
  398 + </fieldType>
  399 +
  400 + <fieldType name="text_path" class="solr.TextField" positionIncrementGap="100">
  401 + <analyzer>
  402 + <tokenizer class="solr.PathHierarchyTokenizerFactory"/>
  403 + </analyzer>
  404 + </fieldType>
  405 +
  406 + <!-- since fields of this type are by default not stored or indexed,
  407 + any data added to them will be ignored outright. -->
  408 + <fieldtype name="ignored" stored="false" indexed="false" multiValued="true" class="solr.StrField" />
  409 +
  410 + <!-- This point type indexes the coordinates as separate fields (subFields)
  411 + If subFieldType is defined, it references a type, and a dynamic field
  412 + definition is created matching *___<typename>. Alternately, if
  413 + subFieldSuffix is defined, that is used to create the subFields.
  414 + Example: if subFieldType="double", then the coordinates would be
  415 + indexed in fields myloc_0___double,myloc_1___double.
  416 + Example: if subFieldSuffix="_d" then the coordinates would be indexed
  417 + in fields myloc_0_d,myloc_1_d
  418 + The subFields are an implementation detail of the fieldType, and end
  419 + users normally should not need to know about them.
  420 + -->
  421 + <fieldType name="point" class="solr.PointType" dimension="2" subFieldSuffix="_d"/>
  422 +
  423 + <!-- A specialized field for geospatial search. If indexed, this fieldType must not be multivalued. -->
  424 + <fieldType name="location" class="solr.LatLonType" subFieldSuffix="_coordinate"/>
  425 +
  426 + <!--
  427 + A Geohash is a compact representation of a latitude longitude pair in a single field.
  428 + See http://wiki.apache.org/solr/SpatialSearch
  429 + -->
  430 + <fieldtype name="geohash" class="solr.GeoHashField"/>
  431 +
  432 +
  433 + </types>
  434 +
  435 +
  436 + <fields>
  437 + <!-- Valid attributes for fields:
  438 + name: mandatory - the name for the field
  439 + type: mandatory - the name of a previously defined type from the
  440 + <types> section
  441 + indexed: true if this field should be indexed (searchable or sortable)
  442 + stored: true if this field should be retrievable
  443 + multiValued: true if this field may contain multiple values per document
  444 + omitNorms: (expert) set to true to omit the norms associated with
  445 + this field (this disables length normalization and index-time
  446 + boosting for the field, and saves some memory). Only full-text
  447 + fields or fields that need an index-time boost need norms.
  448 + termVectors: [false] set to true to store the term vector for a
  449 + given field.
  450 + When using MoreLikeThis, fields used for similarity should be
  451 + stored for best performance.
  452 + termPositions: Store position information with the term vector.
  453 + This will increase storage costs.
  454 + termOffsets: Store offset information with the term vector. This
  455 + will increase storage costs.
  456 + default: a value that should be used if no value is specified
  457 + when adding a document.
  458 + -->
  459 +
  460 + <!-- Base fields (all should be indexed and stored)-->
  461 + <field name="UID" type="string" indexed="true"
  462 + stored="true" required="true" multiValued="false"/>
  463 + <field name="getId" type="string" indexed="false"
  464 + stored="true" required="true" multiValued="false"/>
  465 + <field name="Type" type="string" indexed="true"
  466 + stored="true" required="true" multiValued="false"/>
  467 + <field name="Title" type="text_ptbr" indexed="true"
  468 + stored="true" required="false" multiValued="false"/>
  469 + <field name="Description" type="text_ptbr" indexed="true"
  470 + stored="true" required="false" multiValued="false"/>
  471 + <field name="Creator" type="string" indexed="true"
  472 + stored="true" required="false" multiValued="false"/>
  473 + <field name="created" type="date" indexed="true"
  474 + stored="true" required="false" multiValued="false"/>
  475 + <field name="modified" type="date" indexed="true"
  476 + stored="true" required="false" multiValued="false"/>
  477 + <field name="mailinglist" type="string" indexed="true"
  478 + stored="true" required="false" multiValued="false"/>
  479 + <field name="creator_real_name" type="string" indexed="true"
  480 + stored="true" required="false" multiValued="false"/>
  481 + <field name="creator_profile_uri" type="string" indexed="true"
  482 + stored="true" required="false" multiValued="false"/>
  483 +
  484 + <!-- All next fields shoult NOT be stored -->
  485 + <field name="name" type="string" indexed="true"
  486 + stored="false" required="false" multiValued="false"/>
  487 + <field name="comment" type="text_ptbr" indexed="true"
  488 + stored="false" required="false" multiValued="true" />
  489 + <field name="content" type="text_ptbr" indexed="true"
  490 + stored="false" required="false" multiValued="false"/>
  491 + <field name="keyword" type="text_ptbr" indexed="true"
  492 + stored="false" required="false" multiValued="true"/>
  493 + <field name="milestone" type="string" indexed="true"
  494 + stored="false" required="false" multiValued="false"/>
  495 + <field name="priority" type="string" indexed="true"
  496 + stored="false" required="false" multiValued="false"/>
  497 + <field name="component" type="string" indexed="true"
  498 + stored="false" required="false" multiValued="false"/>
  499 + <field name="version" type="string" indexed="true"
  500 + stored="false" required="false" multiValued="false"/>
  501 + <field name="severity" type="string" indexed="true"
  502 + stored="false" required="false" multiValued="false"/>
  503 + <field name="reporter" type="string" indexed="true"
  504 + stored="false" required="false" multiValued="false"/>
  505 + <field name="owner" type="string" indexed="true"
  506 + stored="false" required="false" multiValued="false"/>
  507 + <field name="status" type="string" indexed="true"
  508 + stored="false" required="false" multiValued="false"/>
  509 + <field name="revision" type="int" indexed="true"
  510 + stored="false" required="false" multiValued="false"/>
  511 + <field name="subject" type="text_ptbr" indexed="true"
  512 + stored="false" required="false" multiValued="false"/>
  513 + <field name="path_string" type="string" indexed="false"
  514 + stored="true" required="false" multiValued="false"/>
  515 +
  516 + <!-- Plone only fields -->
  517 + <field name="review_state" type="string" indexed="true"
  518 + stored="true" required="false" multiValued="false"/>
  519 + <field name="effective" type="date" indexed="true"
  520 + stored="true" required="false" multiValued="false"/>
  521 + <field name="expires" type="date" indexed="true"
  522 + stored="true" required="false" multiValued="false"/>
  523 + <field name="getIcon" type="string" indexed="false"
  524 + stored="true" required="false" multiValued="false"/>
  525 + <field name="getRemoteUrl" type="string" indexed="false"
  526 + stored="true" required="false" multiValued="false"/>
  527 + <field name="exclude_from_nav" type="boolean" indexed="false"
  528 + stored="true" required="false" multiValued="false"/>
  529 + <field name="portal_type" type="string" indexed="true"
  530 + stored="true" required="false" multiValued="false"/>
  531 + <field name="is_folderish" type="boolean" indexed="true"
  532 + stored="true" required="false" multiValued="false"/>
  533 + <field name="Language" type="string" indexed="true"
  534 + stored="true" required="false" multiValued="false"/>
  535 + <field name="Date" type="date" indexed="true"
  536 + stored="true" required="false" multiValued="false"/>
  537 + <field name="allowedRolesAndUsers" type="string" indexed="true"
  538 + stored="false" required="false" multiValued="true"/>
  539 + <field name="object_provides" type="string" indexed="true"
  540 + stored="false" required="false" multiValued="true"/>
  541 + <field name="path_depth" type="integer" indexed="true"
  542 + stored="false" required="false" multiValued="false"/>
  543 + <field name="path_parents" type="string" indexed="true"
  544 + stored="false" required="false" multiValued="true"/>
  545 + <field name="SearchableText" type="text_general" indexed="true"
  546 + stored="false" required="false" multiValued="false"/>
  547 + <field name="searchwords" type="string" indexed="true"
  548 + stored="false" required="false" multiValued="true"/>
  549 + <field name="showinsearch" type="boolean" indexed="true"
  550 + stored="false" required="false" multiValued="false"/>
  551 +
  552 + <dynamicField name="tika_*" type="ignored" />
  553 +
  554 + <field name="collaborator" type="string" indexed="true"
  555 + stored="false" required="false" multiValued="true" />
  556 +
  557 + <!-- catchall field, containing all other searchable text fields (implemented
  558 + via copyField further on in this schema -->
  559 + <field name="default" type="text_general" indexed="true"
  560 + stored="false" required="false" multiValued="true"/>
  561 + </fields>
  562 +
  563 + <!-- Field to use to determine and enforce document uniqueness.
  564 + Unless this field is marked with required="false", it will be a required field
  565 + -->
  566 + <uniqueKey>UID</uniqueKey>
  567 +
  568 + <!-- field for the QueryParser to use when an explicit fieldname is absent -->
  569 + <defaultSearchField>default</defaultSearchField>
  570 +
  571 + <!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
  572 + <solrQueryParser defaultOperator="OR"/>
  573 +
  574 + <!-- copyField commands copy one field to another at the time a document
  575 + is added to the index. It's used either to index the same field differently,
  576 + or to add multiple fields to the same field for easier/faster searching. -->
  577 + <copyField source="Description" dest="default"/>
  578 + <copyField source="SearchableText" dest="default"/>
  579 + <copyField source="Title" dest="default"/>
  580 + <copyField source="content" dest="default" />
  581 + <copyField source="Creator" dest="default" />
  582 + <copyField source="collaborator" dest="default" />
  583 + <copyField source="comment" dest="default" />
  584 + <copyField source="keyword" dest="default" />
  585 +
  586 + <!-- Above, multiple source fields are copied to the [text] field.
  587 + Another way to map multiple source fields to the same
  588 + destination field is to use the dynamic field syntax.
  589 + copyField also supports a maxChars to copy setting. -->
  590 +
  591 + <!-- <copyField source="*_t" dest="text" maxChars="3000"/> -->
  592 +
  593 + <!-- copy name to alphaNameSort, a field designed for sorting by name -->
  594 + <!-- <copyField source="name" dest="alphaNameSort"/> -->
  595 +
  596 + <!-- Similarity is the scoring routine for each document vs. a query.
  597 + A custom similarity may be specified here, but the default is fine
  598 + for most applications. -->
  599 + <!-- <similarity class="org.apache.lucene.search.DefaultSimilarity"/> -->
  600 + <!-- ... OR ...
  601 + Specify a SimilarityFactory class name implementation
  602 + allowing parameters to be used.
  603 + -->
  604 + <!--
  605 + <similarity class="com.example.solr.CustomSimilarityFactory">
  606 + <str name="paramkey">param value</str>
  607 + </similarity>
  608 + -->
  609 +
  610 +
  611 +</schema>
  612 +
... ...
solr-conf/solr-tomcat.xml 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +<!--
  2 + Context configuration file for the Solr Web App
  3 +-->
  4 +
  5 +<Context path="/solr" docBase="/usr/local/share/solr/apache-solr-3.3.0.war"
  6 + debug="0" privileged="true" allowLinking="true" crossContext="true">
  7 + <!-- make symlinks work in Tomcat -->
  8 + <Resources className="org.apache.naming.resources.FileDirContext" allowLinking="true" />
  9 +
  10 + <Environment name="solr/home" type="java.lang.String" value="/usr/local/share/solr" override="true" />
  11 + </Context>
  12 +
... ...
solr-conf/solrconfig.xml 0 → 100644
... ... @@ -0,0 +1,1535 @@
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +<!--
  3 + Licensed to the Apache Software Foundation (ASF) under one or more
  4 + contributor license agreements. See the NOTICE file distributed with
  5 + this work for additional information regarding copyright ownership.
  6 + The ASF licenses this file to You under the Apache License, Version 2.0
  7 + (the "License"); you may not use this file except in compliance with
  8 + the License. You may obtain a copy of the License at
  9 +
  10 + http://www.apache.org/licenses/LICENSE-2.0
  11 +
  12 + Unless required by applicable law or agreed to in writing, software
  13 + distributed under the License is distributed on an "AS IS" BASIS,
  14 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15 + See the License for the specific language governing permissions and
  16 + limitations under the License.
  17 +-->
  18 +
  19 +<!--
  20 + For more details about configurations options that may appear in
  21 + this file, see http://wiki.apache.org/solr/SolrConfigXml.
  22 +-->
  23 +<config>
  24 + <!-- In all configuration below, a prefix of "solr." for class names
  25 + is an alias that causes solr to search appropriate packages,
  26 + including org.apache.solr.(search|update|request|core|analysis)
  27 +
  28 + You may also specify a fully qualified Java classname if you
  29 + have your own custom plugins.
  30 + -->
  31 +
  32 + <!-- Set this to 'false' if you want solr to continue working after
  33 + it has encountered an severe configuration error. In a
  34 + production environment, you may want solr to keep working even
  35 + if one handler is mis-configured.
  36 +
  37 + You may also set this to false using by setting the system
  38 + property:
  39 +
  40 + -Dsolr.abortOnConfigurationError=false
  41 + -->
  42 + <abortOnConfigurationError>${solr.abortOnConfigurationError:true}</abortOnConfigurationError>
  43 +
  44 + <!-- Controls what version of Lucene various components of Solr
  45 + adhere to. Generally, you want to use the latest version to
  46 + get all bug fixes and improvements. It is highly recommended
  47 + that you fully re-index after changing this setting as it can
  48 + affect both how text is indexed and queried.
  49 + -->
  50 + <luceneMatchVersion>LUCENE_33</luceneMatchVersion>
  51 +
  52 + <!-- lib directives can be used to instruct Solr to load an Jars
  53 + identified and use them to resolve any "plugins" specified in
  54 + your solrconfig.xml or schema.xml (ie: Analyzers, Request
  55 + Handlers, etc...).
  56 +
  57 + All directories and paths are resolved relative to the
  58 + instanceDir.
  59 +
  60 + If a "./lib" directory exists in your instanceDir, all files
  61 + found in it are included as if you had used the following
  62 + syntax...
  63 +
  64 + <lib dir="./lib" />
  65 + -->
  66 + <!-- A dir option by itself adds any files found in the directory to
  67 + the classpath, this is useful for including all jars in a
  68 + directory.
  69 + -->
  70 + <lib dir="../../contrib/extraction/lib" />
  71 + <!-- When a regex is specified in addition to a directory, only the
  72 + files in that directory which completely match the regex
  73 + (anchored on both ends) will be included.
  74 + -->
  75 + <lib dir="../../dist/" regex="apache-solr-cell-\d.*\.jar" />
  76 + <lib dir="../../dist/" regex="apache-solr-clustering-\d.*\.jar" />
  77 + <lib dir="../../dist/" regex="apache-solr-dataimporthandler-\d.*\.jar" />
  78 +
  79 + <!-- If a dir option (with or without a regex) is used and nothing
  80 + is found that matches, it will be ignored
  81 + -->
  82 + <lib dir="../../contrib/clustering/lib/" />
  83 + <lib dir="/total/crap/dir/ignored" />
  84 + <!-- an exact path can be used to specify a specific file. This
  85 + will cause a serious error to be logged if it can't be loaded.
  86 + -->
  87 + <!--
  88 + <lib path="../a-jar-that-does-not-exist.jar" />
  89 + -->
  90 +
  91 + <!-- Data Directory
  92 +
  93 + Used to specify an alternate directory to hold all index data
  94 + other than the default ./data under the Solr home. If
  95 + replication is in use, this should match the replication
  96 + configuration.
  97 + -->
  98 + <dataDir>${solr.data.dir:/var/local/lib/solr/data}</dataDir>
  99 +
  100 +
  101 + <!-- The DirectoryFactory to use for indexes.
  102 +
  103 + solr.StandardDirectoryFactory, the default, is filesystem
  104 + based. solr.RAMDirectoryFactory is memory based, not
  105 + persistent, and doesn't work with replication.
  106 + -->
  107 + <directoryFactory name="DirectoryFactory"
  108 + class="${solr.directoryFactory:solr.StandardDirectoryFactory}"/>
  109 +
  110 +
  111 + <!-- Index Defaults
  112 +
  113 + Values here affect all index writers and act as a default
  114 + unless overridden.
  115 +
  116 + WARNING: See also the <mainIndex> section below for parameters
  117 + that overfor Solr's main Lucene index.
  118 + -->
  119 + <indexDefaults>
  120 +
  121 + <useCompoundFile>false</useCompoundFile>
  122 +
  123 + <mergeFactor>10</mergeFactor>
  124 + <!-- Sets the amount of RAM that may be used by Lucene indexing
  125 + for buffering added documents and deletions before they are
  126 + flushed to the Directory. -->
  127 + <ramBufferSizeMB>32</ramBufferSizeMB>
  128 + <!-- If both ramBufferSizeMB and maxBufferedDocs is set, then
  129 + Lucene will flush based on whichever limit is hit first.
  130 + -->
  131 + <!-- <maxBufferedDocs>1000</maxBufferedDocs> -->
  132 +
  133 + <maxFieldLength>10000</maxFieldLength>
  134 + <writeLockTimeout>1000</writeLockTimeout>
  135 + <commitLockTimeout>10000</commitLockTimeout>
  136 +
  137 + <!-- Expert: Merge Policy
  138 +
  139 + The Merge Policy in Lucene controls how merging is handled by
  140 + Lucene. The default in Solr 3.3 is TieredMergePolicy.
  141 +
  142 + The default in 2.3 was the LogByteSizeMergePolicy,
  143 + previous versions used LogDocMergePolicy.
  144 +
  145 + LogByteSizeMergePolicy chooses segments to merge based on
  146 + their size. The Lucene 2.2 default, LogDocMergePolicy chose
  147 + when to merge based on number of documents
  148 +
  149 + Other implementations of MergePolicy must have a no-argument
  150 + constructor
  151 + -->
  152 + <!--
  153 + <mergePolicy class="org.apache.lucene.index.TieredMergePolicy"/>
  154 + -->
  155 +
  156 + <!-- Expert: Merge Scheduler
  157 +
  158 + The Merge Scheduler in Lucene controls how merges are
  159 + performed. The ConcurrentMergeScheduler (Lucene 2.3 default)
  160 + can perform merges in the background using separate threads.
  161 + The SerialMergeScheduler (Lucene 2.2 default) does not.
  162 + -->
  163 + <!--
  164 + <mergeScheduler class="org.apache.lucene.index.ConcurrentMergeScheduler"/>
  165 + -->
  166 +
  167 + <!-- LockFactory
  168 +
  169 + This option specifies which Lucene LockFactory implementation
  170 + to use.
  171 +
  172 + single = SingleInstanceLockFactory - suggested for a
  173 + read-only index or when there is no possibility of
  174 + another process trying to modify the index.
  175 + native = NativeFSLockFactory - uses OS native file locking.
  176 + Do not use when multiple solr webapps in the same
  177 + JVM are attempting to share a single index.
  178 + simple = SimpleFSLockFactory - uses a plain file for locking
  179 +
  180 + (For backwards compatibility with Solr 1.2, 'simple' is the
  181 + default if not specified.)
  182 +
  183 + More details on the nuances of each LockFactory...
  184 + http://wiki.apache.org/lucene-java/AvailableLockFactories
  185 + -->
  186 + <lockType>native</lockType>
  187 +
  188 + <!-- Expert: Controls how often Lucene loads terms into memory
  189 + Default is 128 and is likely good for most everyone.
  190 + -->
  191 + <!-- <termIndexInterval>256</termIndexInterval> -->
  192 + </indexDefaults>
  193 +
  194 + <!-- Main Index
  195 +
  196 + Values here override the values in the <indexDefaults> section
  197 + for the main on disk index.
  198 + -->
  199 + <mainIndex>
  200 +
  201 + <useCompoundFile>false</useCompoundFile>
  202 + <ramBufferSizeMB>32</ramBufferSizeMB>
  203 + <mergeFactor>10</mergeFactor>
  204 +
  205 + <!-- Unlock On Startup
  206 +
  207 + If true, unlock any held write or commit locks on startup.
  208 + This defeats the locking mechanism that allows multiple
  209 + processes to safely access a lucene index, and should be used
  210 + with care.
  211 +
  212 + This is not needed if lock type is 'none' or 'single'
  213 + -->
  214 + <unlockOnStartup>false</unlockOnStartup>
  215 +
  216 + <!-- If true, IndexReaders will be reopened (often more efficient)
  217 + instead of closed and then opened.
  218 + -->
  219 + <reopenReaders>true</reopenReaders>
  220 +
  221 + <!-- Commit Deletion Policy
  222 +
  223 + Custom deletion policies can specified here. The class must
  224 + implement org.apache.lucene.index.IndexDeletionPolicy.
  225 +
  226 + http://lucene.apache.org/java/2_9_1/api/all/org/apache/lucene/index/IndexDeletionPolicy.html
  227 +
  228 + The standard Solr IndexDeletionPolicy implementation supports
  229 + deleting index commit points on number of commits, age of
  230 + commit point and optimized status.
  231 +
  232 + The latest commit point should always be preserved regardless
  233 + of the criteria.
  234 + -->
  235 + <deletionPolicy class="solr.SolrDeletionPolicy">
  236 + <!-- The number of commit points to be kept -->
  237 + <str name="maxCommitsToKeep">1</str>
  238 + <!-- The number of optimized commit points to be kept -->
  239 + <str name="maxOptimizedCommitsToKeep">0</str>
  240 + <!--
  241 + Delete all commit points once they have reached the given age.
  242 + Supports DateMathParser syntax e.g.
  243 + -->
  244 + <!--
  245 + <str name="maxCommitAge">30MINUTES</str>
  246 + <str name="maxCommitAge">1DAY</str>
  247 + -->
  248 + </deletionPolicy>
  249 +
  250 + <!-- Lucene Infostream
  251 +
  252 + To aid in advanced debugging, Lucene provides an "InfoStream"
  253 + of detailed information when indexing.
  254 +
  255 + Setting The value to true will instruct the underlying Lucene
  256 + IndexWriter to write its debugging info the specified file
  257 + -->
  258 + <infoStream file="INFOSTREAM.txt">false</infoStream>
  259 +
  260 + </mainIndex>
  261 +
  262 + <!-- JMX
  263 +
  264 + This example enables JMX if and only if an existing MBeanServer
  265 + is found, use this if you want to configure JMX through JVM
  266 + parameters. Remove this to disable exposing Solr configuration
  267 + and statistics to JMX.
  268 +
  269 + For more details see http://wiki.apache.org/solr/SolrJmx
  270 + -->
  271 + <jmx />
  272 + <!-- If you want to connect to a particular server, specify the
  273 + agentId
  274 + -->
  275 + <!-- <jmx agentId="myAgent" /> -->
  276 + <!-- If you want to start a new MBeanServer, specify the serviceUrl -->
  277 + <!-- <jmx serviceUrl="service:jmx:rmi:///jndi/rmi://localhost:9999/solr"/>
  278 + -->
  279 +
  280 + <!-- The default high-performance update handler -->
  281 + <updateHandler class="solr.DirectUpdateHandler2">
  282 +
  283 + <!-- AutoCommit
  284 +
  285 + Perform a <commit/> automatically under certain conditions.
  286 + Instead of enabling autoCommit, consider using "commitWithin"
  287 + when adding documents.
  288 +
  289 + http://wiki.apache.org/solr/UpdateXmlMessages
  290 +
  291 + maxDocs - Maximum number of documents to add since the last
  292 + commit before automatically triggering a new commit.
  293 +
  294 + maxTime - Maximum amount of time that is allowed to pass
  295 + since a document was added before automaticly
  296 + triggering a new commit.
  297 + -->
  298 + <!--
  299 + <autoCommit>
  300 + <maxDocs>10000</maxDocs>
  301 + <maxTime>1000</maxTime>
  302 + </autoCommit>
  303 + -->
  304 +
  305 + <!-- Update Related Event Listeners
  306 +
  307 + Various IndexWriter related events can trigger Listeners to
  308 + take actions.
  309 +
  310 + postCommit - fired after every commit or optimize command
  311 + postOptimize - fired after every optimize command
  312 + -->
  313 + <!-- The RunExecutableListener executes an external command from a
  314 + hook such as postCommit or postOptimize.
  315 +
  316 + exe - the name of the executable to run
  317 + dir - dir to use as the current working directory. (default=".")
  318 + wait - the calling thread waits until the executable returns.
  319 + (default="true")
  320 + args - the arguments to pass to the program. (default is none)
  321 + env - environment variables to set. (default is none)
  322 + -->
  323 + <!-- This example shows how RunExecutableListener could be used
  324 + with the script based replication...
  325 + http://wiki.apache.org/solr/CollectionDistribution
  326 + -->
  327 + <!--
  328 + <listener event="postCommit" class="solr.RunExecutableListener">
  329 + <str name="exe">solr/bin/snapshooter</str>
  330 + <str name="dir">.</str>
  331 + <bool name="wait">true</bool>
  332 + <arr name="args"> <str>arg1</str> <str>arg2</str> </arr>
  333 + <arr name="env"> <str>MYVAR=val1</str> </arr>
  334 + </listener>
  335 + -->
  336 + </updateHandler>
  337 +
  338 + <!-- IndexReaderFactory
  339 +
  340 + Use the following format to specify a custom IndexReaderFactory,
  341 + which allows for alternate IndexReader implementations.
  342 +
  343 + ** Experimental Feature **
  344 +
  345 + Please note - Using a custom IndexReaderFactory may prevent
  346 + certain other features from working. The API to
  347 + IndexReaderFactory may change without warning or may even be
  348 + removed from future releases if the problems cannot be
  349 + resolved.
  350 +
  351 +
  352 + ** Features that may not work with custom IndexReaderFactory **
  353 +
  354 + The ReplicationHandler assumes a disk-resident index. Using a
  355 + custom IndexReader implementation may cause incompatibility
  356 + with ReplicationHandler and may cause replication to not work
  357 + correctly. See SOLR-1366 for details.
  358 +
  359 + -->
  360 + <!--
  361 + <indexReaderFactory name="IndexReaderFactory" class="package.class">
  362 + <str name="someArg">Some Value</str>
  363 + </indexReaderFactory >
  364 + -->
  365 + <!-- By explicitly declaring the Factory, the termIndexDivisor can
  366 + be specified.
  367 + -->
  368 + <!--
  369 + <indexReaderFactory name="IndexReaderFactory"
  370 + class="solr.StandardIndexReaderFactory">
  371 + <int name="setTermIndexDivisor">12</int>
  372 + </indexReaderFactory >
  373 + -->
  374 +
  375 +
  376 + <query>
  377 + <!-- Max Boolean Clauses
  378 +
  379 + Maximum number of clauses in each BooleanQuery, an exception
  380 + is thrown if exceeded.
  381 +
  382 + ** WARNING **
  383 +
  384 + This option actually modifies a global Lucene property that
  385 + will affect all SolrCores. If multiple solrconfig.xml files
  386 + disagree on this property, the value at any given moment will
  387 + be based on the last SolrCore to be initialized.
  388 +
  389 + -->
  390 + <maxBooleanClauses>1024</maxBooleanClauses>
  391 +
  392 +
  393 + <!-- Solr Internal Query Caches
  394 +
  395 + There are two implementations of cache available for Solr,
  396 + LRUCache, based on a synchronized LinkedHashMap, and
  397 + FastLRUCache, based on a ConcurrentHashMap.
  398 +
  399 + FastLRUCache has faster gets and slower puts in single
  400 + threaded operation and thus is generally faster than LRUCache
  401 + when the hit ratio of the cache is high (> 75%), and may be
  402 + faster under other scenarios on multi-cpu systems.
  403 + -->
  404 +
  405 + <!-- Filter Cache
  406 +
  407 + Cache used by SolrIndexSearcher for filters (DocSets),
  408 + unordered sets of *all* documents that match a query. When a
  409 + new searcher is opened, its caches may be prepopulated or
  410 + "autowarmed" using data from caches in the old searcher.
  411 + autowarmCount is the number of items to prepopulate. For
  412 + LRUCache, the autowarmed items will be the most recently
  413 + accessed items.
  414 +
  415 + Parameters:
  416 + class - the SolrCache implementation LRUCache or
  417 + (LRUCache or FastLRUCache)
  418 + size - the maximum number of entries in the cache
  419 + initialSize - the initial capacity (number of entries) of
  420 + the cache. (see java.util.HashMap)
  421 + autowarmCount - the number of entries to prepopulate from
  422 + and old cache.
  423 + -->
  424 + <filterCache class="solr.FastLRUCache"
  425 + size="512"
  426 + initialSize="512"
  427 + autowarmCount="0"/>
  428 +
  429 + <!-- Query Result Cache
  430 +
  431 + Caches results of searches - ordered lists of document ids
  432 + (DocList) based on a query, a sort, and the range of documents requested.
  433 + -->
  434 + <queryResultCache class="solr.LRUCache"
  435 + size="512"
  436 + initialSize="512"
  437 + autowarmCount="0"/>
  438 +
  439 + <!-- Document Cache
  440 +
  441 + Caches Lucene Document objects (the stored fields for each
  442 + document). Since Lucene internal document ids are transient,
  443 + this cache will not be autowarmed.
  444 + -->
  445 + <documentCache class="solr.LRUCache"
  446 + size="512"
  447 + initialSize="512"
  448 + autowarmCount="0"/>
  449 +
  450 + <!-- Field Value Cache
  451 +
  452 + Cache used to hold field values that are quickly accessible
  453 + by document id. The fieldValueCache is created by default
  454 + even if not configured here.
  455 + -->
  456 + <!--
  457 + <fieldValueCache class="solr.FastLRUCache"
  458 + size="512"
  459 + autowarmCount="128"
  460 + showItems="32" />
  461 + -->
  462 +
  463 + <!-- Custom Cache
  464 +
  465 + Example of a generic cache. These caches may be accessed by
  466 + name through SolrIndexSearcher.getCache(),cacheLookup(), and
  467 + cacheInsert(). The purpose is to enable easy caching of
  468 + user/application level data. The regenerator argument should
  469 + be specified as an implementation of solr.CacheRegenerator
  470 + if autowarming is desired.
  471 + -->
  472 + <!--
  473 + <cache name="myUserCache"
  474 + class="solr.LRUCache"
  475 + size="4096"
  476 + initialSize="1024"
  477 + autowarmCount="1024"
  478 + regenerator="com.mycompany.MyRegenerator"
  479 + />
  480 + -->
  481 +
  482 +
  483 + <!-- Lazy Field Loading
  484 +
  485 + If true, stored fields that are not requested will be loaded
  486 + lazily. This can result in a significant speed improvement
  487 + if the usual case is to not load all stored fields,
  488 + especially if the skipped fields are large compressed text
  489 + fields.
  490 + -->
  491 + <enableLazyFieldLoading>true</enableLazyFieldLoading>
  492 +
  493 + <!-- Use Filter For Sorted Query
  494 +
  495 + A possible optimization that attempts to use a filter to
  496 + satisfy a search. If the requested sort does not include
  497 + score, then the filterCache will be checked for a filter
  498 + matching the query. If found, the filter will be used as the
  499 + source of document ids, and then the sort will be applied to
  500 + that.
  501 +
  502 + For most situations, this will not be useful unless you
  503 + frequently get the same search repeatedly with different sort
  504 + options, and none of them ever use "score"
  505 + -->
  506 + <!--
  507 + <useFilterForSortedQuery>true</useFilterForSortedQuery>
  508 + -->
  509 +
  510 + <!-- Result Window Size
  511 +
  512 + An optimization for use with the queryResultCache. When a search
  513 + is requested, a superset of the requested number of document ids
  514 + are collected. For example, if a search for a particular query
  515 + requests matching documents 10 through 19, and queryWindowSize is 50,
  516 + then documents 0 through 49 will be collected and cached. Any further
  517 + requests in that range can be satisfied via the cache.
  518 + -->
  519 + <queryResultWindowSize>20</queryResultWindowSize>
  520 +
  521 + <!-- Maximum number of documents to cache for any entry in the
  522 + queryResultCache.
  523 + -->
  524 + <queryResultMaxDocsCached>200</queryResultMaxDocsCached>
  525 +
  526 + <!-- Query Related Event Listeners
  527 +
  528 + Various IndexSearcher related events can trigger Listeners to
  529 + take actions.
  530 +
  531 + newSearcher - fired whenever a new searcher is being prepared
  532 + and there is a current searcher handling requests (aka
  533 + registered). It can be used to prime certain caches to
  534 + prevent long request times for certain requests.
  535 +
  536 + firstSearcher - fired whenever a new searcher is being
  537 + prepared but there is no current registered searcher to handle
  538 + requests or to gain autowarming data from.
  539 +
  540 +
  541 + -->
  542 + <!-- QuerySenderListener takes an array of NamedList and executes a
  543 + local query request for each NamedList in sequence.
  544 + -->
  545 + <listener event="newSearcher" class="solr.QuerySenderListener">
  546 + <arr name="queries">
  547 + <!--
  548 + <lst><str name="q">solr</str><str name="sort">price asc</str></lst>
  549 + <lst><str name="q">rocks</str><str name="sort">weight asc</str></lst>
  550 + -->
  551 + </arr>
  552 + </listener>
  553 + <listener event="firstSearcher" class="solr.QuerySenderListener">
  554 + <arr name="queries">
  555 + <lst>
  556 + <str name="q">static firstSearcher warming in solrconfig.xml</str>
  557 + </lst>
  558 + </arr>
  559 + </listener>
  560 +
  561 + <!-- Use Cold Searcher
  562 +
  563 + If a search request comes in and there is no current
  564 + registered searcher, then immediately register the still
  565 + warming searcher and use it. If "false" then all requests
  566 + will block until the first searcher is done warming.
  567 + -->
  568 + <useColdSearcher>false</useColdSearcher>
  569 +
  570 + <!-- Max Warming Searchers
  571 +
  572 + Maximum number of searchers that may be warming in the
  573 + background concurrently. An error is returned if this limit
  574 + is exceeded.
  575 +
  576 + Recommend values of 1-2 for read-only slaves, higher for
  577 + masters w/o cache warming.
  578 + -->
  579 + <maxWarmingSearchers>2</maxWarmingSearchers>
  580 +
  581 + </query>
  582 +
  583 +
  584 + <!-- Request Dispatcher
  585 +
  586 + This section contains instructions for how the SolrDispatchFilter
  587 + should behave when processing requests for this SolrCore.
  588 +
  589 + handleSelect affects the behavior of requests such as /select?qt=XXX
  590 +
  591 + handleSelect="true" will cause the SolrDispatchFilter to process
  592 + the request and will result in consistent error handling and
  593 + formatting for all types of requests.
  594 +
  595 + handleSelect="false" will cause the SolrDispatchFilter to
  596 + ignore "/select" requests and fallback to using the legacy
  597 + SolrServlet and it's Solr 1.1 style error formatting
  598 + -->
  599 + <requestDispatcher handleSelect="true" >
  600 + <!-- Request Parsing
  601 +
  602 + These settings indicate how Solr Requests may be parsed, and
  603 + what restrictions may be placed on the ContentStreams from
  604 + those requests
  605 +
  606 + enableRemoteStreaming - enables use of the stream.file
  607 + and stream.url parameters for specifying remote streams.
  608 +
  609 + multipartUploadLimitInKB - specifies the max size of
  610 + Multipart File Uploads that Solr will allow in a Request.
  611 +
  612 + *** WARNING ***
  613 + The settings below authorize Solr to fetch remote files, You
  614 + should make sure your system has some authentication before
  615 + using enableRemoteStreaming="true"
  616 +
  617 + -->
  618 + <requestParsers enableRemoteStreaming="true"
  619 + multipartUploadLimitInKB="2048000" />
  620 +
  621 + <!-- HTTP Caching
  622 +
  623 + Set HTTP caching related parameters (for proxy caches and clients).
  624 +
  625 + The options below instruct Solr not to output any HTTP Caching
  626 + related headers
  627 + -->
  628 + <httpCaching never304="true" />
  629 + <!-- If you include a <cacheControl> directive, it will be used to
  630 + generate a Cache-Control header (as well as an Expires header
  631 + if the value contains "max-age=")
  632 +
  633 + By default, no Cache-Control header is generated.
  634 +
  635 + You can use the <cacheControl> option even if you have set
  636 + never304="true"
  637 + -->
  638 + <!--
  639 + <httpCaching never304="true" >
  640 + <cacheControl>max-age=30, public</cacheControl>
  641 + </httpCaching>
  642 + -->
  643 + <!-- To enable Solr to respond with automatically generated HTTP
  644 + Caching headers, and to response to Cache Validation requests
  645 + correctly, set the value of never304="false"
  646 +
  647 + This will cause Solr to generate Last-Modified and ETag
  648 + headers based on the properties of the Index.
  649 +
  650 + The following options can also be specified to affect the
  651 + values of these headers...
  652 +
  653 + lastModFrom - the default value is "openTime" which means the
  654 + Last-Modified value (and validation against If-Modified-Since
  655 + requests) will all be relative to when the current Searcher
  656 + was opened. You can change it to lastModFrom="dirLastMod" if
  657 + you want the value to exactly correspond to when the physical
  658 + index was last modified.
  659 +
  660 + etagSeed="..." is an option you can change to force the ETag
  661 + header (and validation against If-None-Match requests) to be
  662 + different even if the index has not changed (ie: when making
  663 + significant changes to your config file)
  664 +
  665 + (lastModifiedFrom and etagSeed are both ignored if you use
  666 + the never304="true" option)
  667 + -->
  668 + <!--
  669 + <httpCaching lastModifiedFrom="openTime"
  670 + etagSeed="Solr">
  671 + <cacheControl>max-age=30, public</cacheControl>
  672 + </httpCaching>
  673 + -->
  674 + </requestDispatcher>
  675 +
  676 + <!-- Request Handlers
  677 +
  678 + http://wiki.apache.org/solr/SolrRequestHandler
  679 +
  680 + incoming queries will be dispatched to the correct handler
  681 + based on the path or the qt (query type) param.
  682 +
  683 + Names starting with a '/' are accessed with the a path equal to
  684 + the registered name. Names without a leading '/' are accessed
  685 + with: http://host/app/[core/]select?qt=name
  686 +
  687 + If a /select request is processed with out a qt param
  688 + specified, the requestHandler that declares default="true" will
  689 + be used.
  690 +
  691 + If a Request Handler is declared with startup="lazy", then it will
  692 + not be initialized until the first request that uses it.
  693 +
  694 + -->
  695 + <!-- SearchHandler
  696 +
  697 + http://wiki.apache.org/solr/SearchHandler
  698 +
  699 + For processing Search Queries, the primary Request Handler
  700 + provided with Solr is "SearchHandler" It delegates to a sequent
  701 + of SearchComponents (see below) and supports distributed
  702 + queries across multiple shards
  703 + -->
  704 + <requestHandler name="search" class="solr.SearchHandler" default="true">
  705 + <!-- default values for query parameters can be specified, these
  706 + will be overridden by parameters in the request
  707 + -->
  708 + <lst name="defaults">
  709 + <str name="echoParams">explicit</str>
  710 + <int name="rows">10</int>
  711 + </lst>
  712 + <!-- In addition to defaults, "appends" params can be specified
  713 + to identify values which should be appended to the list of
  714 + multi-val params from the query (or the existing "defaults").
  715 + -->
  716 + <!-- In this example, the param "fq=instock:true" would be appended to
  717 + any query time fq params the user may specify, as a mechanism for
  718 + partitioning the index, independent of any user selected filtering
  719 + that may also be desired (perhaps as a result of faceted searching).
  720 +
  721 + NOTE: there is *absolutely* nothing a client can do to prevent these
  722 + "appends" values from being used, so don't use this mechanism
  723 + unless you are sure you always want it.
  724 + -->
  725 + <!--
  726 + <lst name="appends">
  727 + <str name="fq">inStock:true</str>
  728 + </lst>
  729 + -->
  730 + <!-- "invariants" are a way of letting the Solr maintainer lock down
  731 + the options available to Solr clients. Any params values
  732 + specified here are used regardless of what values may be specified
  733 + in either the query, the "defaults", or the "appends" params.
  734 +
  735 + In this example, the facet.field and facet.query params would
  736 + be fixed, limiting the facets clients can use. Faceting is
  737 + not turned on by default - but if the client does specify
  738 + facet=true in the request, these are the only facets they
  739 + will be able to see counts for; regardless of what other
  740 + facet.field or facet.query params they may specify.
  741 +
  742 + NOTE: there is *absolutely* nothing a client can do to prevent these
  743 + "invariants" values from being used, so don't use this mechanism
  744 + unless you are sure you always want it.
  745 + -->
  746 + <!--
  747 + <lst name="invariants">
  748 + <str name="facet.field">cat</str>
  749 + <str name="facet.field">manu_exact</str>
  750 + <str name="facet.query">price:[* TO 500]</str>
  751 + <str name="facet.query">price:[500 TO *]</str>
  752 + </lst>
  753 + -->
  754 + <!-- If the default list of SearchComponents is not desired, that
  755 + list can either be overridden completely, or components can be
  756 + prepended or appended to the default list. (see below)
  757 + -->
  758 + <!--
  759 + <arr name="components">
  760 + <str>nameOfCustomComponent1</str>
  761 + <str>nameOfCustomComponent2</str>
  762 + </arr>
  763 + -->
  764 + </requestHandler>
  765 +
  766 + <!-- A Robust Example
  767 +
  768 + This example SearchHandler declaration shows off usage of the
  769 + SearchHandler with many defaults declared
  770 +
  771 + Note that multiple instances of the same Request Handler
  772 + (SearchHandler) can be registered multiple times with different
  773 + names (and different init parameters)
  774 + -->
  775 + <requestHandler name="/browse" class="solr.SearchHandler">
  776 + <lst name="defaults">
  777 + <str name="echoParams">explicit</str>
  778 +
  779 + <!-- VelocityResponseWriter settings -->
  780 + <str name="wt">velocity</str>
  781 +
  782 + <str name="v.template">browse</str>
  783 + <str name="v.layout">layout</str>
  784 + <str name="title">Solritas</str>
  785 +
  786 + <str name="defType">edismax</str>
  787 + <str name="q.alt">*:*</str>
  788 + <str name="rows">10</str>
  789 + <str name="fl">*,score</str>
  790 + <str name="mlt.qf">
  791 + text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4
  792 + </str>
  793 + <str name="mlt.fl">text,features,name,sku,id,manu,cat</str>
  794 + <int name="mlt.count">3</int>
  795 +
  796 + <str name="qf">
  797 + text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4
  798 + </str>
  799 +
  800 + <str name="facet">on</str>
  801 + <str name="facet.field">cat</str>
  802 + <str name="facet.field">manu_exact</str>
  803 + <str name="facet.query">ipod</str>
  804 + <str name="facet.query">GB</str>
  805 + <str name="facet.mincount">1</str>
  806 + <str name="facet.pivot">cat,inStock</str>
  807 + <str name="facet.range">price</str>
  808 + <int name="f.price.facet.range.start">0</int>
  809 + <int name="f.price.facet.range.end">600</int>
  810 + <int name="f.price.facet.range.gap">50</int>
  811 + <str name="f.price.facet.range.other">after</str>
  812 + <str name="facet.range">manufacturedate_dt</str>
  813 + <str name="f.manufacturedate_dt.facet.range.start">NOW/YEAR-10YEARS</str>
  814 + <str name="f.manufacturedate_dt.facet.range.end">NOW</str>
  815 + <str name="f.manufacturedate_dt.facet.range.gap">+1YEAR</str>
  816 + <str name="f.manufacturedate_dt.facet.range.other">before</str>
  817 + <str name="f.manufacturedate_dt.facet.range.other">after</str>
  818 +
  819 +
  820 + <!-- Highlighting defaults -->
  821 + <str name="hl">on</str>
  822 + <str name="hl.fl">text features name</str>
  823 + <str name="f.name.hl.fragsize">0</str>
  824 + <str name="f.name.hl.alternateField">name</str>
  825 + </lst>
  826 + <arr name="last-components">
  827 + <str>spellcheck</str>
  828 + </arr>
  829 + <!--
  830 + <str name="url-scheme">httpx</str>
  831 + -->
  832 + </requestHandler>
  833 +
  834 + <!-- XML Update Request Handler.
  835 +
  836 + http://wiki.apache.org/solr/UpdateXmlMessages
  837 +
  838 + The canonical Request Handler for Modifying the Index through
  839 + commands specified using XML.
  840 +
  841 + Note: Since solr1.1 requestHandlers requires a valid content
  842 + type header if posted in the body. For example, curl now
  843 + requires: -H 'Content-type:text/xml; charset=utf-8'
  844 + -->
  845 + <requestHandler name="/update"
  846 + class="solr.XmlUpdateRequestHandler">
  847 + <!-- See below for information on defining
  848 + updateRequestProcessorChains that can be used by name
  849 + on each Update Request
  850 + -->
  851 + <!--
  852 + <lst name="defaults">
  853 + <str name="update.chain">dedupe</str>
  854 + </lst>
  855 + -->
  856 + </requestHandler>
  857 + <!-- Binary Update Request Handler
  858 + http://wiki.apache.org/solr/javabin
  859 + -->
  860 + <requestHandler name="/update/javabin"
  861 + class="solr.BinaryUpdateRequestHandler" />
  862 +
  863 + <!-- CSV Update Request Handler
  864 + http://wiki.apache.org/solr/UpdateCSV
  865 + -->
  866 + <requestHandler name="/update/csv"
  867 + class="solr.CSVRequestHandler"
  868 + startup="lazy" />
  869 +
  870 + <!-- JSON Update Request Handler
  871 + http://wiki.apache.org/solr/UpdateJSON
  872 + -->
  873 + <requestHandler name="/update/json"
  874 + class="solr.JsonUpdateRequestHandler"
  875 + startup="lazy" />
  876 +
  877 + <!-- Solr Cell Update Request Handler
  878 +
  879 + http://wiki.apache.org/solr/ExtractingRequestHandler
  880 +
  881 + -->
  882 + <requestHandler name="/update/extract"
  883 + startup="lazy"
  884 + class="solr.extraction.ExtractingRequestHandler" >
  885 + <lst name="defaults">
  886 + <!-- All the main content goes into "text"... if you need to return
  887 + the extracted text or do highlighting, use a stored field. -->
  888 + <str name="fmap.content">text</str>
  889 + <str name="lowernames">true</str>
  890 + <str name="uprefix">ignored_</str>
  891 +
  892 + <!-- capture link hrefs but ignore div attributes -->
  893 + <str name="captureAttr">true</str>
  894 + <str name="fmap.a">links</str>
  895 + <str name="fmap.div">ignored_</str>
  896 + </lst>
  897 + </requestHandler>
  898 +
  899 + <!-- Field Analysis Request Handler
  900 +
  901 + RequestHandler that provides much the same functionality as
  902 + analysis.jsp. Provides the ability to specify multiple field
  903 + types and field names in the same request and outputs
  904 + index-time and query-time analysis for each of them.
  905 +
  906 + Request parameters are:
  907 + analysis.fieldname - field name whose analyzers are to be used
  908 +
  909 + analysis.fieldtype - field type whose analyzers are to be used
  910 + analysis.fieldvalue - text for index-time analysis
  911 + q (or analysis.q) - text for query time analysis
  912 + analysis.showmatch (true|false) - When set to true and when
  913 + query analysis is performed, the produced tokens of the
  914 + field value analysis will be marked as "matched" for every
  915 + token that is produces by the query analysis
  916 + -->
  917 + <requestHandler name="/analysis/field"
  918 + startup="lazy"
  919 + class="solr.FieldAnalysisRequestHandler" />
  920 +
  921 +
  922 + <!-- Document Analysis Handler
  923 +
  924 + http://wiki.apache.org/solr/AnalysisRequestHandler
  925 +
  926 + An analysis handler that provides a breakdown of the analysis
  927 + process of provided docuemnts. This handler expects a (single)
  928 + content stream with the following format:
  929 +
  930 + <docs>
  931 + <doc>
  932 + <field name="id">1</field>
  933 + <field name="name">The Name</field>
  934 + <field name="text">The Text Value</field>
  935 + </doc>
  936 + <doc>...</doc>
  937 + <doc>...</doc>
  938 + ...
  939 + </docs>
  940 +
  941 + Note: Each document must contain a field which serves as the
  942 + unique key. This key is used in the returned response to associate
  943 + an analysis breakdown to the analyzed document.
  944 +
  945 + Like the FieldAnalysisRequestHandler, this handler also supports
  946 + query analysis by sending either an "analysis.query" or "q"
  947 + request parameter that holds the query text to be analyzed. It
  948 + also supports the "analysis.showmatch" parameter which when set to
  949 + true, all field tokens that match the query tokens will be marked
  950 + as a "match".
  951 + -->
  952 + <requestHandler name="/analysis/document"
  953 + class="solr.DocumentAnalysisRequestHandler"
  954 + startup="lazy" />
  955 +
  956 + <!-- Admin Handlers
  957 +
  958 + Admin Handlers - This will register all the standard admin
  959 + RequestHandlers.
  960 + -->
  961 + <requestHandler name="/admin/"
  962 + class="solr.admin.AdminHandlers" />
  963 + <!-- This single handler is equivalent to the following... -->
  964 + <!--
  965 + <requestHandler name="/admin/luke" class="solr.admin.LukeRequestHandler" />
  966 + <requestHandler name="/admin/system" class="solr.admin.SystemInfoHandler" />
  967 + <requestHandler name="/admin/plugins" class="solr.admin.PluginInfoHandler" />
  968 + <requestHandler name="/admin/threads" class="solr.admin.ThreadDumpHandler" />
  969 + <requestHandler name="/admin/properties" class="solr.admin.PropertiesRequestHandler" />
  970 + <requestHandler name="/admin/file" class="solr.admin.ShowFileRequestHandler" >
  971 + -->
  972 + <!-- If you wish to hide files under ${solr.home}/conf, explicitly
  973 + register the ShowFileRequestHandler using:
  974 + -->
  975 + <!--
  976 + <requestHandler name="/admin/file"
  977 + class="solr.admin.ShowFileRequestHandler" >
  978 + <lst name="invariants">
  979 + <str name="hidden">synonyms.txt</str>
  980 + <str name="hidden">anotherfile.txt</str>
  981 + </lst>
  982 + </requestHandler>
  983 + -->
  984 +
  985 + <!-- ping/healthcheck -->
  986 + <requestHandler name="/admin/ping" class="solr.PingRequestHandler">
  987 + <lst name="defaults">
  988 + <str name="qt">search</str>
  989 + <str name="q">solrpingquery</str>
  990 + <str name="echoParams">all</str>
  991 + </lst>
  992 + </requestHandler>
  993 +
  994 + <!-- Echo the request contents back to the client -->
  995 + <requestHandler name="/debug/dump" class="solr.DumpRequestHandler" >
  996 + <lst name="defaults">
  997 + <str name="echoParams">explicit</str>
  998 + <str name="echoHandler">true</str>
  999 + </lst>
  1000 + </requestHandler>
  1001 +
  1002 + <!-- Solr Replication
  1003 +
  1004 + The SolrReplicationHandler supports replicating indexes from a
  1005 + "master" used for indexing and "salves" used for queries.
  1006 +
  1007 + http://wiki.apache.org/solr/SolrReplication
  1008 +
  1009 + In the example below, remove the <lst name="master"> section if
  1010 + this is just a slave and remove the <lst name="slave"> section
  1011 + if this is just a master.
  1012 + -->
  1013 + <!--
  1014 + <requestHandler name="/replication" class="solr.ReplicationHandler" >
  1015 + <lst name="master">
  1016 + <str name="replicateAfter">commit</str>
  1017 + <str name="replicateAfter">startup</str>
  1018 + <str name="confFiles">schema.xml,stopwords.txt</str>
  1019 + </lst>
  1020 + <lst name="slave">
  1021 + <str name="masterUrl">http://localhost:8983/solr/replication</str>
  1022 + <str name="pollInterval">00:00:60</str>
  1023 + </lst>
  1024 + </requestHandler>
  1025 + -->
  1026 +
  1027 + <!-- Search Components
  1028 +
  1029 + Search components are registered to SolrCore and used by
  1030 + instances of SearchHandler (which can access them by name)
  1031 +
  1032 + By default, the following components are available:
  1033 +
  1034 + <searchComponent name="query" class="solr.QueryComponent" />
  1035 + <searchComponent name="facet" class="solr.FacetComponent" />
  1036 + <searchComponent name="mlt" class="solr.MoreLikeThisComponent" />
  1037 + <searchComponent name="highlight" class="solr.HighlightComponent" />
  1038 + <searchComponent name="stats" class="solr.StatsComponent" />
  1039 + <searchComponent name="debug" class="solr.DebugComponent" />
  1040 +
  1041 + Default configuration in a requestHandler would look like:
  1042 +
  1043 + <arr name="components">
  1044 + <str>query</str>
  1045 + <str>facet</str>
  1046 + <str>mlt</str>
  1047 + <str>highlight</str>
  1048 + <str>stats</str>
  1049 + <str>debug</str>
  1050 + </arr>
  1051 +
  1052 + If you register a searchComponent to one of the standard names,
  1053 + that will be used instead of the default.
  1054 +
  1055 + To insert components before or after the 'standard' components, use:
  1056 +
  1057 + <arr name="first-components">
  1058 + <str>myFirstComponentName</str>
  1059 + </arr>
  1060 +
  1061 + <arr name="last-components">
  1062 + <str>myLastComponentName</str>
  1063 + </arr>
  1064 +
  1065 + NOTE: The component registered with the name "debug" will
  1066 + always be executed after the "last-components"
  1067 +
  1068 + -->
  1069 +
  1070 + <!-- Spell Check
  1071 +
  1072 + The spell check component can return a list of alternative spelling
  1073 + suggestions.
  1074 +
  1075 + http://wiki.apache.org/solr/SpellCheckComponent
  1076 + -->
  1077 + <searchComponent name="spellcheck" class="solr.SpellCheckComponent">
  1078 +
  1079 + <str name="queryAnalyzerFieldType">textSpell</str>
  1080 +
  1081 + <!-- Multiple "Spell Checkers" can be declared and used by this
  1082 + component
  1083 + -->
  1084 +
  1085 + <!-- a spellchecker built from a field of the main index, and
  1086 + written to disk
  1087 + -->
  1088 + <lst name="spellchecker">
  1089 + <str name="name">default</str>
  1090 + <str name="field">name</str>
  1091 + <str name="spellcheckIndexDir">spellchecker</str>
  1092 + <!-- uncomment this to require terms to occur in 1% of the documents in order to be included in the dictionary
  1093 + <float name="thresholdTokenFrequency">.01</float>
  1094 + -->
  1095 + </lst>
  1096 +
  1097 + <!-- a spellchecker that uses a different distance measure -->
  1098 + <!--
  1099 + <lst name="spellchecker">
  1100 + <str name="name">jarowinkler</str>
  1101 + <str name="field">spell</str>
  1102 + <str name="distanceMeasure">
  1103 + org.apache.lucene.search.spell.JaroWinklerDistance
  1104 + </str>
  1105 + <str name="spellcheckIndexDir">spellcheckerJaro</str>
  1106 + </lst>
  1107 + -->
  1108 +
  1109 + <!-- a spellchecker that use an alternate comparator
  1110 +
  1111 + comparatorClass be one of:
  1112 + 1. score (default)
  1113 + 2. freq (Frequency first, then score)
  1114 + 3. A fully qualified class name
  1115 + -->
  1116 + <!--
  1117 + <lst name="spellchecker">
  1118 + <str name="name">freq</str>
  1119 + <str name="field">lowerfilt</str>
  1120 + <str name="spellcheckIndexDir">spellcheckerFreq</str>
  1121 + <str name="comparatorClass">freq</str>
  1122 + <str name="buildOnCommit">true</str>
  1123 + -->
  1124 +
  1125 + <!-- A spellchecker that reads the list of words from a file -->
  1126 + <!--
  1127 + <lst name="spellchecker">
  1128 + <str name="classname">solr.FileBasedSpellChecker</str>
  1129 + <str name="name">file</str>
  1130 + <str name="sourceLocation">spellings.txt</str>
  1131 + <str name="characterEncoding">UTF-8</str>
  1132 + <str name="spellcheckIndexDir">spellcheckerFile</str>
  1133 + </lst>
  1134 + -->
  1135 + </searchComponent>
  1136 +
  1137 + <!-- A request handler for demonstrating the spellcheck component.
  1138 +
  1139 + NOTE: This is purely as an example. The whole purpose of the
  1140 + SpellCheckComponent is to hook it into the request handler that
  1141 + handles your normal user queries so that a separate request is
  1142 + not needed to get suggestions.
  1143 +
  1144 + IN OTHER WORDS, THERE IS REALLY GOOD CHANCE THE SETUP BELOW IS
  1145 + NOT WHAT YOU WANT FOR YOUR PRODUCTION SYSTEM!
  1146 +
  1147 + See http://wiki.apache.org/solr/SpellCheckComponent for details
  1148 + on the request parameters.
  1149 + -->
  1150 + <requestHandler name="/spell" class="solr.SearchHandler" startup="lazy">
  1151 + <lst name="defaults">
  1152 + <str name="spellcheck.onlyMorePopular">false</str>
  1153 + <str name="spellcheck.extendedResults">false</str>
  1154 + <str name="spellcheck.count">1</str>
  1155 + </lst>
  1156 + <arr name="last-components">
  1157 + <str>spellcheck</str>
  1158 + </arr>
  1159 + </requestHandler>
  1160 +
  1161 + <!-- Term Vector Component
  1162 +
  1163 + http://wiki.apache.org/solr/TermVectorComponent
  1164 + -->
  1165 + <searchComponent name="tvComponent" class="solr.TermVectorComponent"/>
  1166 +
  1167 + <!-- A request handler for demonstrating the term vector component
  1168 +
  1169 + This is purely as an example.
  1170 +
  1171 + In reality you will likely want to add the component to your
  1172 + already specified request handlers.
  1173 + -->
  1174 + <requestHandler name="tvrh" class="solr.SearchHandler" startup="lazy">
  1175 + <lst name="defaults">
  1176 + <bool name="tv">true</bool>
  1177 + </lst>
  1178 + <arr name="last-components">
  1179 + <str>tvComponent</str>
  1180 + </arr>
  1181 + </requestHandler>
  1182 +
  1183 + <!-- Clustering Component
  1184 +
  1185 + http://wiki.apache.org/solr/ClusteringComponent
  1186 +
  1187 + This relies on third party jars which are notincluded in the
  1188 + release. To use this component (and the "/clustering" handler)
  1189 + Those jars will need to be downloaded, and you'll need to set
  1190 + the solr.cluster.enabled system property when running solr...
  1191 +
  1192 + java -Dsolr.clustering.enabled=true -jar start.jar
  1193 + -->
  1194 + <searchComponent name="clustering"
  1195 + enable="${solr.clustering.enabled:false}"
  1196 + class="solr.clustering.ClusteringComponent" >
  1197 + <!-- Declare an engine -->
  1198 + <lst name="engine">
  1199 + <!-- The name, only one can be named "default" -->
  1200 + <str name="name">default</str>
  1201 +
  1202 + <!-- Class name of Carrot2 clustering algorithm.
  1203 +
  1204 + Currently available algorithms are:
  1205 +
  1206 + * org.carrot2.clustering.lingo.LingoClusteringAlgorithm
  1207 + * org.carrot2.clustering.stc.STCClusteringAlgorithm
  1208 + * org.carrot2.clustering.kmeans.BisectingKMeansClusteringAlgorithm
  1209 +
  1210 + See http://project.carrot2.org/algorithms.html for the
  1211 + algorithm's characteristics.
  1212 + -->
  1213 + <str name="carrot.algorithm">org.carrot2.clustering.lingo.LingoClusteringAlgorithm</str>
  1214 +
  1215 + <!-- Overriding values for Carrot2 default algorithm attributes.
  1216 +
  1217 + For a description of all available attributes, see:
  1218 + http://download.carrot2.org/stable/manual/#chapter.components.
  1219 + Use attribute key as name attribute of str elements
  1220 + below. These can be further overridden for individual
  1221 + requests by specifying attribute key as request parameter
  1222 + name and attribute value as parameter value.
  1223 + -->
  1224 + <str name="LingoClusteringAlgorithm.desiredClusterCountBase">20</str>
  1225 +
  1226 + <!-- Location of Carrot2 lexical resources.
  1227 +
  1228 + A directory from which to load Carrot2-specific stop words
  1229 + and stop labels. Absolute or relative to Solr config directory.
  1230 + If a specific resource (e.g. stopwords.en) is present in the
  1231 + specified dir, it will completely override the corresponding
  1232 + default one that ships with Carrot2.
  1233 +
  1234 + For an overview of Carrot2 lexical resources, see:
  1235 + http://download.carrot2.org/head/manual/#chapter.lexical-resources
  1236 + -->
  1237 + <str name="carrot.lexicalResourcesDir">clustering/carrot2</str>
  1238 +
  1239 + <!-- The language to assume for the documents.
  1240 +
  1241 + For a list of allowed values, see:
  1242 + http://download.carrot2.org/stable/manual/#section.attribute.lingo.MultilingualClustering.defaultLanguage
  1243 + -->
  1244 + <str name="MultilingualClustering.defaultLanguage">ENGLISH</str>
  1245 + </lst>
  1246 + <lst name="engine">
  1247 + <str name="name">stc</str>
  1248 + <str name="carrot.algorithm">org.carrot2.clustering.stc.STCClusteringAlgorithm</str>
  1249 + </lst>
  1250 + </searchComponent>
  1251 +
  1252 + <!-- A request handler for demonstrating the clustering component
  1253 +
  1254 + This is purely as an example.
  1255 +
  1256 + In reality you will likely want to add the component to your
  1257 + already specified request handlers.
  1258 + -->
  1259 + <requestHandler name="/clustering"
  1260 + startup="lazy"
  1261 + enable="${solr.clustering.enabled:false}"
  1262 + class="solr.SearchHandler">
  1263 + <lst name="defaults">
  1264 + <bool name="clustering">true</bool>
  1265 + <str name="clustering.engine">default</str>
  1266 + <bool name="clustering.results">true</bool>
  1267 + <!-- The title field -->
  1268 + <str name="carrot.title">name</str>
  1269 + <str name="carrot.url">id</str>
  1270 + <!-- The field to cluster on -->
  1271 + <str name="carrot.snippet">features</str>
  1272 + <!-- produce summaries -->
  1273 + <bool name="carrot.produceSummary">true</bool>
  1274 + <!-- the maximum number of labels per cluster -->
  1275 + <!--<int name="carrot.numDescriptions">5</int>-->
  1276 + <!-- produce sub clusters -->
  1277 + <bool name="carrot.outputSubClusters">false</bool>
  1278 +
  1279 + <str name="defType">edismax</str>
  1280 + <str name="qf">
  1281 + text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4
  1282 + </str>
  1283 + <str name="q.alt">*:*</str>
  1284 + <str name="rows">10</str>
  1285 + <str name="fl">*,score</str>
  1286 + </lst>
  1287 + <arr name="last-components">
  1288 + <str>clustering</str>
  1289 + </arr>
  1290 + </requestHandler>
  1291 +
  1292 + <!-- Terms Component
  1293 +
  1294 + http://wiki.apache.org/solr/TermsComponent
  1295 +
  1296 + A component to return terms and document frequency of those
  1297 + terms
  1298 + -->
  1299 + <searchComponent name="terms" class="solr.TermsComponent"/>
  1300 +
  1301 + <!-- A request handler for demonstrating the terms component -->
  1302 + <requestHandler name="/terms" class="solr.SearchHandler" startup="lazy">
  1303 + <lst name="defaults">
  1304 + <bool name="terms">true</bool>
  1305 + </lst>
  1306 + <arr name="components">
  1307 + <str>terms</str>
  1308 + </arr>
  1309 + </requestHandler>
  1310 +
  1311 +
  1312 + <!-- Query Elevation Component
  1313 +
  1314 + http://wiki.apache.org/solr/QueryElevationComponent
  1315 +
  1316 + a search component that enables you to configure the top
  1317 + results for a given query regardless of the normal lucene
  1318 + scoring.
  1319 + -->
  1320 + <searchComponent name="elevator" class="solr.QueryElevationComponent" >
  1321 + <!-- pick a fieldType to analyze queries -->
  1322 + <str name="queryFieldType">string</str>
  1323 + <str name="config-file">elevate.xml</str>
  1324 + </searchComponent>
  1325 +
  1326 + <!-- A request handler for demonstrating the elevator component -->
  1327 + <requestHandler name="/elevate" class="solr.SearchHandler" startup="lazy">
  1328 + <lst name="defaults">
  1329 + <str name="echoParams">explicit</str>
  1330 + </lst>
  1331 + <arr name="last-components">
  1332 + <str>elevator</str>
  1333 + </arr>
  1334 + </requestHandler>
  1335 +
  1336 + <!-- Highlighting Component
  1337 +
  1338 + http://wiki.apache.org/solr/HighlightingParameters
  1339 + -->
  1340 + <searchComponent class="solr.HighlightComponent" name="highlight">
  1341 + <highlighting>
  1342 + <!-- Configure the standard fragmenter -->
  1343 + <!-- This could most likely be commented out in the "default" case -->
  1344 + <fragmenter name="gap"
  1345 + default="true"
  1346 + class="solr.highlight.GapFragmenter">
  1347 + <lst name="defaults">
  1348 + <int name="hl.fragsize">100</int>
  1349 + </lst>
  1350 + </fragmenter>
  1351 +
  1352 + <!-- A regular-expression-based fragmenter
  1353 + (for sentence extraction)
  1354 + -->
  1355 + <fragmenter name="regex"
  1356 + class="solr.highlight.RegexFragmenter">
  1357 + <lst name="defaults">
  1358 + <!-- slightly smaller fragsizes work better because of slop -->
  1359 + <int name="hl.fragsize">70</int>
  1360 + <!-- allow 50% slop on fragment sizes -->
  1361 + <float name="hl.regex.slop">0.5</float>
  1362 + <!-- a basic sentence pattern -->
  1363 + <str name="hl.regex.pattern">[-\w ,/\n\&quot;&apos;]{20,200}</str>
  1364 + </lst>
  1365 + </fragmenter>
  1366 +
  1367 + <!-- Configure the standard formatter -->
  1368 + <formatter name="html"
  1369 + default="true"
  1370 + class="solr.highlight.HtmlFormatter">
  1371 + <lst name="defaults">
  1372 + <str name="hl.simple.pre"><![CDATA[<em>]]></str>
  1373 + <str name="hl.simple.post"><![CDATA[</em>]]></str>
  1374 + </lst>
  1375 + </formatter>
  1376 +
  1377 + <!-- Configure the standard encoder -->
  1378 + <encoder name="html"
  1379 + class="solr.highlight.HtmlEncoder" />
  1380 +
  1381 + <!-- Configure the standard fragListBuilder -->
  1382 + <fragListBuilder name="simple"
  1383 + default="true"
  1384 + class="solr.highlight.SimpleFragListBuilder"/>
  1385 +
  1386 + <!-- Configure the single fragListBuilder -->
  1387 + <fragListBuilder name="single"
  1388 + class="solr.highlight.SingleFragListBuilder"/>
  1389 +
  1390 + <!-- default tag FragmentsBuilder -->
  1391 + <fragmentsBuilder name="default"
  1392 + default="true"
  1393 + class="solr.highlight.ScoreOrderFragmentsBuilder">
  1394 + <!--
  1395 + <lst name="defaults">
  1396 + <str name="hl.multiValuedSeparatorChar">/</str>
  1397 + </lst>
  1398 + -->
  1399 + </fragmentsBuilder>
  1400 +
  1401 + <!-- multi-colored tag FragmentsBuilder -->
  1402 + <fragmentsBuilder name="colored"
  1403 + class="solr.highlight.ScoreOrderFragmentsBuilder">
  1404 + <lst name="defaults">
  1405 + <str name="hl.tag.pre"><![CDATA[
  1406 + <b style="background:yellow">,<b style="background:lawgreen">,
  1407 + <b style="background:aquamarine">,<b style="background:magenta">,
  1408 + <b style="background:palegreen">,<b style="background:coral">,
  1409 + <b style="background:wheat">,<b style="background:khaki">,
  1410 + <b style="background:lime">,<b style="background:deepskyblue">]]></str>
  1411 + <str name="hl.tag.post"><![CDATA[</b>]]></str>
  1412 + </lst>
  1413 + </fragmentsBuilder>
  1414 + </highlighting>
  1415 + </searchComponent>
  1416 +
  1417 + <!-- Update Processors
  1418 +
  1419 + Chains of Update Processor Factories for dealing with Update
  1420 + Requests can be declared, and then used by name in Update
  1421 + Request Processors
  1422 +
  1423 + http://wiki.apache.org/solr/UpdateRequestProcessor
  1424 +
  1425 + -->
  1426 + <!-- Deduplication
  1427 +
  1428 + An example dedup update processor that creates the "id" field
  1429 + on the fly based on the hash code of some other fields. This
  1430 + example has overwriteDupes set to false since we are using the
  1431 + id field as the signatureField and Solr will maintain
  1432 + uniqueness based on that anyway.
  1433 +
  1434 + -->
  1435 + <!--
  1436 + <updateRequestProcessorChain name="dedupe">
  1437 + <processor class="solr.processor.SignatureUpdateProcessorFactory">
  1438 + <bool name="enabled">true</bool>
  1439 + <str name="signatureField">id</str>
  1440 + <bool name="overwriteDupes">false</bool>
  1441 + <str name="fields">name,features,cat</str>
  1442 + <str name="signatureClass">solr.processor.Lookup3Signature</str>
  1443 + </processor>
  1444 + <processor class="solr.LogUpdateProcessorFactory" />
  1445 + <processor class="solr.RunUpdateProcessorFactory" />
  1446 + </updateRequestProcessorChain>
  1447 + -->
  1448 +
  1449 + <!-- Response Writers
  1450 +
  1451 + http://wiki.apache.org/solr/QueryResponseWriter
  1452 +
  1453 + Request responses will be written using the writer specified by
  1454 + the 'wt' request parameter matching the name of a registered
  1455 + writer.
  1456 +
  1457 + The "default" writer is the default and will be used if 'wt' is
  1458 + not specified in the request.
  1459 + -->
  1460 + <!-- The following response writers are implicitly configured unless
  1461 + overridden...
  1462 + -->
  1463 + <!--
  1464 + <queryResponseWriter name="xml"
  1465 + default="true"
  1466 + class="solr.XMLResponseWriter" />
  1467 + <queryResponseWriter name="json" class="solr.JSONResponseWriter"/>
  1468 + <queryResponseWriter name="python" class="solr.PythonResponseWriter"/>
  1469 + <queryResponseWriter name="ruby" class="solr.RubyResponseWriter"/>
  1470 + <queryResponseWriter name="php" class="solr.PHPResponseWriter"/>
  1471 + <queryResponseWriter name="phps" class="solr.PHPSerializedResponseWriter"/>
  1472 + <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter"/>
  1473 + <queryResponseWriter name="csv" class="solr.CSVResponseWriter"/>
  1474 + -->
  1475 + <!--
  1476 + Custom response writers can be declared as needed...
  1477 + -->
  1478 + <!--
  1479 + <queryResponseWriter name="custom" class="com.example.MyResponseWriter"/>
  1480 + -->
  1481 +
  1482 + <!-- XSLT response writer transforms the XML output by any xslt file found
  1483 + in Solr's conf/xslt directory. Changes to xslt files are checked for
  1484 + every xsltCacheLifetimeSeconds.
  1485 + -->
  1486 + <queryResponseWriter name="xslt" class="solr.XSLTResponseWriter">
  1487 + <int name="xsltCacheLifetimeSeconds">5</int>
  1488 + </queryResponseWriter>
  1489 +
  1490 + <!-- Query Parsers
  1491 +
  1492 + http://wiki.apache.org/solr/SolrQuerySyntax
  1493 +
  1494 + Multiple QParserPlugins can be registered by name, and then
  1495 + used in either the "defType" param for the QueryComponent (used
  1496 + by SearchHandler) or in LocalParams
  1497 + -->
  1498 + <!-- example of registering a query parser -->
  1499 + <!--
  1500 + <queryParser name="myparser" class="com.mycompany.MyQParserPlugin"/>
  1501 + -->
  1502 +
  1503 + <!-- Function Parsers
  1504 +
  1505 + http://wiki.apache.org/solr/FunctionQuery
  1506 +
  1507 + Multiple ValueSourceParsers can be registered by name, and then
  1508 + used as function names when using the "func" QParser.
  1509 + -->
  1510 + <!-- example of registering a custom function parser -->
  1511 + <!--
  1512 + <valueSourceParser name="myfunc"
  1513 + class="com.mycompany.MyValueSourceParser" />
  1514 + -->
  1515 +
  1516 + <!-- Legacy config for the admin interface -->
  1517 + <admin>
  1518 + <defaultQuery>*:*</defaultQuery>
  1519 +
  1520 + <!-- configure a healthcheck file for servers behind a
  1521 + loadbalancer
  1522 + -->
  1523 + <!--
  1524 + <healthcheck type="file">server-enabled</healthcheck>
  1525 + -->
  1526 + </admin>
  1527 +
  1528 + <requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
  1529 + <lst name="defaults">
  1530 + <str name="config">/etc/solr/data-config.xml</str>
  1531 + </lst>
  1532 + </requestHandler>
  1533 +
  1534 +
  1535 +</config>
... ...
sorl-conf/README
... ... @@ -1,16 +0,0 @@
1   -Installation instructions for Ubuntu 10.04
2   --------------------------------------------
3   -
4   -* Install Java, tomcat, Solr and JDBC Postgres drivers (Ubuntu partner repositories must be enabled):
5   -sudo apt-get install sun-java6-bin tomcat6 solr-common libpg-java
6   -
7   -* Link the JDBC Postgres drivers into the Solr installation:
8   -sudo ln -s /usr/share/java/postgresql-jdbc3-8.4.jar /usr/share/solr/WEB-INF/lib/
9   -
10   -* Check data-config.xml to make sure all information to connect to the databases are right
11   -
12   -* Copy the configuration files from this folder into /etc/solr/conf/
13   -
14   -* Restart tomcat:
15   -sudo /etc/init.d/tomcat6 restart
16   -
sorl-conf/data-config.xml
... ... @@ -1,314 +0,0 @@
1   - <dataConfig>
2   - <dataSource name="trac"
3   - type="JdbcDataSource"
4   - driver="org.postgresql.Driver"
5   - url="jdbc:postgresql://localhost/trac_gitec"
6   - user="colab" />
7   - <dataSource name="colab"
8   - type="JdbcDataSource"
9   - driver="org.postgresql.Driver"
10   - url="jdbc:postgresql://localhost/colab"
11   - user="colab" />
12   -
13   - <document>
14   -
15   - <entity name="wiki"
16   - dataSource="trac"
17   - transformer="TemplateTransformer,DateFormatTransformer"
18   - query="SELECT
19   - name,
20   - TIMESTAMP 'epoch' + max(time) * INTERVAL '1s' AS modified,
21   - max(version) AS version
22   - FROM wiki GROUP BY name"
23   - deltaQuery="
24   - SELECT DISTINCT
25   - name
26   - FROM
27   - wiki
28   - WHERE
29   - time > EXTRACT(
30   - epoch FROM TIMESTAMP '${dataimporter.wiki.last_index_time}'
31   - )"
32   - deltaImportQuery="
33   - SELECT
34   - name,
35   - TIMESTAMP 'epoch' + max(time) * INTERVAL '1s' AS modified,
36   - max(version) AS version
37   - FROM
38   - wiki
39   - WHERE
40   - name = '${dataimporter.delta.id}'
41   - GROUP BY name">
42   -
43   - <entity name="wiki_creation"
44   - dataSource="trac"
45   - query="SELECT
46   - author AS creator,
47   - TIMESTAMP 'epoch' + time * INTERVAL '1s' AS created
48   - FROM
49   - wiki
50   - WHERE
51   - name = '${wiki.name}'
52   - AND version = 1" />
53   -
54   - <entity name="wiki_collaborators"
55   - dataSource="trac"
56   - query="SELECT DISTINCT
57   - author AS collaborator
58   - FROM
59   - wiki
60   - WHERE
61   - name = '${wiki.name}'
62   - AND author != ''" />
63   -
64   - <entity name="content"
65   - dataSource="trac"
66   - query="SELECT
67   - text AS content
68   - FROM
69   - wiki
70   - WHERE
71   - name = '${wiki.name}'
72   - AND version = '${wiki.version}'" />
73   -
74   - <field column="uid" template="WIKI_${wiki.name}" />
75   - <field column="id" template="${wiki.name}" />
76   - <field column="type" template="wiki" />
77   - <field column="title" template="${wiki.name}" />
78   - <field column="created" name="created"
79   - dateTimeFormat="yyyy-MM-dd hh:mm:ss"/>
80   - <field column="modified" name="modified"
81   - dateTimeFormat="yyyy-MM-dd hh:mm:ss"/>
82   - </entity>
83   -
84   - <entity name="ticket"
85   - dataSource="trac"
86   - transformer="TemplateTransformer,DateFormatTransformer"
87   - pk="id"
88   - deltaQuery="
89   - SELECT
90   - id
91   - FROM
92   - ticket
93   - WHERE
94   - time > EXTRACT(
95   - epoch FROM TIMESTAMP '${dataimporter.ticket.last_index_time}'
96   - )"
97   - query="SELECT
98   - id,
99   - summary,
100   - description,
101   - milestone,
102   - priority,
103   - component,
104   - version,
105   - severity,
106   - reporter,
107   - owner,
108   - status,
109   - TIMESTAMP 'epoch' + time * INTERVAL '1s' AS created,
110   - TIMESTAMP 'epoch' + changetime * INTERVAL '1s' AS modified
111   - FROM
112   - ticket">
113   -
114   - <entity name="ticket_collaborator"
115   - dataSource="trac"
116   - query="SELECT
117   - reporter AS collaborator
118   - FROM
119   - ticket
120   - WHERE
121   - id = ${ticket.id}
122   -
123   - UNION
124   -
125   - SELECT
126   - owner AS collaborator
127   - FROM
128   - ticket
129   - WHERE
130   - id = ${ticket.id}
131   -
132   - UNION
133   -
134   - SELECT DISTINCT
135   - author AS collaborator
136   - FROM
137   - ticket_change
138   - WHERE
139   - ticket = ${ticket.id}" />
140   -
141   - <entity name="ticket_keywords"
142   - dataSource="trac"
143   - query="SELECT DISTINCT
144   - REGEXP_SPLIT_TO_TABLE(keywords, ',|\\s') AS keyword
145   - FROM
146   - ticket
147   - WHERE
148   - id = ${ticket.id} AND
149   - keywords != ''" />
150   -
151   - <entity name="ticket_comments"
152   - dataSource="trac"
153   - query="SELECT
154   - newvalue AS comment
155   - FROM
156   - ticket_change
157   - WHERE
158   - ticket = ${ticket.id}
159   - AND field = 'comment'" />
160   -
161   - <field column="uid" template="TICKET_${ticket.id}" />
162   - <field column="type" template="ticket" />
163   - <field column="title"
164   - template="#${ticket.id} (${ticket.status}) - ${ticket.summary}" />
165   - <field column="creator" template="${ticket.reporter}" />
166   - <field column="created" name="created"
167   - dateTimeFormat="yyyy-MM-dd hh:mm:ss"/>
168   - <field column="modified" name="modified"
169   - dateTimeFormat="yyyy-MM-dd hh:mm:ss"/>
170   - </entity>
171   -
172   - <entity name="changeset"
173   - dataSource="trac"
174   - transformer="TemplateTransformer,DateFormatTransformer"
175   - pk="rev"
176   - deltaQuery="
177   - SELECT
178   - rev
179   - FROM
180   - revision
181   - WHERE
182   - time > EXTRACT(
183   - epoch FROM TIMESTAMP '${dataimporter.changeset.last_index_time}'
184   - )"
185   -
186   - query="SELECT
187   - rev AS revision,
188   - author AS creator,
189   - author AS collaborator,
190   - TIMESTAMP 'epoch' + time * INTERVAL '1s' AS created,
191   - message
192   - FROM
193   - revision">
194   - <field column="uid" template="CHANGESET_${changeset.revision}" />
195   - <field column="id" template="${changeset.revision}" />
196   - <field column="type" template="changeset" />
197   - <field column="title"
198   - template="[${changeset.revision}] - ${changeset.message}" />
199   - <field column="created" name="created"
200   - dateTimeFormat="yyyy-MM-dd hh:mm:ss"/>
201   - </entity>
202   -
203   - <entity name="thread"
204   - dataSource="colab"
205   - transformer="TemplateTransformer,DateFormatTransformer"
206   - deltaQuery="
207   - SELECT
208   - thread_id AS id
209   - FROM
210   - super_archives_message
211   - GROUP BY
212   - thread_id
213   - HAVING
214   - max(received_time) > '${dataimporter.thread.last_index_time}'"
215   - deltaImportQuery="SELECT
216   - sam.thread_id AS id,
217   - sat.subject_token AS name,
218   - sat.latest_message_id,
219   - saml.name AS mailinglist,
220   - array_to_string(array_agg(sam.body), ' ') AS content
221   - FROM
222   - super_archives_message AS sam
223   - JOIN super_archives_thread AS sat
224   - ON sat.id = sam.thread_id
225   - JOIN super_archives_mailinglist AS saml
226   - ON sat.mailinglist_id = saml.id
227   - WHERE
228   - sat.id = '${dataimporter.delta.id}'
229   - GROUP BY
230   - sam.thread_id,
231   - sat.subject_token,
232   - sat.latest_message_id,
233   - saml.name"
234   -
235   - query="SELECT
236   - sam.thread_id AS id,
237   - sat.subject_token AS name,
238   - sat.latest_message_id,
239   - saml.name AS mailinglist,
240   - array_to_string(array_agg(sam.body), ' ') AS content
241   - FROM
242   - super_archives_message AS sam
243   - JOIN super_archives_thread AS sat
244   - ON sat.id = sam.thread_id
245   - JOIN super_archives_mailinglist AS saml
246   - ON sat.mailinglist_id = saml.id
247   - GROUP BY
248   - sam.thread_id,
249   - sat.subject_token,
250   - sat.latest_message_id,
251   - saml.name">
252   -
253   - <!--
254   - Check about "DISTINCT ON" here:
255   - http://archives.postgresql.org/pgsql-general/2002-06/msg01330.php
256   - -->
257   - <entity name="first_message"
258   - dataSource="colab"
259   - transformer="TemplateTransformer"
260   - query="SELECT DISTINCT ON (sam.thread_id)
261   - sam.body AS description,
262   - sam.received_time AS created,
263   - sam.subject_clean AS subject,
264   - au.username AS creator
265   - FROM
266   - super_archives_message AS sam
267   - JOIN super_archives_emailaddress AS saea
268   - ON sam.from_address_id = saea.id
269   - LEFT JOIN auth_user AS au
270   - ON au.id = saea.user_id
271   - WHERE
272   - sam.thread_id = ${thread.id}
273   - ORDER BY
274   - sam.thread_id,
275   - sam.received_time">
276   - <field column="title" template="${first_message.subject}" />
277   - </entity>
278   -
279   - <entity name="latest_message"
280   - dataSource="colab"
281   - query="SELECT
282   - received_time AS modified
283   - FROM
284   - super_archives_message
285   - WHERE
286   - id = ${thread.latest_message_id}" />
287   -
288   - <entity name="thread_collaborators"
289   - dataSource="colab"
290   - query="SELECT DISTINCT
291   - au.username AS collaborator
292   - FROM
293   - super_archives_message AS sam
294   - JOIN super_archives_emailaddress AS saea
295   - ON sam.from_address_id = saea.id
296   - JOIN auth_user AS au
297   - ON au.id = saea.user_id
298   - WHERE
299   - thread_id = ${thread.id}" />
300   -
301   - <field column="uid" template="THREAD_${thread.id}" />
302   - <field column="type" template="thread" />
303   - <field column="created" name="created"
304   - dateTimeFormat="yyyy-MM-dd hh:mm:ss" />
305   - <field column="modified" name="modified"
306   - dateTimeFormat="yyyy-MM-dd hh:mm:ss" />
307   - </entity>
308   - </document>
309   -
310   -</dataConfig>
311   -
312   -<!--
313   -vim: ts=2 sw=2 ss=2 expandtab:
314   --->
sorl-conf/schema.xml
... ... @@ -1,499 +0,0 @@
1   -<?xml version="1.0" encoding="UTF-8" ?>
2   -<!--
3   - Licensed to the Apache Software Foundation (ASF) under one or more
4   - contributor license agreements. See the NOTICE file distributed with
5   - this work for additional information regarding copyright ownership.
6   - The ASF licenses this file to You under the Apache License, Version 2.0
7   - (the "License"); you may not use this file except in compliance with
8   - the License. You may obtain a copy of the License at
9   -
10   - http://www.apache.org/licenses/LICENSE-2.0
11   -
12   - Unless required by applicable law or agreed to in writing, software
13   - distributed under the License is distributed on an "AS IS" BASIS,
14   - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   - See the License for the specific language governing permissions and
16   - limitations under the License.
17   --->
18   -
19   -<!--
20   - This is the Solr schema file. This file should be named "schema.xml" and
21   - should be in the conf directory under the solr home
22   - (i.e. ./solr/conf/schema.xml by default)
23   - or located where the classloader for the Solr webapp can find it.
24   -
25   - This example schema is the recommended starting point for users.
26   - It should be kept correct and concise, usable out-of-the-box.
27   -
28   - For more information, on how to customize this file, please see
29   - http://wiki.apache.org/solr/SchemaXml
30   -
31   - PERFORMANCE NOTE: this schema includes many optional features and should not
32   - be used for benchmarking. To improve performance one could
33   - - set stored="false" for all fields possible (esp large fields) when you
34   - only need to search on the field but don't need to return the original
35   - value.
36   - - set indexed="false" if you don't need to search on the field, but only
37   - return the field as a result of searching on other indexed fields.
38   - - remove all unneeded copyField statements
39   - - for best index size and searching performance, set "index" to false
40   - for all general text fields, use copyField to copy them to the
41   - catchall "text" field, and use that for searching.
42   - - For maximum indexing performance, use the StreamingUpdateSolrServer
43   - java client.
44   - - Remember to run the JVM in server mode, and use a higher logging level
45   - that avoids logging every request
46   --->
47   -
48   -<schema name="example" version="1.2">
49   - <!-- attribute "name" is the name of this schema and is only used for display purposes.
50   - Applications should change this to reflect the nature of the search collection.
51   - version="1.2" is Solr's version number for the schema syntax and semantics. It should
52   - not normally be changed by applications.
53   - 1.0: multiValued attribute did not exist, all fields are multiValued by nature
54   - 1.1: multiValued attribute introduced, false by default
55   - 1.2: omitTermFreqAndPositions attribute introduced, true by default except for text fields.
56   - -->
57   -
58   - <types>
59   - <!-- field type definitions. The "name" attribute is
60   - just a label to be used by field definitions. The "class"
61   - attribute and any other attributes determine the real
62   - behavior of the fieldType.
63   - Class names starting with "solr" refer to java classes in the
64   - org.apache.solr.analysis package.
65   - -->
66   -
67   - <!-- The StrField type is not analyzed, but indexed/stored verbatim.
68   - - StrField and TextField support an optional compressThreshold which
69   - limits compression (if enabled in the derived fields) to values which
70   - exceed a certain size (in characters).
71   - -->
72   - <fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
73   -
74   - <!-- boolean type: "true" or "false" -->
75   - <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true" omitNorms="true"/>
76   - <!--Binary data type. The data should be sent/retrieved in as Base64 encoded Strings -->
77   - <fieldtype name="binary" class="solr.BinaryField"/>
78   -
79   - <!-- The optional sortMissingLast and sortMissingFirst attributes are
80   - currently supported on types that are sorted internally as strings.
81   - This includes "string","boolean","sint","slong","sfloat","sdouble","pdate"
82   - - If sortMissingLast="true", then a sort on this field will cause documents
83   - without the field to come after documents with the field,
84   - regardless of the requested sort order (asc or desc).
85   - - If sortMissingFirst="true", then a sort on this field will cause documents
86   - without the field to come before documents with the field,
87   - regardless of the requested sort order.
88   - - If sortMissingLast="false" and sortMissingFirst="false" (the default),
89   - then default lucene sorting will be used which places docs without the
90   - field first in an ascending sort and last in a descending sort.
91   - -->
92   -
93   - <!--
94   - Default numeric field types. For faster range queries, consider the tint/tfloat/tlong/tdouble types.
95   - -->
96   - <fieldType name="int" class="solr.TrieIntField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
97   - <fieldType name="float" class="solr.TrieFloatField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
98   - <fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
99   - <fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
100   -
101   - <!--
102   - Numeric field types that index each value at various levels of precision
103   - to accelerate range queries when the number of values between the range
104   - endpoints is large. See the javadoc for NumericRangeQuery for internal
105   - implementation details.
106   -
107   - Smaller precisionStep values (specified in bits) will lead to more tokens
108   - indexed per value, slightly larger index size, and faster range queries.
109   - A precisionStep of 0 disables indexing at different precision levels.
110   - -->
111   - <fieldType name="tint" class="solr.TrieIntField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
112   - <fieldType name="tfloat" class="solr.TrieFloatField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
113   - <fieldType name="tlong" class="solr.TrieLongField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
114   - <fieldType name="tdouble" class="solr.TrieDoubleField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
115   -
116   - <!-- The format for this date field is of the form 1995-12-31T23:59:59Z, and
117   - is a more restricted form of the canonical representation of dateTime
118   - http://www.w3.org/TR/xmlschema-2/#dateTime
119   - The trailing "Z" designates UTC time and is mandatory.
120   - Optional fractional seconds are allowed: 1995-12-31T23:59:59.999Z
121   - All other components are mandatory.
122   -
123   - Expressions can also be used to denote calculations that should be
124   - performed relative to "NOW" to determine the value, ie...
125   -
126   - NOW/HOUR
127   - ... Round to the start of the current hour
128   - NOW-1DAY
129   - ... Exactly 1 day prior to now
130   - NOW/DAY+6MONTHS+3DAYS
131   - ... 6 months and 3 days in the future from the start of
132   - the current day
133   -
134   - Consult the DateField javadocs for more information.
135   -
136   - Note: For faster range queries, consider the tdate type
137   - -->
138   - <fieldType name="date" class="solr.TrieDateField" omitNorms="true" precisionStep="0" positionIncrementGap="0"/>
139   -
140   - <!-- A Trie based date field for faster date range queries and date faceting. -->
141   - <fieldType name="tdate" class="solr.TrieDateField" omitNorms="true" precisionStep="6" positionIncrementGap="0"/>
142   -
143   -
144   - <!--
145   - Note:
146   - These should only be used for compatibility with existing indexes (created with older Solr versions)
147   - or if "sortMissingFirst" or "sortMissingLast" functionality is needed. Use Trie based fields instead.
148   -
149   - Plain numeric field types that store and index the text
150   - value verbatim (and hence don't support range queries, since the
151   - lexicographic ordering isn't equal to the numeric ordering)
152   - -->
153   - <fieldType name="pint" class="solr.IntField" omitNorms="true"/>
154   - <fieldType name="plong" class="solr.LongField" omitNorms="true"/>
155   - <fieldType name="pfloat" class="solr.FloatField" omitNorms="true"/>
156   - <fieldType name="pdouble" class="solr.DoubleField" omitNorms="true"/>
157   - <fieldType name="pdate" class="solr.DateField" sortMissingLast="true" omitNorms="true"/>
158   -
159   -
160   - <!--
161   - Note:
162   - These should only be used for compatibility with existing indexes (created with older Solr versions)
163   - or if "sortMissingFirst" or "sortMissingLast" functionality is needed. Use Trie based fields instead.
164   -
165   - Numeric field types that manipulate the value into
166   - a string value that isn't human-readable in its internal form,
167   - but with a lexicographic ordering the same as the numeric ordering,
168   - so that range queries work correctly.
169   - -->
170   - <fieldType name="sint" class="solr.SortableIntField" sortMissingLast="true" omitNorms="true"/>
171   - <fieldType name="slong" class="solr.SortableLongField" sortMissingLast="true" omitNorms="true"/>
172   - <fieldType name="sfloat" class="solr.SortableFloatField" sortMissingLast="true" omitNorms="true"/>
173   - <fieldType name="sdouble" class="solr.SortableDoubleField" sortMissingLast="true" omitNorms="true"/>
174   -
175   -
176   - <!-- The "RandomSortField" is not used to store or search any
177   - data. You can declare fields of this type it in your schema
178   - to generate pseudo-random orderings of your docs for sorting
179   - purposes. The ordering is generated based on the field name
180   - and the version of the index, As long as the index version
181   - remains unchanged, and the same field name is reused,
182   - the ordering of the docs will be consistent.
183   - If you want different psuedo-random orderings of documents,
184   - for the same version of the index, use a dynamicField and
185   - change the name
186   - -->
187   - <fieldType name="random" class="solr.RandomSortField" indexed="true" />
188   -
189   - <!-- solr.TextField allows the specification of custom text analyzers
190   - specified as a tokenizer and a list of token filters. Different
191   - analyzers may be specified for indexing and querying.
192   -
193   - The optional positionIncrementGap puts space between multiple fields of
194   - this type on the same document, with the purpose of preventing false phrase
195   - matching across fields.
196   -
197   - For more info on customizing your analyzer chain, please see
198   - http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters
199   - -->
200   -
201   - <!-- One can also specify an existing Analyzer class that has a
202   - default constructor via the class attribute on the analyzer element
203   - <fieldType name="text_greek" class="solr.TextField">
204   - <analyzer class="org.apache.lucene.analysis.el.GreekAnalyzer"/>
205   - </fieldType>
206   - -->
207   -
208   - <!-- A text field that only splits on whitespace for exact matching of words -->
209   - <fieldType name="text_ws" class="solr.TextField" positionIncrementGap="100">
210   - <analyzer>
211   - <tokenizer class="solr.WhitespaceTokenizerFactory"/>
212   - </analyzer>
213   - </fieldType>
214   -
215   - <!-- A text field that uses WordDelimiterFilter to enable splitting and matching of
216   - words on case-change, alpha numeric boundaries, and non-alphanumeric chars,
217   - so that a query of "wifi" or "wi fi" could match a document containing "Wi-Fi".
218   - Synonyms and stopwords are customized by external files, and stemming is enabled.
219   - -->
220   - <fieldType name="text" class="solr.TextField" positionIncrementGap="100">
221   - <analyzer type="index">
222   - <tokenizer class="solr.WhitespaceTokenizerFactory"/>
223   - <!-- in this example, we will only use synonyms at query time
224   - <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
225   - -->
226   - <!-- Case insensitive stop word removal.
227   - add enablePositionIncrements=true in both the index and query
228   - analyzers to leave a 'gap' for more accurate phrase queries.
229   - -->
230   - <filter class="solr.StopFilterFactory"
231   - ignoreCase="true"
232   - words="stopwords.txt"
233   - enablePositionIncrements="true"
234   - />
235   - <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1"/>
236   - <filter class="solr.LowerCaseFilterFactory"/>
237   - <filter class="solr.SnowballPorterFilterFactory" language="Portuguese" protected="protwords.txt"/>
238   - </analyzer>
239   - <analyzer type="query">
240   - <tokenizer class="solr.WhitespaceTokenizerFactory"/>
241   - <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
242   - <filter class="solr.StopFilterFactory"
243   - ignoreCase="true"
244   - words="stopwords.txt"
245   - enablePositionIncrements="true"
246   - />
247   - <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
248   - <filter class="solr.LowerCaseFilterFactory"/>
249   - <filter class="solr.SnowballPorterFilterFactory" language="Portuguese" protected="protwords.txt"/>
250   - </analyzer>
251   - </fieldType>
252   -
253   -
254   - <!-- Less flexible matching, but less false matches. Probably not ideal for product names,
255   - but may be good for SKUs. Can insert dashes in the wrong place and still match. -->
256   - <fieldType name="textTight" class="solr.TextField" positionIncrementGap="100" >
257   - <analyzer>
258   - <tokenizer class="solr.WhitespaceTokenizerFactory"/>
259   - <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="false"/>
260   - <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
261   - <filter class="solr.WordDelimiterFilterFactory" generateWordParts="0" generateNumberParts="0" catenateWords="1" catenateNumbers="1" catenateAll="0"/>
262   - <filter class="solr.LowerCaseFilterFactory"/>
263   - <filter class="solr.SnowballPorterFilterFactory" language="Portuguese" protected="protwords.txt"/>
264   - <!-- this filter can remove any duplicate tokens that appear at the same position - sometimes
265   - possible with WordDelimiterFilter in conjuncton with stemming. -->
266   - <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
267   - </analyzer>
268   - </fieldType>
269   -
270   -
271   - <!-- A general unstemmed text field - good if one does not know the language of the field -->
272   - <fieldType name="textgen" class="solr.TextField" positionIncrementGap="100">
273   - <analyzer type="index">
274   - <tokenizer class="solr.WhitespaceTokenizerFactory"/>
275   - <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
276   - <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="0"/>
277   - <filter class="solr.LowerCaseFilterFactory"/>
278   - </analyzer>
279   - <analyzer type="query">
280   - <tokenizer class="solr.WhitespaceTokenizerFactory"/>
281   - <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
282   - <filter class="solr.StopFilterFactory"
283   - ignoreCase="true"
284   - words="stopwords.txt"
285   - enablePositionIncrements="true"
286   - />
287   - <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="0"/>
288   - <filter class="solr.LowerCaseFilterFactory"/>
289   - </analyzer>
290   - </fieldType>
291   -
292   -
293   - <!-- A general unstemmed text field that indexes tokens normally and also
294   - reversed (via ReversedWildcardFilterFactory), to enable more efficient
295   - leading wildcard queries. -->
296   - <fieldType name="text_rev" class="solr.TextField" positionIncrementGap="100">
297   - <analyzer type="index">
298   - <tokenizer class="solr.WhitespaceTokenizerFactory"/>
299   - <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
300   - <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="0"/>
301   - <filter class="solr.LowerCaseFilterFactory"/>
302   - <filter class="solr.ReversedWildcardFilterFactory" withOriginal="true"
303   - maxPosAsterisk="3" maxPosQuestion="2" maxFractionAsterisk="0.33"/>
304   - </analyzer>
305   - <analyzer type="query">
306   - <tokenizer class="solr.WhitespaceTokenizerFactory"/>
307   - <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
308   - <filter class="solr.StopFilterFactory"
309   - ignoreCase="true"
310   - words="stopwords.txt"
311   - enablePositionIncrements="true"
312   - />
313   - <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="0"/>
314   - <filter class="solr.LowerCaseFilterFactory"/>
315   - </analyzer>
316   - </fieldType>
317   -
318   - <!-- charFilter + WhitespaceTokenizer -->
319   - <!--
320   - <fieldType name="textCharNorm" class="solr.TextField" positionIncrementGap="100" >
321   - <analyzer>
322   - <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/>
323   - <tokenizer class="solr.WhitespaceTokenizerFactory"/>
324   - </analyzer>
325   - </fieldType>
326   - -->
327   -
328   - <!-- This is an example of using the KeywordTokenizer along
329   - With various TokenFilterFactories to produce a sortable field
330   - that does not include some properties of the source text
331   - -->
332   - <fieldType name="alphaOnlySort" class="solr.TextField" sortMissingLast="true" omitNorms="true">
333   - <analyzer>
334   - <!-- KeywordTokenizer does no actual tokenizing, so the entire
335   - input string is preserved as a single token
336   - -->
337   - <tokenizer class="solr.KeywordTokenizerFactory"/>
338   - <!-- The LowerCase TokenFilter does what you expect, which can be
339   - when you want your sorting to be case insensitive
340   - -->
341   - <filter class="solr.LowerCaseFilterFactory" />
342   - <!-- The TrimFilter removes any leading or trailing whitespace -->
343   - <filter class="solr.TrimFilterFactory" />
344   - <!-- The PatternReplaceFilter gives you the flexibility to use
345   - Java Regular expression to replace any sequence of characters
346   - matching a pattern with an arbitrary replacement string,
347   - which may include back references to portions of the original
348   - string matched by the pattern.
349   -
350   - See the Java Regular Expression documentation for more
351   - information on pattern and replacement string syntax.
352   -
353   - http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/package-summary.html
354   - -->
355   - <filter class="solr.PatternReplaceFilterFactory"
356   - pattern="([^a-z])" replacement="" replace="all"
357   - />
358   - </analyzer>
359   - </fieldType>
360   -
361   - <fieldtype name="phonetic" stored="false" indexed="true" class="solr.TextField" >
362   - <analyzer>
363   - <tokenizer class="solr.StandardTokenizerFactory"/>
364   - <filter class="solr.DoubleMetaphoneFilterFactory" inject="false"/>
365   - </analyzer>
366   - </fieldtype>
367   -
368   - <fieldtype name="payloads" stored="false" indexed="true" class="solr.TextField" >
369   - <analyzer>
370   - <tokenizer class="solr.WhitespaceTokenizerFactory"/>
371   - <!--
372   - The DelimitedPayloadTokenFilter can put payloads on tokens... for example,
373   - a token of "foo|1.4" would be indexed as "foo" with a payload of 1.4f
374   - Attributes of the DelimitedPayloadTokenFilterFactory :
375   - "delimiter" - a one character delimiter. Default is | (pipe)
376   - "encoder" - how to encode the following value into a playload
377   - float -> org.apache.lucene.analysis.payloads.FloatEncoder,
378   - integer -> o.a.l.a.p.IntegerEncoder
379   - identity -> o.a.l.a.p.IdentityEncoder
380   - Fully Qualified class name implementing PayloadEncoder, Encoder must have a no arg constructor.
381   - -->
382   - <filter class="solr.DelimitedPayloadTokenFilterFactory" encoder="float"/>
383   - </analyzer>
384   - </fieldtype>
385   -
386   - <!-- lowercases the entire field value, keeping it as a single token. -->
387   - <fieldType name="lowercase" class="solr.TextField" positionIncrementGap="100">
388   - <analyzer>
389   - <tokenizer class="solr.KeywordTokenizerFactory"/>
390   - <filter class="solr.LowerCaseFilterFactory" />
391   - </analyzer>
392   - </fieldType>
393   -
394   -
395   - <!-- since fields of this type are by default not stored or indexed,
396   - any data added to them will be ignored outright. -->
397   - <fieldtype name="ignored" stored="false" indexed="false" multiValued="true" class="solr.StrField" />
398   -
399   - </types>
400   -
401   -
402   - <fields>
403   - <!-- Valid attributes for fields:
404   - name: mandatory - the name for the field
405   - type: mandatory - the name of a previously defined type from the
406   - <types> section
407   - indexed: true if this field should be indexed (searchable or sortable)
408   - stored: true if this field should be retrievable
409   - compressed: [false] if this field should be stored using gzip compression
410   - (this will only apply if the field type is compressable; among
411   - the standard field types, only TextField and StrField are)
412   - multiValued: true if this field may contain multiple values per document
413   - omitNorms: (expert) set to true to omit the norms associated with
414   - this field (this disables length normalization and index-time
415   - boosting for the field, and saves some memory). Only full-text
416   - fields or fields that need an index-time boost need norms.
417   - termVectors: [false] set to true to store the term vector for a
418   - given field.
419   - When using MoreLikeThis, fields used for similarity should be
420   - stored for best performance.
421   - termPositions: Store position information with the term vector.
422   - This will increase storage costs.
423   - termOffsets: Store offset information with the term vector. This
424   - will increase storage costs.
425   - default: a value that should be used if no value is specified
426   - when adding a document.
427   - -->
428   -
429   - <!-- Base fields (all should be indexed and stored)-->
430   - <field name="uid" type="string" indexed="true" stored="true" required="true" />
431   - <field name="id" type="string" indexed="true" stored="true" required="true" />
432   - <field name="type" type="string" indexed="true" stored="true" required="true" />
433   - <field name="title" type="text" indexed="true" stored="true" required="true" />
434   - <field name="description" type="text" indexed="true" stored="true" />
435   - <field name="creator" type="string" indexed="true" stored="true" />
436   - <field name="created" type="date" indexed="true" stored="true" />
437   - <field name="modified" type="date" indexed="true" stored="true" />
438   -
439   - <!-- All next fields shoult NOT be stored -->
440   - <field name="collaborator" type="textgen" indexed="true" stored="false" multiValued="true" />
441   - <field name="name" type="string" indexed="true" stored="false" />
442   - <field name="content" type="text" indexed="true" stored="false" />
443   - <field name="comment" type="text" indexed="true" stored="false" multiValued="true" />
444   - <field name="keyword" type="text" indexed="true" stored="false" multiValued="true" />
445   - <field name="milestone" type="string" indexed="true" stored="false" />
446   - <field name="priority" type="string" indexed="true" stored="false" />
447   - <field name="component" type="string" indexed="true" stored="false" />
448   - <field name="version" type="string" indexed="true" stored="false" />
449   - <field name="severity" type="string" indexed="true" stored="false" />
450   - <field name="reporter" type="textgen" indexed="true" stored="false" />
451   - <field name="owner" type="textgen" indexed="true" stored="false" />
452   - <field name="status" type="string" indexed="true" stored="false" />
453   - <field name="subject" type="text" indexed="true" stored="false" />
454   - <field name="revision" type="int" indexed="true" stored="false" />
455   - <field name="mailinglist" type="textgen" indexed="true" stored="false" />
456   -
457   - <!-- catchall field, containing all other searchable text fields (implemented
458   - via copyField further on in this schema -->
459   - <field name="default" type="textgen" indexed="true" stored="false" multiValued="true"/>
460   - </fields>
461   -
462   - <!-- Field to use to determine and enforce document uniqueness.
463   - Unless this field is marked with required="false", it will be a required field
464   - -->
465   - <uniqueKey>uid</uniqueKey>
466   -
467   - <!-- field for the QueryParser to use when an explicit fieldname is absent -->
468   - <defaultSearchField>default</defaultSearchField>
469   -
470   - <!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
471   - <solrQueryParser defaultOperator="OR"/>
472   -
473   - <!-- copyField commands copy one field to another at the time a document
474   - is added to the index. It's used either to index the same field differently,
475   - or to add multiple fields to the same field for easier/faster searching. -->
476   -
477   - <copyField source="content" dest="default" />
478   - <copyField source="title" dest="default" />
479   - <copyField source="description" dest="default" />
480   - <copyField source="creator" dest="default" />
481   - <copyField source="collaborator" dest="default" />
482   - <copyField source="comment" dest="default" />
483   - <copyField source="keyword" dest="default" />
484   -
485   -<!-- Similarity is the scoring routine for each document vs. a query.
486   - A custom similarity may be specified here, but the default is fine
487   - for most applications. -->
488   - <!-- <similarity class="org.apache.lucene.search.DefaultSimilarity"/> -->
489   - <!-- ... OR ...
490   - Specify a SimilarityFactory class name implementation
491   - allowing parameters to be used.
492   - -->
493   - <!--
494   - <similarity class="com.example.solr.CustomSimilarityFactory">
495   - <str name="paramkey">param value</str>
496   - </similarity>
497   - -->
498   -
499   -</schema>
sorl-conf/solrconfig.xml
... ... @@ -1,1040 +0,0 @@
1   -<?xml version="1.0" encoding="UTF-8" ?>
2   -<!--
3   - Licensed to the Apache Software Foundation (ASF) under one or more
4   - contributor license agreements. See the NOTICE file distributed with
5   - this work for additional information regarding copyright ownership.
6   - The ASF licenses this file to You under the Apache License, Version 2.0
7   - (the "License"); you may not use this file except in compliance with
8   - the License. You may obtain a copy of the License at
9   -
10   - http://www.apache.org/licenses/LICENSE-2.0
11   -
12   - Unless required by applicable law or agreed to in writing, software
13   - distributed under the License is distributed on an "AS IS" BASIS,
14   - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   - See the License for the specific language governing permissions and
16   - limitations under the License.
17   --->
18   -<!--
19   - For more details about configurations options that may appear in this
20   - file, see http://wiki.apache.org/solr/SolrConfigXml.
21   -
22   - Specifically, the Solr Config can support XInclude, which may make it easier to manage
23   - the configuration. See https://issues.apache.org/jira/browse/SOLR-1167
24   --->
25   -<config>
26   - <!-- Set this to 'false' if you want solr to continue working after it has
27   - encountered an severe configuration error. In a production environment,
28   - you may want solr to keep working even if one handler is mis-configured.
29   -
30   - You may also set this to false using by setting the system property:
31   - -Dsolr.abortOnConfigurationError=false
32   - -->
33   - <abortOnConfigurationError>${solr.abortOnConfigurationError:true}</abortOnConfigurationError>
34   -
35   - <!-- lib directives can be used to instruct Solr to load an Jars identified
36   - and use them to resolve any "plugins" specified in your solrconfig.xml or
37   - schema.xml (ie: Analyzers, Request Handlers, etc...).
38   -
39   - All directories and paths are resolved relative the instanceDir.
40   -
41   - If a "./lib" directory exists in your instanceDir, all files found in it
42   - are included as if you had used the following syntax...
43   -
44   - <lib dir="./lib" />
45   - -->
46   - <!-- A dir option by itself adds any files found in the directory to the
47   - classpath, this is useful for including all jars in a directory.
48   - -->
49   - <!--lib dir="../../contrib/extraction/lib" /-->
50   - <!-- When a regex is specified in addition to a directory, only the files in that
51   - directory which completely match the regex (anchored on both ends)
52   - will be included.
53   - -->
54   - <!--lib dir="../../dist/" regex="apache-solr-cell-\d.*\.jar" />
55   - <lib dir="../../dist/" regex="apache-solr-clustering-\d.*\.jar" /-->
56   - <!-- If a dir option (with or without a regex) is used and nothing is found
57   - that matches, it will be ignored
58   - -->
59   - <!--lib dir="../../contrib/clustering/lib/downloads/" />
60   - <lib dir="../../contrib/clustering/lib/" />
61   - <lib dir="/total/crap/dir/ignored" /-->
62   - <!-- an exact path can be used to specify a specific file. This will cause
63   - a serious error to be logged if it can't be loaded.
64   - <lib path="../a-jar-that-does-not-exist.jar" />
65   - -->
66   -
67   -
68   - <!-- Used to specify an alternate directory to hold all index data
69   - other than the default ./data under the Solr home.
70   - If replication is in use, this should match the replication configuration. -->
71   - <dataDir>/var/lib/solr/data</dataDir>
72   -
73   -
74   - <!-- WARNING: this <indexDefaults> section only provides defaults for index writers
75   - in general. See also the <mainIndex> section after that when changing parameters
76   - for Solr's main Lucene index. -->
77   - <indexDefaults>
78   - <!-- Values here affect all index writers and act as a default unless overridden. -->
79   - <useCompoundFile>false</useCompoundFile>
80   -
81   - <mergeFactor>10</mergeFactor>
82   - <!-- If both ramBufferSizeMB and maxBufferedDocs is set, then Lucene will flush
83   - based on whichever limit is hit first. -->
84   - <!--<maxBufferedDocs>1000</maxBufferedDocs>-->
85   -
86   - <!-- Sets the amount of RAM that may be used by Lucene indexing
87   - for buffering added documents and deletions before they are
88   - flushed to the Directory. -->
89   - <ramBufferSizeMB>32</ramBufferSizeMB>
90   - <!-- <maxMergeDocs>2147483647</maxMergeDocs> -->
91   - <maxFieldLength>10000</maxFieldLength>
92   - <writeLockTimeout>1000</writeLockTimeout>
93   - <commitLockTimeout>10000</commitLockTimeout>
94   -
95   - <!--
96   - Expert: Turn on Lucene's auto commit capability. This causes intermediate
97   - segment flushes to write a new lucene index descriptor, enabling it to be
98   - opened by an external IndexReader. This can greatly slow down indexing
99   - speed. NOTE: Despite the name, this value does not have any relation to
100   - Solr's autoCommit functionality
101   - -->
102   - <!--<luceneAutoCommit>false</luceneAutoCommit>-->
103   -
104   - <!--
105   - Expert: The Merge Policy in Lucene controls how merging is handled by
106   - Lucene. The default in 2.3 is the LogByteSizeMergePolicy, previous
107   - versions used LogDocMergePolicy.
108   -
109   - LogByteSizeMergePolicy chooses segments to merge based on their size. The
110   - Lucene 2.2 default, LogDocMergePolicy chose when to merge based on number
111   - of documents
112   -
113   - Other implementations of MergePolicy must have a no-argument constructor
114   - -->
115   - <!--<mergePolicy class="org.apache.lucene.index.LogByteSizeMergePolicy"/>-->
116   -
117   - <!--
118   - Expert:
119   - The Merge Scheduler in Lucene controls how merges are performed. The
120   - ConcurrentMergeScheduler (Lucene 2.3 default) can perform merges in the
121   - background using separate threads. The SerialMergeScheduler (Lucene 2.2
122   - default) does not.
123   - -->
124   - <!--<mergeScheduler class="org.apache.lucene.index.ConcurrentMergeScheduler"/>-->
125   -
126   -
127   - <!--
128   - This option specifies which Lucene LockFactory implementation to use.
129   -
130   - single = SingleInstanceLockFactory - suggested for a read-only index
131   - or when there is no possibility of another process trying
132   - to modify the index.
133   - native = NativeFSLockFactory - uses OS native file locking
134   - simple = SimpleFSLockFactory - uses a plain file for locking
135   -
136   - (For backwards compatibility with Solr 1.2, 'simple' is the default
137   - if not specified.)
138   - -->
139   - <lockType>native</lockType>
140   - <!--
141   - Expert:
142   - Controls how often Lucene loads terms into memory -->
143   - <!--<termIndexInterval>256</termIndexInterval>-->
144   - </indexDefaults>
145   -
146   - <mainIndex>
147   - <!-- options specific to the main on-disk lucene index -->
148   - <useCompoundFile>false</useCompoundFile>
149   - <ramBufferSizeMB>32</ramBufferSizeMB>
150   - <mergeFactor>10</mergeFactor>
151   - <!-- Deprecated -->
152   - <!--<maxBufferedDocs>1000</maxBufferedDocs>-->
153   - <!--<maxMergeDocs>2147483647</maxMergeDocs>-->
154   -
155   - <!-- inherit from indexDefaults <maxFieldLength>10000</maxFieldLength> -->
156   -
157   - <!-- If true, unlock any held write or commit locks on startup.
158   - This defeats the locking mechanism that allows multiple
159   - processes to safely access a lucene index, and should be
160   - used with care.
161   - This is not needed if lock type is 'none' or 'single'
162   - -->
163   - <unlockOnStartup>false</unlockOnStartup>
164   -
165   - <!-- If true, IndexReaders will be reopened (often more efficient) instead
166   - of closed and then opened. -->
167   - <reopenReaders>true</reopenReaders>
168   -
169   - <!--
170   - Expert:
171   - Controls how often Lucene loads terms into memory. Default is 128 and is likely good for most everyone. -->
172   - <!--<termIndexInterval>256</termIndexInterval>-->
173   -
174   - <!--
175   - Custom deletion policies can specified here. The class must
176   - implement org.apache.lucene.index.IndexDeletionPolicy.
177   -
178   - http://lucene.apache.org/java/2_3_2/api/org/apache/lucene/index/IndexDeletionPolicy.html
179   -
180   - The standard Solr IndexDeletionPolicy implementation supports deleting
181   - index commit points on number of commits, age of commit point and
182   - optimized status.
183   -
184   - The latest commit point should always be preserved regardless
185   - of the criteria.
186   - -->
187   - <deletionPolicy class="solr.SolrDeletionPolicy">
188   - <!-- The number of commit points to be kept -->
189   - <str name="maxCommitsToKeep">1</str>
190   - <!-- The number of optimized commit points to be kept -->
191   - <str name="maxOptimizedCommitsToKeep">0</str>
192   - <!--
193   - Delete all commit points once they have reached the given age.
194   - Supports DateMathParser syntax e.g.
195   -
196   - <str name="maxCommitAge">30MINUTES</str>
197   - <str name="maxCommitAge">1DAY</str>
198   - -->
199   - </deletionPolicy>
200   -
201   - <!-- To aid in advanced debugging, you may turn on IndexWriter debug logging.
202   - Setting to true will set the file that the underlying Lucene IndexWriter
203   - will write its debug infostream to. -->
204   - <infoStream file="INFOSTREAM.txt">false</infoStream>
205   -
206   - </mainIndex>
207   -
208   - <!-- Enables JMX if and only if an existing MBeanServer is found, use this
209   - if you want to configure JMX through JVM parameters. Remove this to disable
210   - exposing Solr configuration and statistics to JMX.
211   -
212   - If you want to connect to a particular server, specify the agentId
213   - e.g. <jmx agentId="myAgent" />
214   -
215   - If you want to start a new MBeanServer, specify the serviceUrl
216   - e.g <jmx serviceUrl="service:jmx:rmi:///jndi/rmi://localhost:9999/solr"/>
217   -
218   - For more details see http://wiki.apache.org/solr/SolrJmx
219   - -->
220   - <jmx />
221   -
222   - <!-- the default high-performance update handler -->
223   - <updateHandler class="solr.DirectUpdateHandler2">
224   - <!-- A prefix of "solr." for class names is an alias that
225   - causes solr to search appropriate packages, including
226   - org.apache.solr.(search|update|request|core|analysis)
227   - -->
228   -
229   - <!-- Perform a <commit/> automatically under certain conditions:
230   - maxDocs - number of updates since last commit is greater than this
231   - maxTime - oldest uncommited update (in ms) is this long ago
232   - Instead of enabling autoCommit, consider using "commitWithin"
233   - when adding documents. http://wiki.apache.org/solr/UpdateXmlMessages
234   - <autoCommit>
235   - <maxDocs>10000</maxDocs>
236   - <maxTime>1000</maxTime>
237   - </autoCommit>
238   - -->
239   -
240   -
241   - <!-- The RunExecutableListener executes an external command from a
242   - hook such as postCommit or postOptimize.
243   - exe - the name of the executable to run
244   - dir - dir to use as the current working directory. default="."
245   - wait - the calling thread waits until the executable returns. default="true"
246   - args - the arguments to pass to the program. default=nothing
247   - env - environment variables to set. default=nothing
248   - -->
249   - <!-- A postCommit event is fired after every commit or optimize command
250   - <listener event="postCommit" class="solr.RunExecutableListener">
251   - <str name="exe">solr/bin/snapshooter</str>
252   - <str name="dir">.</str>
253   - <bool name="wait">true</bool>
254   - <arr name="args"> <str>arg1</str> <str>arg2</str> </arr>
255   - <arr name="env"> <str>MYVAR=val1</str> </arr>
256   - </listener>
257   - -->
258   - <!-- A postOptimize event is fired only after every optimize command
259   - <listener event="postOptimize" class="solr.RunExecutableListener">
260   - <str name="exe">snapshooter</str>
261   - <str name="dir">solr/bin</str>
262   - <bool name="wait">true</bool>
263   - </listener>
264   - -->
265   -
266   - </updateHandler>
267   -
268   - <!-- Use the following format to specify a custom IndexReaderFactory - allows for alternate
269   - IndexReader implementations.
270   -
271   - ** Experimental Feature **
272   - Please note - Using a custom IndexReaderFactory may prevent certain other features
273   - from working. The API to IndexReaderFactory may change without warning or may even
274   - be removed from future releases if the problems cannot be resolved.
275   -
276   - ** Features that may not work with custom IndexReaderFactory **
277   - The ReplicationHandler assumes a disk-resident index. Using a custom
278   - IndexReader implementation may cause incompatibility with ReplicationHandler and
279   - may cause replication to not work correctly. See SOLR-1366 for details.
280   -
281   - <indexReaderFactory name="IndexReaderFactory" class="package.class">
282   - Parameters as required by the implementation
283   - </indexReaderFactory >
284   - -->
285   - <!-- To set the termInfosIndexDivisor, do this: -->
286   - <!--<indexReaderFactory name="IndexReaderFactory" class="org.apache.solr.core.StandardIndexReaderFactory">
287   - <int name="termInfosIndexDivisor">12</int>
288   - </indexReaderFactory >-->
289   -
290   -
291   - <query>
292   - <!-- Maximum number of clauses in a boolean query... in the past, this affected
293   - range or prefix queries that expanded to big boolean queries - built in Solr
294   - query parsers no longer create queries with this limitation.
295   - An exception is thrown if exceeded. -->
296   - <maxBooleanClauses>1024</maxBooleanClauses>
297   -
298   -
299   - <!-- There are two implementations of cache available for Solr,
300   - LRUCache, based on a synchronized LinkedHashMap, and
301   - FastLRUCache, based on a ConcurrentHashMap. FastLRUCache has faster gets
302   - and slower puts in single threaded operation and thus is generally faster
303   - than LRUCache when the hit ratio of the cache is high (> 75%), and may be
304   - faster under other scenarios on multi-cpu systems. -->
305   - <!-- Cache used by SolrIndexSearcher for filters (DocSets),
306   - unordered sets of *all* documents that match a query.
307   - When a new searcher is opened, its caches may be prepopulated
308   - or "autowarmed" using data from caches in the old searcher.
309   - autowarmCount is the number of items to prepopulate. For LRUCache,
310   - the autowarmed items will be the most recently accessed items.
311   - Parameters:
312   - class - the SolrCache implementation LRUCache or FastLRUCache
313   - size - the maximum number of entries in the cache
314   - initialSize - the initial capacity (number of entries) of
315   - the cache. (seel java.util.HashMap)
316   - autowarmCount - the number of entries to prepopulate from
317   - and old cache.
318   - -->
319   - <filterCache
320   - class="solr.FastLRUCache"
321   - size="512"
322   - initialSize="512"
323   - autowarmCount="0"/>
324   -
325   - <!-- Cache used to hold field values that are quickly accessible
326   - by document id. The fieldValueCache is created by default
327   - even if not configured here.
328   - <fieldValueCache
329   - class="solr.FastLRUCache"
330   - size="512"
331   - autowarmCount="128"
332   - showItems="32"
333   - />
334   - -->
335   -
336   - <!-- queryResultCache caches results of searches - ordered lists of
337   - document ids (DocList) based on a query, a sort, and the range
338   - of documents requested. -->
339   - <queryResultCache
340   - class="solr.LRUCache"
341   - size="512"
342   - initialSize="512"
343   - autowarmCount="0"/>
344   -
345   - <!-- documentCache caches Lucene Document objects (the stored fields for each document).
346   - Since Lucene internal document ids are transient, this cache will not be autowarmed. -->
347   - <documentCache
348   - class="solr.LRUCache"
349   - size="512"
350   - initialSize="512"
351   - autowarmCount="0"/>
352   -
353   - <!-- If true, stored fields that are not requested will be loaded lazily.
354   - This can result in a significant speed improvement if the usual case is to
355   - not load all stored fields, especially if the skipped fields are large
356   - compressed text fields.
357   - -->
358   - <enableLazyFieldLoading>true</enableLazyFieldLoading>
359   -
360   - <!-- Example of a generic cache. These caches may be accessed by name
361   - through SolrIndexSearcher.getCache(),cacheLookup(), and cacheInsert().
362   - The purpose is to enable easy caching of user/application level data.
363   - The regenerator argument should be specified as an implementation
364   - of solr.search.CacheRegenerator if autowarming is desired. -->
365   - <!--
366   - <cache name="myUserCache"
367   - class="solr.LRUCache"
368   - size="4096"
369   - initialSize="1024"
370   - autowarmCount="1024"
371   - regenerator="org.mycompany.mypackage.MyRegenerator"
372   - />
373   - -->
374   -
375   - <!-- An optimization that attempts to use a filter to satisfy a search.
376   - If the requested sort does not include score, then the filterCache
377   - will be checked for a filter matching the query. If found, the filter
378   - will be used as the source of document ids, and then the sort will be
379   - applied to that.
380   - <useFilterForSortedQuery>true</useFilterForSortedQuery>
381   - -->
382   -
383   - <!-- An optimization for use with the queryResultCache. When a search
384   - is requested, a superset of the requested number of document ids
385   - are collected. For example, if a search for a particular query
386   - requests matching documents 10 through 19, and queryWindowSize is 50,
387   - then documents 0 through 49 will be collected and cached. Any further
388   - requests in that range can be satisfied via the cache. -->
389   - <queryResultWindowSize>20</queryResultWindowSize>
390   -
391   - <!-- Maximum number of documents to cache for any entry in the
392   - queryResultCache. -->
393   - <queryResultMaxDocsCached>200</queryResultMaxDocsCached>
394   -
395   - <!-- a newSearcher event is fired whenever a new searcher is being prepared
396   - and there is a current searcher handling requests (aka registered).
397   - It can be used to prime certain caches to prevent long request times for
398   - certain requests.
399   - -->
400   - <!-- QuerySenderListener takes an array of NamedList and executes a
401   - local query request for each NamedList in sequence. -->
402   - <listener event="newSearcher" class="solr.QuerySenderListener">
403   - <arr name="queries">
404   - <!--
405   - <lst> <str name="q">solr</str> <str name="start">0</str> <str name="rows">10</str> </lst>
406   - <lst> <str name="q">rocks</str> <str name="start">0</str> <str name="rows">10</str> </lst>
407   - <lst><str name="q">static newSearcher warming query from solrconfig.xml</str></lst>
408   - -->
409   - </arr>
410   - </listener>
411   -
412   - <!-- a firstSearcher event is fired whenever a new searcher is being
413   - prepared but there is no current registered searcher to handle
414   - requests or to gain autowarming data from. -->
415   - <listener event="firstSearcher" class="solr.QuerySenderListener">
416   - <arr name="queries">
417   - <lst> <str name="q">solr rocks</str><str name="start">0</str><str name="rows">10</str></lst>
418   - <lst><str name="q">static firstSearcher warming query from solrconfig.xml</str></lst>
419   - </arr>
420   - </listener>
421   -
422   - <!-- If a search request comes in and there is no current registered searcher,
423   - then immediately register the still warming searcher and use it. If
424   - "false" then all requests will block until the first searcher is done
425   - warming. -->
426   - <useColdSearcher>false</useColdSearcher>
427   -
428   - <!-- Maximum number of searchers that may be warming in the background
429   - concurrently. An error is returned if this limit is exceeded. Recommend
430   - 1-2 for read-only slaves, higher for masters w/o cache warming. -->
431   - <maxWarmingSearchers>2</maxWarmingSearchers>
432   -
433   - </query>
434   -
435   - <!--
436   - Let the dispatch filter handler /select?qt=XXX
437   - handleSelect=true will use consistent error handling for /select and /update
438   - handleSelect=false will use solr1.1 style error formatting
439   - -->
440   - <requestDispatcher handleSelect="true" >
441   - <!--Make sure your system has some authentication before enabling remote streaming! -->
442   - <requestParsers enableRemoteStreaming="true" multipartUploadLimitInKB="2048000" />
443   -
444   - <!-- Set HTTP caching related parameters (for proxy caches and clients).
445   -
446   - To get the behaviour of Solr 1.2 (ie: no caching related headers)
447   - use the never304="true" option and do not specify a value for
448   - <cacheControl>
449   - -->
450   - <!-- <httpCaching never304="true"> -->
451   - <httpCaching lastModifiedFrom="openTime"
452   - etagSeed="Solr">
453   - <!-- lastModFrom="openTime" is the default, the Last-Modified value
454   - (and validation against If-Modified-Since requests) will all be
455   - relative to when the current Searcher was opened.
456   - You can change it to lastModFrom="dirLastMod" if you want the
457   - value to exactly corrispond to when the physical index was last
458   - modified.
459   -
460   - etagSeed="..." is an option you can change to force the ETag
461   - header (and validation against If-None-Match requests) to be
462   - differnet even if the index has not changed (ie: when making
463   - significant changes to your config file)
464   -
465   - lastModifiedFrom and etagSeed are both ignored if you use the
466   - never304="true" option.
467   - -->
468   - <!-- If you include a <cacheControl> directive, it will be used to
469   - generate a Cache-Control header, as well as an Expires header
470   - if the value contains "max-age="
471   -
472   - By default, no Cache-Control header is generated.
473   -
474   - You can use the <cacheControl> option even if you have set
475   - never304="true"
476   - -->
477   - <!-- <cacheControl>max-age=30, public</cacheControl> -->
478   - </httpCaching>
479   - </requestDispatcher>
480   -
481   -
482   - <!-- requestHandler plugins... incoming queries will be dispatched to the
483   - correct handler based on the path or the qt (query type) param.
484   - Names starting with a '/' are accessed with the a path equal to the
485   - registered name. Names without a leading '/' are accessed with:
486   - http://host/app/select?qt=name
487   - If no qt is defined, the requestHandler that declares default="true"
488   - will be used.
489   - -->
490   - <requestHandler name="standard" class="solr.SearchHandler" default="true">
491   - <!-- default values for query parameters -->
492   - <lst name="defaults">
493   - <str name="echoParams">explicit</str>
494   - <!--
495   - <int name="rows">10</int>
496   - <str name="fl">*</str>
497   - <str name="version">2.1</str>
498   - -->
499   - </lst>
500   - </requestHandler>
501   -
502   -<!-- Please refer to http://wiki.apache.org/solr/SolrReplication for details on configuring replication -->
503   -<!-- remove the <lst name="master"> section if this is just a slave -->
504   -<!-- remove the <lst name="slave"> section if this is just a master -->
505   -<!--
506   -<requestHandler name="/replication" class="solr.ReplicationHandler" >
507   - <lst name="master">
508   - <str name="replicateAfter">commit</str>
509   - <str name="replicateAfter">startup</str>
510   - <str name="confFiles">schema.xml,stopwords.txt</str>
511   - </lst>
512   - <lst name="slave">
513   - <str name="masterUrl">http://localhost:8983/solr/replication</str>
514   - <str name="pollInterval">00:00:60</str>
515   - </lst>
516   -</requestHandler>-->
517   -
518   - <!-- DisMaxRequestHandler allows easy searching across multiple fields
519   - for simple user-entered phrases. It's implementation is now
520   - just the standard SearchHandler with a default query type
521   - of "dismax".
522   - see http://wiki.apache.org/solr/DisMaxRequestHandler
523   - -->
524   - <requestHandler name="dismax" class="solr.SearchHandler" >
525   - <lst name="defaults">
526   - <str name="defType">dismax</str>
527   - <str name="echoParams">explicit</str>
528   - <float name="tie">0.01</float>
529   - <str name="qf">
530   - text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4
531   - </str>
532   - <str name="pf">
533   - text^0.2 features^1.1 name^1.5 manu^1.4 manu_exact^1.9
534   - </str>
535   - <str name="bf">
536   - popularity^0.5 recip(price,1,1000,1000)^0.3
537   - </str>
538   - <str name="fl">
539   - id,name,price,score
540   - </str>
541   - <str name="mm">
542   - 2&lt;-1 5&lt;-2 6&lt;90%
543   - </str>
544   - <int name="ps">100</int>
545   - <str name="q.alt">*:*</str>
546   - <!-- example highlighter config, enable per-query with hl=true -->
547   - <str name="hl.fl">text features name</str>
548   - <!-- for this field, we want no fragmenting, just highlighting -->
549   - <str name="f.name.hl.fragsize">0</str>
550   - <!-- instructs Solr to return the field itself if no query terms are
551   - found -->
552   - <str name="f.name.hl.alternateField">name</str>
553   - <str name="f.text.hl.fragmenter">regex</str> <!-- defined below -->
554   - </lst>
555   - </requestHandler>
556   -
557   - <!-- Note how you can register the same handler multiple times with
558   - different names (and different init parameters)
559   - -->
560   - <requestHandler name="partitioned" class="solr.SearchHandler" >
561   - <lst name="defaults">
562   - <str name="defType">dismax</str>
563   - <str name="echoParams">explicit</str>
564   - <str name="qf">text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0</str>
565   - <str name="mm">2&lt;-1 5&lt;-2 6&lt;90%</str>
566   - <!-- This is an example of using Date Math to specify a constantly
567   - moving date range in a config...
568   - -->
569   - <str name="bq">incubationdate_dt:[* TO NOW/DAY-1MONTH]^2.2</str>
570   - </lst>
571   - <!-- In addition to defaults, "appends" params can be specified
572   - to identify values which should be appended to the list of
573   - multi-val params from the query (or the existing "defaults").
574   -
575   - In this example, the param "fq=instock:true" will be appended to
576   - any query time fq params the user may specify, as a mechanism for
577   - partitioning the index, independent of any user selected filtering
578   - that may also be desired (perhaps as a result of faceted searching).
579   -
580   - NOTE: there is *absolutely* nothing a client can do to prevent these
581   - "appends" values from being used, so don't use this mechanism
582   - unless you are sure you always want it.
583   - -->
584   - <lst name="appends">
585   - <str name="fq">inStock:true</str>
586   - </lst>
587   - <!-- "invariants" are a way of letting the Solr maintainer lock down
588   - the options available to Solr clients. Any params values
589   - specified here are used regardless of what values may be specified
590   - in either the query, the "defaults", or the "appends" params.
591   -
592   - In this example, the facet.field and facet.query params are fixed,
593   - limiting the facets clients can use. Faceting is not turned on by
594   - default - but if the client does specify facet=true in the request,
595   - these are the only facets they will be able to see counts for;
596   - regardless of what other facet.field or facet.query params they
597   - may specify.
598   -
599   - NOTE: there is *absolutely* nothing a client can do to prevent these
600   - "invariants" values from being used, so don't use this mechanism
601   - unless you are sure you always want it.
602   - -->
603   - <lst name="invariants">
604   - <str name="facet.field">cat</str>
605   - <str name="facet.field">manu_exact</str>
606   - <str name="facet.query">price:[* TO 500]</str>
607   - <str name="facet.query">price:[500 TO *]</str>
608   - </lst>
609   - </requestHandler>
610   -
611   -
612   - <!--
613   - Search components are registered to SolrCore and used by Search Handlers
614   -
615   - By default, the following components are avaliable:
616   -
617   - <searchComponent name="query" class="org.apache.solr.handler.component.QueryComponent" />
618   - <searchComponent name="facet" class="org.apache.solr.handler.component.FacetComponent" />
619   - <searchComponent name="mlt" class="org.apache.solr.handler.component.MoreLikeThisComponent" />
620   - <searchComponent name="highlight" class="org.apache.solr.handler.component.HighlightComponent" />
621   - <searchComponent name="stats" class="org.apache.solr.handler.component.StatsComponent" />
622   - <searchComponent name="debug" class="org.apache.solr.handler.component.DebugComponent" />
623   -
624   - Default configuration in a requestHandler would look like:
625   - <arr name="components">
626   - <str>query</str>
627   - <str>facet</str>
628   - <str>mlt</str>
629   - <str>highlight</str>
630   - <str>stats</str>
631   - <str>debug</str>
632   - </arr>
633   -
634   - If you register a searchComponent to one of the standard names, that will be used instead.
635   - To insert components before or after the 'standard' components, use:
636   -
637   - <arr name="first-components">
638   - <str>myFirstComponentName</str>
639   - </arr>
640   -
641   - <arr name="last-components">
642   - <str>myLastComponentName</str>
643   - </arr>
644   - -->
645   -
646   - <!-- The spell check component can return a list of alternative spelling
647   - suggestions. -->
648   - <searchComponent name="spellcheck" class="solr.SpellCheckComponent">
649   -
650   - <str name="queryAnalyzerFieldType">textSpell</str>
651   -
652   - <lst name="spellchecker">
653   - <str name="name">default</str>
654   - <str name="field">name</str>
655   - <str name="spellcheckIndexDir">./spellchecker</str>
656   - </lst>
657   -
658   - <!-- a spellchecker that uses a different distance measure
659   - <lst name="spellchecker">
660   - <str name="name">jarowinkler</str>
661   - <str name="field">spell</str>
662   - <str name="distanceMeasure">org.apache.lucene.search.spell.JaroWinklerDistance</str>
663   - <str name="spellcheckIndexDir">./spellchecker2</str>
664   - </lst>
665   - -->
666   -
667   - <!-- a file based spell checker
668   - <lst name="spellchecker">
669   - <str name="classname">solr.FileBasedSpellChecker</str>
670   - <str name="name">file</str>
671   - <str name="sourceLocation">spellings.txt</str>
672   - <str name="characterEncoding">UTF-8</str>
673   - <str name="spellcheckIndexDir">./spellcheckerFile</str>
674   - </lst>
675   - -->
676   - </searchComponent>
677   -
678   - <!-- A request handler utilizing the spellcheck component.
679   - #############################################################################
680   - NOTE: This is purely as an example. The whole purpose of the
681   - SpellCheckComponent is to hook it into the request handler that handles (i.e.
682   - the standard or dismax SearchHandler) queries such that a separate request is
683   - not needed to get suggestions.
684   -
685   - IN OTHER WORDS, THERE IS REALLY GOOD CHANCE THE SETUP BELOW IS NOT WHAT YOU
686   - WANT FOR YOUR PRODUCTION SYSTEM!
687   - #############################################################################
688   - -->
689   - <requestHandler name="/spell" class="solr.SearchHandler" lazy="true">
690   - <lst name="defaults">
691   - <!-- omp = Only More Popular -->
692   - <str name="spellcheck.onlyMorePopular">false</str>
693   - <!-- exr = Extended Results -->
694   - <str name="spellcheck.extendedResults">false</str>
695   - <!-- The number of suggestions to return -->
696   - <str name="spellcheck.count">1</str>
697   - </lst>
698   - <arr name="last-components">
699   - <str>spellcheck</str>
700   - </arr>
701   - </requestHandler>
702   -
703   - <searchComponent name="tvComponent" class="org.apache.solr.handler.component.TermVectorComponent"/>
704   - <!-- A Req Handler for working with the tvComponent. This is purely as an example.
705   - You will likely want to add the component to your already specified request handlers. -->
706   - <requestHandler name="tvrh" class="org.apache.solr.handler.component.SearchHandler">
707   - <lst name="defaults">
708   - <bool name="tv">true</bool>
709   - </lst>
710   - <arr name="last-components">
711   - <str>tvComponent</str>
712   - </arr>
713   - </requestHandler>
714   -
715   - <!-- Clustering Component
716   - http://wiki.apache.org/solr/ClusteringComponent
717   - This relies on third party jars which are not included in the release.
718   - To use this component (and the "/clustering" handler)
719   - Those jars will need to be downloaded, and you'll need to set the
720   - solr.cluster.enabled system property when running solr...
721   - java -Dsolr.clustering.enabled=true -jar start.jar
722   - -->
723   - <searchComponent
724   - name="clusteringComponent"
725   - enable="${solr.clustering.enabled:false}"
726   - class="org.apache.solr.handler.clustering.ClusteringComponent" >
727   - <!-- Declare an engine -->
728   - <lst name="engine">
729   - <!-- The name, only one can be named "default" -->
730   - <str name="name">default</str>
731   - <!--
732   - Class name of Carrot2 clustering algorithm. Currently available algorithms are:
733   -
734   - * org.carrot2.clustering.lingo.LingoClusteringAlgorithm
735   - * org.carrot2.clustering.stc.STCClusteringAlgorithm
736   -
737   - See http://project.carrot2.org/algorithms.html for the algorithm's characteristics.
738   - -->
739   - <str name="carrot.algorithm">org.carrot2.clustering.lingo.LingoClusteringAlgorithm</str>
740   - <!--
741   - Overriding values for Carrot2 default algorithm attributes. For a description
742   - of all available attributes, see: http://download.carrot2.org/stable/manual/#chapter.components.
743   - Use attribute key as name attribute of str elements below. These can be further
744   - overridden for individual requests by specifying attribute key as request
745   - parameter name and attribute value as parameter value.
746   - -->
747   - <str name="LingoClusteringAlgorithm.desiredClusterCountBase">20</str>
748   - </lst>
749   - <lst name="engine">
750   - <str name="name">stc</str>
751   - <str name="carrot.algorithm">org.carrot2.clustering.stc.STCClusteringAlgorithm</str>
752   - </lst>
753   - </searchComponent>
754   - <requestHandler name="/clustering"
755   - enable="${solr.clustering.enabled:false}"
756   - class="solr.SearchHandler">
757   - <lst name="defaults">
758   - <bool name="clustering">true</bool>
759   - <str name="clustering.engine">default</str>
760   - <bool name="clustering.results">true</bool>
761   - <!-- The title field -->
762   - <str name="carrot.title">name</str>
763   - <str name="carrot.url">id</str>
764   - <!-- The field to cluster on -->
765   - <str name="carrot.snippet">features</str>
766   - <!-- produce summaries -->
767   - <bool name="carrot.produceSummary">true</bool>
768   - <!-- the maximum number of labels per cluster -->
769   - <!--<int name="carrot.numDescriptions">5</int>-->
770   - <!-- produce sub clusters -->
771   - <bool name="carrot.outputSubClusters">false</bool>
772   - </lst>
773   - <arr name="last-components">
774   - <str>clusteringComponent</str>
775   - </arr>
776   - </requestHandler>
777   -
778   - <!-- Solr Cell: http://wiki.apache.org/solr/ExtractingRequestHandler -->
779   - <requestHandler name="/update/extract" class="org.apache.solr.handler.extraction.ExtractingRequestHandler" startup="lazy">
780   - <lst name="defaults">
781   - <!-- All the main content goes into "text"... if you need to return
782   - the extracted text or do highlighting, use a stored field. -->
783   - <str name="fmap.content">text</str>
784   - <str name="lowernames">true</str>
785   - <str name="uprefix">ignored_</str>
786   -
787   - <!-- capture link hrefs but ignore div attributes -->
788   - <str name="captureAttr">true</str>
789   - <str name="fmap.a">links</str>
790   - <str name="fmap.div">ignored_</str>
791   - </lst>
792   - </requestHandler>
793   -
794   -
795   - <!-- A component to return terms and document frequency of those terms.
796   - This component does not yet support distributed search. -->
797   - <searchComponent name="termsComponent" class="org.apache.solr.handler.component.TermsComponent"/>
798   -
799   - <requestHandler name="/terms" class="org.apache.solr.handler.component.SearchHandler">
800   - <lst name="defaults">
801   - <bool name="terms">true</bool>
802   - </lst>
803   - <arr name="components">
804   - <str>termsComponent</str>
805   - </arr>
806   - </requestHandler>
807   -
808   -
809   - <!-- a search component that enables you to configure the top results for
810   - a given query regardless of the normal lucene scoring.-->
811   - <searchComponent name="elevator" class="solr.QueryElevationComponent" >
812   - <!-- pick a fieldType to analyze queries -->
813   - <str name="queryFieldType">string</str>
814   - <str name="config-file">elevate.xml</str>
815   - </searchComponent>
816   -
817   - <!-- a request handler utilizing the elevator component -->
818   - <requestHandler name="/elevate" class="solr.SearchHandler" startup="lazy">
819   - <lst name="defaults">
820   - <str name="echoParams">explicit</str>
821   - </lst>
822   - <arr name="last-components">
823   - <str>elevator</str>
824   - </arr>
825   - </requestHandler>
826   -
827   -
828   - <!-- Update request handler.
829   -
830   - Note: Since solr1.1 requestHandlers requires a valid content type header if posted in
831   - the body. For example, curl now requires: -H 'Content-type:text/xml; charset=utf-8'
832   - The response format differs from solr1.1 formatting and returns a standard error code.
833   - To enable solr1.1 behavior, remove the /update handler or change its path
834   - -->
835   - <requestHandler name="/update" class="solr.XmlUpdateRequestHandler" />
836   -
837   -
838   - <requestHandler name="/update/javabin" class="solr.BinaryUpdateRequestHandler" />
839   -
840   - <!--
841   - Analysis request handler. Since Solr 1.3. Use to return how a document is analyzed. Useful
842   - for debugging and as a token server for other types of applications.
843   -
844   - This is deprecated in favor of the improved DocumentAnalysisRequestHandler and FieldAnalysisRequestHandler
845   -
846   - <requestHandler name="/analysis" class="solr.AnalysisRequestHandler" />
847   - -->
848   -
849   - <!--
850   - An analysis handler that provides a breakdown of the analysis process of provided docuemnts. This handler expects a
851   - (single) content stream with the following format:
852   -
853   - <docs>
854   - <doc>
855   - <field name="id">1</field>
856   - <field name="name">The Name</field>
857   - <field name="text">The Text Value</field>
858   - <doc>
859   - <doc>...</doc>
860   - <doc>...</doc>
861   - ...
862   - </docs>
863   -
864   - Note: Each document must contain a field which serves as the unique key. This key is used in the returned
865   - response to assoicate an analysis breakdown to the analyzed document.
866   -
867   - Like the FieldAnalysisRequestHandler, this handler also supports query analysis by
868   - sending either an "analysis.query" or "q" request paraemter that holds the query text to be analyized. It also
869   - supports the "analysis.showmatch" parameter which when set to true, all field tokens that match the query
870   - tokens will be marked as a "match".
871   - -->
872   - <requestHandler name="/analysis/document" class="solr.DocumentAnalysisRequestHandler" />
873   -
874   - <!--
875   - RequestHandler that provides much the same functionality as analysis.jsp. Provides the ability
876   - to specify multiple field types and field names in the same request and outputs index-time and
877   - query-time analysis for each of them.
878   -
879   - Request parameters are:
880   - analysis.fieldname - The field name whose analyzers are to be used
881   - analysis.fieldtype - The field type whose analyzers are to be used
882   - analysis.fieldvalue - The text for index-time analysis
883   - q (or analysis.q) - The text for query time analysis
884   - analysis.showmatch (true|false) - When set to true and when query analysis is performed, the produced
885   - tokens of the field value analysis will be marked as "matched" for every
886   - token that is produces by the query analysis
887   - -->
888   - <requestHandler name="/analysis/field" class="solr.FieldAnalysisRequestHandler" />
889   -
890   -
891   - <!-- CSV update handler, loaded on demand -->
892   - <requestHandler name="/update/csv" class="solr.CSVRequestHandler" startup="lazy" />
893   -
894   -
895   - <!--
896   - Admin Handlers - This will register all the standard admin RequestHandlers. Adding
897   - this single handler is equivalent to registering:
898   -
899   - <requestHandler name="/admin/luke" class="org.apache.solr.handler.admin.LukeRequestHandler" />
900   - <requestHandler name="/admin/system" class="org.apache.solr.handler.admin.SystemInfoHandler" />
901   - <requestHandler name="/admin/plugins" class="org.apache.solr.handler.admin.PluginInfoHandler" />
902   - <requestHandler name="/admin/threads" class="org.apache.solr.handler.admin.ThreadDumpHandler" />
903   - <requestHandler name="/admin/properties" class="org.apache.solr.handler.admin.PropertiesRequestHandler" />
904   - <requestHandler name="/admin/file" class="org.apache.solr.handler.admin.ShowFileRequestHandler" >
905   -
906   - If you wish to hide files under ${solr.home}/conf, explicitly register the ShowFileRequestHandler using:
907   - <requestHandler name="/admin/file" class="org.apache.solr.handler.admin.ShowFileRequestHandler" >
908   - <lst name="invariants">
909   - <str name="hidden">synonyms.txt</str>
910   - <str name="hidden">anotherfile.txt</str>
911   - </lst>
912   - </requestHandler>
913   - -->
914   - <requestHandler name="/admin/" class="org.apache.solr.handler.admin.AdminHandlers" />
915   -
916   - <!-- ping/healthcheck -->
917   - <requestHandler name="/admin/ping" class="PingRequestHandler">
918   - <lst name="defaults">
919   - <str name="qt">standard</str>
920   - <str name="q">solrpingquery</str>
921   - <str name="echoParams">all</str>
922   - </lst>
923   - </requestHandler>
924   -
925   - <!-- Echo the request contents back to the client -->
926   - <requestHandler name="/debug/dump" class="solr.DumpRequestHandler" >
927   - <lst name="defaults">
928   - <str name="echoParams">explicit</str> <!-- for all params (including the default etc) use: 'all' -->
929   - <str name="echoHandler">true</str>
930   - </lst>
931   - </requestHandler>
932   -
933   - <highlighting>
934   - <!-- Configure the standard fragmenter -->
935   - <!-- This could most likely be commented out in the "default" case -->
936   - <fragmenter name="gap" class="org.apache.solr.highlight.GapFragmenter" default="true">
937   - <lst name="defaults">
938   - <int name="hl.fragsize">100</int>
939   - </lst>
940   - </fragmenter>
941   -
942   - <!-- A regular-expression-based fragmenter (f.i., for sentence extraction) -->
943   - <fragmenter name="regex" class="org.apache.solr.highlight.RegexFragmenter">
944   - <lst name="defaults">
945   - <!-- slightly smaller fragsizes work better because of slop -->
946   - <int name="hl.fragsize">70</int>
947   - <!-- allow 50% slop on fragment sizes -->
948   - <float name="hl.regex.slop">0.5</float>
949   - <!-- a basic sentence pattern -->
950   - <str name="hl.regex.pattern">[-\w ,/\n\"']{20,200}</str>
951   - </lst>
952   - </fragmenter>
953   -
954   - <!-- Configure the standard formatter -->
955   - <formatter name="html" class="org.apache.solr.highlight.HtmlFormatter" default="true">
956   - <lst name="defaults">
957   - <str name="hl.simple.pre"><![CDATA[<em>]]></str>
958   - <str name="hl.simple.post"><![CDATA[</em>]]></str>
959   - </lst>
960   - </formatter>
961   - </highlighting>
962   -
963   - <!-- An example dedup update processor that creates the "id" field on the fly
964   - based on the hash code of some other fields. This example has overwriteDupes
965   - set to false since we are using the id field as the signatureField and Solr
966   - will maintain uniqueness based on that anyway.
967   -
968   - You have to link the chain to an update handler above to use it ie:
969   - <requestHandler name="/update "class="solr.XmlUpdateRequestHandler">
970   - <lst name="defaults">
971   - <str name="update.processor">dedupe</str>
972   - </lst>
973   - </requestHandler>
974   - -->
975   - <!--
976   - <updateRequestProcessorChain name="dedupe">
977   - <processor class="org.apache.solr.update.processor.SignatureUpdateProcessorFactory">
978   - <bool name="enabled">true</bool>
979   - <str name="signatureField">id</str>
980   - <bool name="overwriteDupes">false</bool>
981   - <str name="fields">name,features,cat</str>
982   - <str name="signatureClass">org.apache.solr.update.processor.Lookup3Signature</str>
983   - </processor>
984   - <processor class="solr.LogUpdateProcessorFactory" />
985   - <processor class="solr.RunUpdateProcessorFactory" />
986   - </updateRequestProcessorChain>
987   - -->
988   -
989   -
990   - <!-- queryResponseWriter plugins... query responses will be written using the
991   - writer specified by the 'wt' request parameter matching the name of a registered
992   - writer.
993   - The "default" writer is the default and will be used if 'wt' is not specified
994   - in the request. XMLResponseWriter will be used if nothing is specified here.
995   - The json, python, and ruby writers are also available by default.
996   -
997   - <queryResponseWriter name="xml" class="org.apache.solr.request.XMLResponseWriter" default="true"/>
998   - <queryResponseWriter name="json" class="org.apache.solr.request.JSONResponseWriter"/>
999   - <queryResponseWriter name="python" class="org.apache.solr.request.PythonResponseWriter"/>
1000   - <queryResponseWriter name="ruby" class="org.apache.solr.request.RubyResponseWriter"/>
1001   - <queryResponseWriter name="php" class="org.apache.solr.request.PHPResponseWriter"/>
1002   - <queryResponseWriter name="phps" class="org.apache.solr.request.PHPSerializedResponseWriter"/>
1003   -
1004   - <queryResponseWriter name="custom" class="com.example.MyResponseWriter"/>
1005   - -->
1006   -
1007   - <!-- XSLT response writer transforms the XML output by any xslt file found
1008   - in Solr's conf/xslt directory. Changes to xslt files are checked for
1009   - every xsltCacheLifetimeSeconds.
1010   - -->
1011   - <queryResponseWriter name="xslt" class="org.apache.solr.request.XSLTResponseWriter">
1012   - <int name="xsltCacheLifetimeSeconds">5</int>
1013   - </queryResponseWriter>
1014   -
1015   -
1016   - <!-- example of registering a query parser
1017   - <queryParser name="lucene" class="org.apache.solr.search.LuceneQParserPlugin"/>
1018   - -->
1019   -
1020   - <!-- example of registering a custom function parser
1021   - <valueSourceParser name="myfunc" class="com.mycompany.MyValueSourceParser" />
1022   - -->
1023   -
1024   - <!-- config for the admin interface -->
1025   - <admin>
1026   - <defaultQuery>solr</defaultQuery>
1027   -
1028   - <!-- configure a healthcheck file for servers behind a loadbalancer
1029   - <healthcheck type="file">server-enabled</healthcheck>
1030   - -->
1031   - </admin>
1032   -
1033   - <requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
1034   - <lst name="defaults">
1035   - <str name="config">/etc/solr/conf/data-config.xml</str>
1036   - </lst>
1037   - </requestHandler>
1038   -
1039   -
1040   -</config>