Commit 092b3a271c8131bcac0d2cd8f481b8cee06c1103
1 parent
b7e5d5b3
Exists in
master
and in
39 other branches
Starting indexing attachments
Showing
11 changed files
with
282 additions
and
20 deletions
Show diff stats
requirements.txt
| @@ -10,6 +10,7 @@ django-cliauth==0.9 | @@ -10,6 +10,7 @@ django-cliauth==0.9 | ||
| 10 | django-mobile==0.3.0 | 10 | django-mobile==0.3.0 |
| 11 | django-haystack==2.1 | 11 | django-haystack==2.1 |
| 12 | pysolr==2.1 | 12 | pysolr==2.1 |
| 13 | +poster==0.8.1 | ||
| 13 | etiquetando==0.1 | 14 | etiquetando==0.1 |
| 14 | html2text | 15 | html2text |
| 15 | django-taggit | 16 | django-taggit |
src/colab/custom_settings.py
| @@ -20,6 +20,9 @@ DJANGO_DATE_FORMAT_TO_JS = { | @@ -20,6 +20,9 @@ DJANGO_DATE_FORMAT_TO_JS = { | ||
| 20 | 20 | ||
| 21 | LANGUAGE_CODE = 'pt-br' | 21 | LANGUAGE_CODE = 'pt-br' |
| 22 | 22 | ||
| 23 | +# The absolute path to the folder containing the attachments | ||
| 24 | +ATTACHMENTS_FOLDER_PATH = '' | ||
| 25 | + | ||
| 23 | # ORDERING_DATA receives the options to order for as it's keys and a dict as | 26 | # ORDERING_DATA receives the options to order for as it's keys and a dict as |
| 24 | # value, if you want to order for the last name, you can use something like: | 27 | # value, if you want to order for the last name, you can use something like: |
| 25 | # 'last_name': {'name': 'Last Name', 'fields': 'last_name'} inside the dict, | 28 | # 'last_name': {'name': 'Last Name', 'fields': 'last_name'} inside the dict, |
| @@ -39,6 +42,23 @@ ORDERING_DATA = { | @@ -39,6 +42,23 @@ ORDERING_DATA = { | ||
| 39 | }, | 42 | }, |
| 40 | } | 43 | } |
| 41 | 44 | ||
| 45 | +# File type groupings is a tuple of tuples containg what it should filter, | ||
| 46 | +# how it should be displayed, and a tuple of which mimetypes it includes | ||
| 47 | +FILE_TYPE_GROUPINGS = ( | ||
| 48 | + ('document', gettext(u'Document'), | ||
| 49 | + ('doc', 'docx', 'odt', 'otx', 'dotx', 'pdf', 'ott')), | ||
| 50 | + ('presentation', gettext(u'Presentation'), ('ppt', 'pptx', 'odp')), | ||
| 51 | + ('text', gettext(u'Text'), ('txt', 'po', 'conf', 'log')), | ||
| 52 | + ('code', gettext(u'Code'), | ||
| 53 | + ('py', 'php', 'js', 'sql', 'sh', 'patch', 'diff', 'html', '')), | ||
| 54 | + ('compressed', gettext(u'Compressed'), ('rar', 'zip', 'gz', 'tgz', 'bz2')), | ||
| 55 | + ('image', gettext(u'Image'), | ||
| 56 | + ('jpg', 'jpeg', 'png', 'tiff', 'gif', 'svg', 'psd', 'planner', 'cdr')), | ||
| 57 | + ('spreadsheet', gettext(u'Spreadsheet'), | ||
| 58 | + ('ods', 'xls', 'xlsx', 'xslt', 'csv')), | ||
| 59 | +) | ||
| 60 | + | ||
| 61 | + | ||
| 42 | # the following variable define how many characters should be shown before | 62 | # the following variable define how many characters should be shown before |
| 43 | # a highlighted word, to make sure that the highlighted word will appear | 63 | # a highlighted word, to make sure that the highlighted word will appear |
| 44 | HIGHLIGHT_NUM_CHARS_BEFORE_MATCH = 30 | 64 | HIGHLIGHT_NUM_CHARS_BEFORE_MATCH = 30 |
src/proxy/migrations/0003_create_attachment_view.py
| @@ -17,7 +17,8 @@ class Migration(DataMigration): | @@ -17,7 +17,8 @@ class Migration(DataMigration): | ||
| 17 | CONCAT(attachment.type, '/' , attachment.id, '/', attachment.filename) AS url, | 17 | CONCAT(attachment.type, '/' , attachment.id, '/', attachment.filename) AS url, |
| 18 | attachment.type AS used_by, | 18 | attachment.type AS used_by, |
| 19 | attachment.filename AS filename, | 19 | attachment.filename AS filename, |
| 20 | - (SELECT LOWER(SUBSTRING(attachment.filename FROM '\w{2,3}$'))) AS mimetype, | 20 | + attachment.id as attach_id, |
| 21 | + (SELECT LOWER(SUBSTRING(attachment.filename FROM '\.(\w+)$'))) AS mimetype, | ||
| 21 | attachment.author AS author, | 22 | attachment.author AS author, |
| 22 | attachment.description AS description, | 23 | attachment.description AS description, |
| 23 | attachment.size AS size, | 24 | attachment.size AS size, |
src/proxy/models.py
| 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
| 2 | 2 | ||
| 3 | +import os | ||
| 4 | +import urllib2 | ||
| 5 | + | ||
| 6 | +from django.conf import settings | ||
| 3 | from django.db import models | 7 | from django.db import models |
| 4 | 8 | ||
| 5 | from accounts.models import User | 9 | from accounts.models import User |
| @@ -8,17 +12,28 @@ from hitcount.models import HitCountModelMixin | @@ -8,17 +12,28 @@ from hitcount.models import HitCountModelMixin | ||
| 8 | 12 | ||
| 9 | class Attachment(models.Model, HitCountModelMixin): | 13 | class Attachment(models.Model, HitCountModelMixin): |
| 10 | url = models.TextField(primary_key=True) | 14 | url = models.TextField(primary_key=True) |
| 15 | + attach_id = models.TextField() | ||
| 11 | used_by = models.TextField() | 16 | used_by = models.TextField() |
| 12 | filename = models.TextField() | 17 | filename = models.TextField() |
| 13 | author = models.TextField(blank=True) | 18 | author = models.TextField(blank=True) |
| 14 | description = models.TextField(blank=True) | 19 | description = models.TextField(blank=True) |
| 15 | created = models.DateTimeField(blank=True) | 20 | created = models.DateTimeField(blank=True) |
| 16 | mimetype = models.TextField(blank=True) | 21 | mimetype = models.TextField(blank=True) |
| 22 | + size = models.IntegerField(blank=True) | ||
| 17 | 23 | ||
| 18 | class Meta: | 24 | class Meta: |
| 19 | managed = False | 25 | managed = False |
| 20 | db_table = 'attachment_view' | 26 | db_table = 'attachment_view' |
| 21 | 27 | ||
| 28 | + @property | ||
| 29 | + def filepath(self): | ||
| 30 | + return os.path.join( | ||
| 31 | + settings.ATTACHMENTS_FOLDER_PATH, | ||
| 32 | + self.used_by, | ||
| 33 | + self.attach_id, | ||
| 34 | + urllib2.quote(self.filename.encode('utf8')) | ||
| 35 | + ) | ||
| 36 | + | ||
| 22 | def get_absolute_url(self): | 37 | def get_absolute_url(self): |
| 23 | return u'/raw-attachment/{}'.format(self.url) | 38 | return u'/raw-attachment/{}'.format(self.url) |
| 24 | 39 |
src/proxy/search_indexes.py
| 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
| 2 | 2 | ||
| 3 | import math | 3 | import math |
| 4 | +import string | ||
| 4 | 5 | ||
| 5 | -from datetime import datetime | ||
| 6 | - | ||
| 7 | -from django.db.models import Q | 6 | +from django.template import loader, Context |
| 7 | +from django.utils.text import slugify | ||
| 8 | from haystack import indexes | 8 | from haystack import indexes |
| 9 | +from haystack.utils import log as logging | ||
| 9 | 10 | ||
| 10 | from search.base_indexes import BaseIndex | 11 | from search.base_indexes import BaseIndex |
| 11 | -from .models import Ticket, Wiki, Revision | 12 | +from .models import Attachment, Ticket, Wiki, Revision |
| 13 | + | ||
| 14 | + | ||
| 15 | +logger = logging.getLogger('haystack') | ||
| 16 | + | ||
| 17 | +# the string maketrans always return a string encoded with latin1 | ||
| 18 | +# http://stackoverflow.com/questions/1324067/how-do-i-get-str-translate-to-work-with-unicode-strings | ||
| 19 | +table = string.maketrans( | ||
| 20 | + string.punctuation, | ||
| 21 | + '.' * len(string.punctuation) | ||
| 22 | +).decode('latin1') | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +class AttachmentIndex(BaseIndex, indexes.Indexable): | ||
| 26 | + title = indexes.CharField(model_attr='filename') | ||
| 27 | + description = indexes.CharField(model_attr='description', null=True) | ||
| 28 | + modified = indexes.DateTimeField(model_attr='created', null=True) | ||
| 29 | + used_by = indexes.CharField(model_attr='used_by', null=True, stored=False) | ||
| 30 | + mimetype = indexes.CharField( | ||
| 31 | + model_attr='mimetype', | ||
| 32 | + null=True, | ||
| 33 | + stored=False | ||
| 34 | + ) | ||
| 35 | + size = indexes.IntegerField(model_attr='size', null=True, stored=False) | ||
| 36 | + filename = indexes.CharField(stored=False) | ||
| 37 | + | ||
| 38 | + def get_model(self): | ||
| 39 | + return Attachment | ||
| 40 | + | ||
| 41 | + def get_updated_field(self): | ||
| 42 | + return 'created' | ||
| 43 | + | ||
| 44 | + def prepare(self, obj): | ||
| 45 | + data = super(AttachmentIndex, self).prepare(obj) | ||
| 46 | + | ||
| 47 | + try: | ||
| 48 | + file_obj = open(obj.filepath) | ||
| 49 | + except IOError as e: | ||
| 50 | + logger.warning(u'IOError: %s - %s', e.strerror, e.filename) | ||
| 51 | + return data | ||
| 52 | + backend = self._get_backend(None) | ||
| 53 | + extracted_data = backend.extract_file_contents(file_obj) | ||
| 54 | + | ||
| 55 | + t = loader.select_template( | ||
| 56 | + ('search/indexes/proxy/attachment_text.txt', ) | ||
| 57 | + ) | ||
| 58 | + data['text'] = t.render(Context({ | ||
| 59 | + 'object': obj, | ||
| 60 | + 'extracted': extracted_data, | ||
| 61 | + })) | ||
| 62 | + return data | ||
| 63 | + | ||
| 64 | + def prepare_filename(self, obj): | ||
| 65 | + return obj.filename.translate(table).replace('.', ' ') | ||
| 66 | + | ||
| 67 | + def prepare_icon_name(self, obj): | ||
| 68 | + return u'file' | ||
| 69 | + | ||
| 70 | + def prepare_type(self, obj): | ||
| 71 | + return u'attachment' | ||
| 12 | 72 | ||
| 13 | 73 | ||
| 14 | class WikiIndex(BaseIndex, indexes.Indexable): | 74 | class WikiIndex(BaseIndex, indexes.Indexable): |
| @@ -26,7 +86,7 @@ class WikiIndex(BaseIndex, indexes.Indexable): | @@ -26,7 +86,7 @@ class WikiIndex(BaseIndex, indexes.Indexable): | ||
| 26 | return u'{}\n{}'.format(obj.wiki_text, obj.collaborators) | 86 | return u'{}\n{}'.format(obj.wiki_text, obj.collaborators) |
| 27 | 87 | ||
| 28 | def prepare_icon_name(self, obj): | 88 | def prepare_icon_name(self, obj): |
| 29 | - return u'file' | 89 | + return u'book' |
| 30 | 90 | ||
| 31 | def prepare_type(self, obj): | 91 | def prepare_type(self, obj): |
| 32 | return u'wiki' | 92 | return u'wiki' |
src/proxy/templates/search/indexes/proxy/attachment_text.txt
0 → 100644
| @@ -0,0 +1,15 @@ | @@ -0,0 +1,15 @@ | ||
| 1 | +{{ object.filename }} | ||
| 2 | +{{ object.filename|slugify }} | ||
| 3 | +{{ object.description }} | ||
| 4 | +{{ object.description|slugify }} | ||
| 5 | +{{ object.used_by }} | ||
| 6 | +{{ object.mimetype }} | ||
| 7 | +{{ object.get_author.get_full_name }} | ||
| 8 | + | ||
| 9 | +{% for k, v in extracted.metadata.items %} | ||
| 10 | + {% for val in v %} | ||
| 11 | + {{ k }}: {{ val|safe }} | ||
| 12 | + {% endfor %} | ||
| 13 | +{% endfor %} | ||
| 14 | + | ||
| 15 | +{{ extracted.contents|striptags|safe }} |
src/search/forms.py
| @@ -23,8 +23,8 @@ class ColabSearchForm(SearchForm): | @@ -23,8 +23,8 @@ class ColabSearchForm(SearchForm): | ||
| 23 | list = forms.MultipleChoiceField( | 23 | list = forms.MultipleChoiceField( |
| 24 | required=False, | 24 | required=False, |
| 25 | label=_(u'Mailinglist'), | 25 | label=_(u'Mailinglist'), |
| 26 | - choices=[(v, v) for v in MailingList.objects.values('name') | ||
| 27 | - for (v, v) in v.items()] | 26 | + choices=[(v, v) for v in MailingList.objects.values_list( |
| 27 | + 'name', flat=True)] | ||
| 28 | ) | 28 | ) |
| 29 | milestone = forms.CharField(required=False, label=_(u'Milestone')) | 29 | milestone = forms.CharField(required=False, label=_(u'Milestone')) |
| 30 | priority = forms.CharField(required=False, label=_(u'Priority')) | 30 | priority = forms.CharField(required=False, label=_(u'Priority')) |
| @@ -40,30 +40,71 @@ class ColabSearchForm(SearchForm): | @@ -40,30 +40,71 @@ class ColabSearchForm(SearchForm): | ||
| 40 | role = forms.CharField(required=False, label=_(u'Role')) | 40 | role = forms.CharField(required=False, label=_(u'Role')) |
| 41 | since = forms.DateField(required=False, label=_(u'Since')) | 41 | since = forms.DateField(required=False, label=_(u'Since')) |
| 42 | until = forms.DateField(required=False, label=_(u'Until')) | 42 | until = forms.DateField(required=False, label=_(u'Until')) |
| 43 | + filename = forms.CharField(required=False, label=_(u'Filename')) | ||
| 44 | + used_by = forms.CharField(required=False, label=_(u'Used by')) | ||
| 45 | + mimetype = forms.CharField(required=False, label=_(u'File type')) | ||
| 46 | + size = forms.CharField(required=False, label=_(u'Size')) | ||
| 43 | 47 | ||
| 44 | def search(self): | 48 | def search(self): |
| 45 | if not self.is_valid(): | 49 | if not self.is_valid(): |
| 46 | return self.no_query_found() | 50 | return self.no_query_found() |
| 47 | 51 | ||
| 52 | + # filter_or goes here | ||
| 53 | + sqs = self.searchqueryset.all() | ||
| 54 | + mimetype = self.cleaned_data['mimetype'] | ||
| 55 | + if mimetype: | ||
| 56 | + filter_mimetypes = {'mimetype__in': []} | ||
| 57 | + for type_, display, mimelist in settings.FILE_TYPE_GROUPINGS: | ||
| 58 | + if type_ in mimetype: | ||
| 59 | + filter_mimetypes['mimetype__in'] += mimelist | ||
| 60 | + if not self.cleaned_data['size']: | ||
| 61 | + sqs = sqs.filter_or(mimetype__in=mimelist) | ||
| 62 | + | ||
| 63 | + if self.cleaned_data['size']: | ||
| 64 | + # (1024 * 1024) / 2 | ||
| 65 | + # (1024 * 1024) * 10 | ||
| 66 | + filter_sizes = {} | ||
| 67 | + filter_sizes_exp = {} | ||
| 68 | + if '<500KB' in self.cleaned_data['size']: | ||
| 69 | + filter_sizes['size__lt'] = 524288 | ||
| 70 | + if '500KB__10MB' in self.cleaned_data['size']: | ||
| 71 | + filter_sizes_exp['size__gte'] = 524288 | ||
| 72 | + filter_sizes_exp['size__lte'] = 10485760 | ||
| 73 | + if '>10MB' in self.cleaned_data['size']: | ||
| 74 | + filter_sizes['size__gt'] = 10485760 | ||
| 75 | + | ||
| 76 | + if self.cleaned_data['mimetype']: | ||
| 77 | + # Add the mimetypes filters to this dict and filter it | ||
| 78 | + if filter_sizes_exp: | ||
| 79 | + filter_sizes_exp.update(filter_mimetypes) | ||
| 80 | + sqs = sqs.filter_or(**filter_sizes_exp) | ||
| 81 | + for filter_or in filter_sizes.items(): | ||
| 82 | + filter_or = dict((filter_or, )) | ||
| 83 | + filter_or.update(filter_mimetypes) | ||
| 84 | + sqs = sqs.filter_or(**filter_or) | ||
| 85 | + else: | ||
| 86 | + for filter_or in filter_sizes.items(): | ||
| 87 | + filter_or = dict((filter_or, )) | ||
| 88 | + sqs = sqs.filter_or(**filter_or) | ||
| 89 | + sqs = sqs.filter_or(**filter_sizes_exp) | ||
| 90 | + | ||
| 91 | + if self.cleaned_data['used_by']: | ||
| 92 | + sqs = sqs.filter_or(used_by__in=self.cleaned_data['used_by'].split()) | ||
| 93 | + | ||
| 48 | if self.cleaned_data.get('q'): | 94 | if self.cleaned_data.get('q'): |
| 49 | q = unicodedata.normalize( | 95 | q = unicodedata.normalize( |
| 50 | 'NFKD', unicode(self.cleaned_data.get('q')) | 96 | 'NFKD', unicode(self.cleaned_data.get('q')) |
| 51 | ).encode('ascii', 'ignore') | 97 | ).encode('ascii', 'ignore') |
| 52 | - sqs = self.searchqueryset.auto_query(q) | 98 | + sqs = sqs.auto_query(q) |
| 53 | sqs = sqs.filter(content=AltParser( | 99 | sqs = sqs.filter(content=AltParser( |
| 54 | 'dismax', | 100 | 'dismax', |
| 55 | q, | 101 | q, |
| 56 | pf='title^2.1 author^1.9 description^1.7', | 102 | pf='title^2.1 author^1.9 description^1.7', |
| 57 | mm='2<70%' | 103 | mm='2<70%' |
| 58 | )) | 104 | )) |
| 59 | - else: | ||
| 60 | - sqs = self.searchqueryset.all() | ||
| 61 | - | ||
| 62 | 105 | ||
| 63 | if self.cleaned_data['type']: | 106 | if self.cleaned_data['type']: |
| 64 | - "It will consider other types with a whitespace" | ||
| 65 | - types = self.cleaned_data['type'] | ||
| 66 | - sqs = sqs.filter(type__in=types.split()) | 107 | + sqs = sqs.filter(type=self.cleaned_data['type']) |
| 67 | 108 | ||
| 68 | if self.cleaned_data['order']: | 109 | if self.cleaned_data['order']: |
| 69 | for option, dict_order in settings.ORDERING_DATA.items(): | 110 | for option, dict_order in settings.ORDERING_DATA.items(): |
| @@ -111,6 +152,9 @@ class ColabSearchForm(SearchForm): | @@ -111,6 +152,9 @@ class ColabSearchForm(SearchForm): | ||
| 111 | if self.cleaned_data['until']: | 152 | if self.cleaned_data['until']: |
| 112 | sqs = sqs.filter(modified__lte=self.cleaned_data['until']) | 153 | sqs = sqs.filter(modified__lte=self.cleaned_data['until']) |
| 113 | 154 | ||
| 155 | + if self.cleaned_data['filename']: | ||
| 156 | + sqs = sqs.filter(filename=self.cleaned_data['filename']) | ||
| 157 | + | ||
| 114 | if self.load_all: | 158 | if self.load_all: |
| 115 | sqs = sqs.load_all() | 159 | sqs = sqs.load_all() |
| 116 | 160 |
src/search/views.py
| @@ -5,6 +5,8 @@ from django.utils.translation import ugettext as _ | @@ -5,6 +5,8 @@ from django.utils.translation import ugettext as _ | ||
| 5 | 5 | ||
| 6 | from haystack.views import SearchView | 6 | from haystack.views import SearchView |
| 7 | 7 | ||
| 8 | +from proxy.models import Attachment | ||
| 9 | + | ||
| 8 | 10 | ||
| 9 | class ColabSearchView(SearchView): | 11 | class ColabSearchView(SearchView): |
| 10 | def extra_context(self, *args, **kwargs): | 12 | def extra_context(self, *args, **kwargs): |
| @@ -106,6 +108,26 @@ class ColabSearchView(SearchView): | @@ -106,6 +108,26 @@ class ColabSearchView(SearchView): | ||
| 106 | ('role', _(u'Role'), self.request.GET.get('role')) | 108 | ('role', _(u'Role'), self.request.GET.get('role')) |
| 107 | ), | 109 | ), |
| 108 | }, | 110 | }, |
| 111 | + 'attachment': { | ||
| 112 | + 'name': _(u'Attachment'), | ||
| 113 | + 'fields': ( | ||
| 114 | + ( | ||
| 115 | + 'filename', | ||
| 116 | + _(u'Filename'), | ||
| 117 | + self.request.GET.get('filename') | ||
| 118 | + ), | ||
| 119 | + ('author', _(u'Author'), self.request.GET.get('author')), | ||
| 120 | + ( | ||
| 121 | + 'used_by', | ||
| 122 | + _(u'Used by'), self.request.GET.get('used_by')), | ||
| 123 | + ( | ||
| 124 | + 'mimetype', | ||
| 125 | + _(u'File type'), | ||
| 126 | + self.request.GET.get('mimetype') | ||
| 127 | + ), | ||
| 128 | + ('size', _(u'Size'), self.request.GET.get('size')), | ||
| 129 | + ) | ||
| 130 | + } | ||
| 109 | } | 131 | } |
| 110 | 132 | ||
| 111 | try: | 133 | try: |
| @@ -113,10 +135,36 @@ class ColabSearchView(SearchView): | @@ -113,10 +135,36 @@ class ColabSearchView(SearchView): | ||
| 113 | except AttributeError: | 135 | except AttributeError: |
| 114 | type_chosen = '' | 136 | type_chosen = '' |
| 115 | 137 | ||
| 138 | + mimetype_choices = () | ||
| 139 | + size_choices = () | ||
| 140 | + used_by_choices = () | ||
| 141 | + | ||
| 142 | + if type_chosen == 'attachment': | ||
| 143 | + mimetype_choices = [(type_, display) for type_, display, mimelist_ in settings.FILE_TYPE_GROUPINGS] | ||
| 144 | + size_choices = [ | ||
| 145 | + ('<500KB', u'< 500 KB'), | ||
| 146 | + ('500KB__10MB', u'>= 500 KB <= 10 MB'), | ||
| 147 | + ('>10MB', u'> 10 MB'), | ||
| 148 | + ] | ||
| 149 | + used_by_choices = set([ | ||
| 150 | + (v, v) for v in Attachment.objects.values_list( | ||
| 151 | + 'used_by', flat=True) | ||
| 152 | + ]) | ||
| 153 | + | ||
| 154 | + mimetype_chosen = self.request.GET.get('mimetype') | ||
| 155 | + size_chosen = self.request.GET.get('size') | ||
| 156 | + used_by_chosen = self.request.GET.get('used_by') | ||
| 157 | + | ||
| 116 | return dict( | 158 | return dict( |
| 117 | filters=types.get(type_chosen), | 159 | filters=types.get(type_chosen), |
| 118 | type_chosen=type_chosen, | 160 | type_chosen=type_chosen, |
| 119 | order_data=settings.ORDERING_DATA, | 161 | order_data=settings.ORDERING_DATA, |
| 120 | date_format=date_format, | 162 | date_format=date_format, |
| 121 | use_language=use_language, | 163 | use_language=use_language, |
| 164 | + mimetype_chosen=mimetype_chosen if mimetype_chosen else '', | ||
| 165 | + mimetype_choices=mimetype_choices, | ||
| 166 | + size_chosen=size_chosen if size_chosen else '', | ||
| 167 | + size_choices=size_choices, | ||
| 168 | + used_by_chosen=used_by_chosen if used_by_chosen else '', | ||
| 169 | + used_by_choices=used_by_choices, | ||
| 122 | ) | 170 | ) |
src/templates/search.html
| @@ -21,7 +21,7 @@ | @@ -21,7 +21,7 @@ | ||
| 21 | 21 | ||
| 22 | <ul class="none indent"> | 22 | <ul class="none indent"> |
| 23 | <li {% ifequal type "wiki" %} title="{% trans "Remove filter" %}" {% endifequal %}> | 23 | <li {% ifequal type "wiki" %} title="{% trans "Remove filter" %}" {% endifequal %}> |
| 24 | - <span class="glyphicon glyphicon-file"></span> | 24 | + <span class="glyphicon glyphicon-book"></span> |
| 25 | <a href="{% ifnotequal type "wiki" %} {% append_to_get type='wiki' %} {% else %} {% append_to_get type="" %} {% endifnotequal %}">{% trans "Wiki" %}</a> | 25 | <a href="{% ifnotequal type "wiki" %} {% append_to_get type='wiki' %} {% else %} {% append_to_get type="" %} {% endifnotequal %}">{% trans "Wiki" %}</a> |
| 26 | </li> | 26 | </li> |
| 27 | <li {% ifequal type "thread" %} title="{% trans "Remove filter" %}" {% endifequal %}> | 27 | <li {% ifequal type "thread" %} title="{% trans "Remove filter" %}" {% endifequal %}> |
src/templates/search/search-wiki-preview.html
| 1 | {% load i18n %} | 1 | {% load i18n %} |
| 2 | 2 | ||
| 3 | -<span class="glyphicon glyphicon-file" title="{{ result.type }}"></span> | 3 | +<span class="glyphicon glyphicon-book" title="{{ result.type }}"></span> |
| 4 | 4 | ||
| 5 | <span class="subject"> | 5 | <span class="subject"> |
| 6 | <a href="{{ result.url }}">{{ result.name }}</a> | 6 | <a href="{{ result.url }}">{{ result.name }}</a> |
src/templates/search/search.html
| @@ -62,15 +62,69 @@ | @@ -62,15 +62,69 @@ | ||
| 62 | {% for field_lookup, field_display, field_value in filters.fields %} | 62 | {% for field_lookup, field_display, field_value in filters.fields %} |
| 63 | <div class="form-group"> | 63 | <div class="form-group"> |
| 64 | <label for="{{ field_lookup }}">{{ field_display }}</label> | 64 | <label for="{{ field_lookup }}">{{ field_display }}</label> |
| 65 | - {% ifequal field_lookup "list" %} | 65 | + {% if field_lookup == "list" %} |
| 66 | <select name="{{ field_lookup }}" class="form-control" multiple> | 66 | <select name="{{ field_lookup }}" class="form-control" multiple> |
| 67 | {% for value, option in form.fields.list.choices %} | 67 | {% for value, option in form.fields.list.choices %} |
| 68 | <option value="{{ value }}" {% if value in field_value %}selected{% endif %}>{{ option }}</option> | 68 | <option value="{{ value }}" {% if value in field_value %}selected{% endif %}>{{ option }}</option> |
| 69 | {% endfor %} | 69 | {% endfor %} |
| 70 | </select> | 70 | </select> |
| 71 | + {% elif field_lookup == "size" %} | ||
| 72 | + <ul class="unstyled-list"> | ||
| 73 | + {% for value, option in size_choices %} | ||
| 74 | + {% with value|add:" "|add:size_chosen as sizelistadd %} | ||
| 75 | + {% if value in field_value %} | ||
| 76 | + <li class="selected" title="{% trans "Remove filter" %}"> | ||
| 77 | + <span class="glyphicon glyphicon-remove"></span> | ||
| 78 | + <a href="{% pop_from_get size=value %}">{{ option }}</a> | ||
| 79 | + </li> | ||
| 80 | + {% else %} | ||
| 81 | + <li> | ||
| 82 | + <span class="glyphicon glyphicon-chevron-right"></span> | ||
| 83 | + <a href="{% append_to_get size=sizelistadd %}">{{ option }}</a> | ||
| 84 | + </li> | ||
| 85 | + {% endif %} | ||
| 86 | + {% endwith %} | ||
| 87 | + {% endfor %} | ||
| 88 | + </ul> | ||
| 89 | + {% elif field_lookup == "mimetype" %} | ||
| 90 | + <ul class="unstyled-list"> | ||
| 91 | + {% for value, option in mimetype_choices %} | ||
| 92 | + {% with value|add:" "|add:mimetype_chosen as mimelistadd %} | ||
| 93 | + {% if value in mime_chosen %} | ||
| 94 | + <li class="selected" title="{% trans "Remove filter" %}"> | ||
| 95 | + <span class="glyphicon glyphicon-remove"></span> | ||
| 96 | + <a href="{% pop_from_get mimetype=value %}">{{ option }}</a> | ||
| 97 | + </li> | ||
| 98 | + {% else %} | ||
| 99 | + <li> | ||
| 100 | + <span class="glyphicon glyphicon-chevron-right"></span> | ||
| 101 | + <a href="{% append_to_get mimetype=mimelistadd %}">{{ option }}</a> | ||
| 102 | + </li> | ||
| 103 | + {% endif %} | ||
| 104 | + {% endwith %} | ||
| 105 | + {% endfor %} | ||
| 106 | + </ul> | ||
| 107 | + {% elif field_lookup == "used_by" %} | ||
| 108 | + <ul class="unstyled-list"> | ||
| 109 | + {% for value, option in used_by_choices %} | ||
| 110 | + {% with value|add:" "|add:used_by_chosen as used_byadd %} | ||
| 111 | + {% if value in used_by_chosen %} | ||
| 112 | + <li class="selected" title="{% trans "Remove filter" %}"> | ||
| 113 | + <span class="glyphicon glyphicon-remove"></span> | ||
| 114 | + <a href="{% pop_from_get used_by=value %}">{{ option }}</a> | ||
| 115 | + </li> | ||
| 116 | + {% else %} | ||
| 117 | + <li> | ||
| 118 | + <span class="glyphicon glyphicon-chevron-right"></span> | ||
| 119 | + <a href="{% append_to_get used_by=used_byadd %}">{{ option }}</a> | ||
| 120 | + </li> | ||
| 121 | + {% endif %} | ||
| 122 | + {% endwith %} | ||
| 123 | + {% endfor %} | ||
| 124 | + </ul> | ||
| 71 | {% else %} | 125 | {% else %} |
| 72 | <input type="text" class="form-control" placeholder="{{ field_display }}" name="{{ field_lookup }}" {% if field_value %}value="{{ field_value }}"{% endif %}> | 126 | <input type="text" class="form-control" placeholder="{{ field_display }}" name="{{ field_lookup }}" {% if field_value %}value="{{ field_value }}"{% endif %}> |
| 73 | - {% endifequal %} | 127 | + {% endif %} |
| 74 | </div> | 128 | </div> |
| 75 | {% endfor %} | 129 | {% endfor %} |
| 76 | <button type="submit" class="btn btn-default pull-right"> | 130 | <button type="submit" class="btn btn-default pull-right"> |
| @@ -101,7 +155,7 @@ | @@ -101,7 +155,7 @@ | ||
| 101 | 155 | ||
| 102 | <ul class="unstyled-list"> | 156 | <ul class="unstyled-list"> |
| 103 | <li> | 157 | <li> |
| 104 | - <span class="glyphicon glyphicon-file"></span> | 158 | + <span class="glyphicon glyphicon-book"></span> |
| 105 | <a href="{% append_to_get type='wiki' %}">{% trans "Wiki" %}</a> | 159 | <a href="{% append_to_get type='wiki' %}">{% trans "Wiki" %}</a> |
| 106 | </li> | 160 | </li> |
| 107 | <li> | 161 | <li> |
| @@ -120,6 +174,10 @@ | @@ -120,6 +174,10 @@ | ||
| 120 | <span class="glyphicon glyphicon-user"></span> | 174 | <span class="glyphicon glyphicon-user"></span> |
| 121 | <a href="{% append_to_get type='user' %}">{% trans "User" %}</a> | 175 | <a href="{% append_to_get type='user' %}">{% trans "User" %}</a> |
| 122 | </li> | 176 | </li> |
| 177 | + <li> | ||
| 178 | + <span class="glyphicon glyphicon-file"></span> | ||
| 179 | + <a href="{% append_to_get type='attachment' %}">{% trans "Attachment" %}</a> | ||
| 180 | + </li> | ||
| 123 | </ul> | 181 | </ul> |
| 124 | {% endif %} | 182 | {% endif %} |
| 125 | <hr /> | 183 | <hr /> |