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 /> |