Commit 0625388dc18ff6d216c897b0e2b6c3cd09565750
1 parent
89b44981
Exists in
master
and in
39 other branches
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
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
| ... | ... | @@ -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 = '/login/' |
| 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 | ... | ... |
| ... | ... | @@ -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' | ... | ... |
| ... | ... | @@ -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 | ... | ... |
| ... | ... | @@ -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])) | ... | ... |
| ... | ... | @@ -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="password"] { |
| 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 | + | ... | ... |
| ... | ... | @@ -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 | ... | ... |
158 Bytes
No preview for this file type
colab/static/img/logo_small.png
397 Bytes
208 Bytes
colab/static/img/thumbs_up.jpg
4.9 KB
270 Bytes
442 Bytes
202 Bytes
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/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, 'email')]) |
| 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 | ... | ... |
| ... | ... | @@ -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'] | ... | ... |
| ... | ... | @@ -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'] | ... | ... |
| ... | ... | @@ -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/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
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)) | ... | ... |
| ... | ... | @@ -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 | ... | ... |
| ... | ... | @@ -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> | ... | ... |
| ... | ... | @@ -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 | ... | ... |
| ... | ... | @@ -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 %} | ... | ... |
| ... | ... | @@ -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 %} | ... | ... |
| ... | ... | @@ -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 |
| ... | ... | @@ -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)) | ... | ... |
| ... | ... | @@ -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 | + | ... | ... |
| ... | ... | @@ -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) | ... | ... |
| ... | ... | @@ -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> | ... | ... |
| ... | ... | @@ -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 | + | ... | ... |
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +archives -fstype=fuse,rw,nodev,users,noatime :sshfs\#root@listas.interlegis.gov.br\:/var/lib/mailman/archives/private | ... | ... |
| ... | ... | @@ -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 | ... | ... |
| ... | ... | @@ -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
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 | +) | ... | ... |
| ... | ... | @@ -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) | ... | ... |
| ... | ... | @@ -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 | + | ... | ... |
| ... | ... | @@ -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 | + | ... | ... |
| ... | ... | @@ -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 | + | ... | ... |
| ... | ... | @@ -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 | + | ... | ... |
| ... | ... | @@ -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 | + | ... | ... |
| ... | ... | @@ -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\"']{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<-1 5<-2 6<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<-1 5<-2 6<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> |