Commit | Line | Data |
---|---|---|
5f23ec55 PH |
1 | # -*- coding: utf-8 -*- |
2 | from __future__ import unicode_literals | |
3 | ||
4 | import six | |
5 | ||
6 | from lxml.html.clean import Cleaner as LxmlCleaner | |
7 | ||
8 | from django.core.exceptions import ImproperlyConfigured | |
9 | from django.db import models | |
10 | try: | |
11 | from django.utils.encoding import force_unicode | |
12 | except ImportError: | |
13 | from django.utils.encoding import force_text as force_unicode | |
14 | from django.utils.importlib import import_module | |
15 | from django.utils.html import strip_tags as _strip_tags | |
16 | ||
17 | from haystack import DEFAULT_ALIAS | |
18 | from haystack.indexes import SearchIndex | |
19 | ||
20 | from cms.utils.i18n import get_language_code | |
21 | ||
22 | from .conf import settings | |
23 | ||
24 | ||
25 | def alias_from_language(language): | |
26 | """ | |
27 | Returns alias if alias is a valid language. | |
28 | """ | |
29 | language = get_language_code(language) | |
30 | ||
31 | if language == settings.ALDRYN_SEARCH_DEFAULT_LANGUAGE: | |
32 | return DEFAULT_ALIAS | |
33 | return language | |
34 | ||
35 | ||
36 | def clean_join(separator, iterable): | |
37 | """ | |
38 | Filters out iterable to only join non empty items. | |
39 | """ | |
40 | return separator.join(filter(None, iterable)) | |
41 | ||
42 | ||
43 | def get_callable(string_or_callable): | |
44 | """ | |
45 | If given a callable then it returns it, otherwise it resolves the path | |
46 | and returns an object. | |
47 | """ | |
48 | if callable(string_or_callable): | |
49 | return string_or_callable | |
50 | else: | |
51 | module_name, object_name = string_or_callable.rsplit('.', 1) | |
52 | if module_name.startswith('aldryn_search'): | |
53 | module_name = "project." + module_name | |
54 | module = import_module(module_name) | |
55 | return getattr(module, object_name) | |
56 | ||
57 | ||
58 | def _get_language_from_alias_func(): | |
59 | path_or_callable = settings.ALDRYN_SEARCH_LANGUAGE_FROM_ALIAS | |
60 | ||
61 | if path_or_callable: | |
62 | try: | |
63 | func = get_callable(path_or_callable) | |
64 | except AttributeError as error: | |
65 | raise ImproperlyConfigured( | |
66 | 'ALDRYN_SEARCH_LANGUAGE_FROM_ALIAS: %s' % (str(error))) | |
67 | if not callable(func): | |
68 | raise ImproperlyConfigured( | |
69 | 'ALDRYN_SEARCH_LANGUAGE_FROM_ALIAS: %s is not callable' % func) | |
70 | else: | |
71 | func = None | |
72 | return func | |
73 | ||
74 | ||
75 | def get_index_base(): | |
76 | index_string = settings.ALDRYN_SEARCH_INDEX_BASE_CLASS | |
77 | try: | |
78 | BaseClass = get_callable(index_string) | |
79 | except AttributeError as error: | |
80 | raise ImproperlyConfigured( | |
81 | 'ALDRYN_SEARCH_INDEX_BASE_CLASS: %s' % (str(error))) | |
82 | ||
83 | if not issubclass(BaseClass, SearchIndex): | |
84 | raise ImproperlyConfigured( | |
85 | 'ALDRYN_SEARCH_INDEX_BASE_CLASS: %s is not a subclass of haystack.indexes.SearchIndex' % index_string) | |
86 | ||
87 | required_fields = ['text', 'language'] | |
88 | ||
89 | if not all(field in BaseClass.fields for field in required_fields): | |
90 | raise ImproperlyConfigured('ALDRYN_SEARCH_INDEX_BASE_CLASS: %s must contain at least these fields: %s' % ( | |
91 | index_string, required_fields)) | |
92 | return BaseClass | |
93 | ||
94 | ||
95 | def language_from_alias(alias): | |
96 | """ | |
97 | Returns alias if alias is a valid language. | |
98 | """ | |
99 | languages = [language[0] for language in settings.LANGUAGES] | |
100 | ||
101 | return alias if alias in languages else None | |
102 | ||
103 | ||
104 | def get_field_value(obj, name): | |
105 | """ | |
106 | Given a model instance and a field name (or attribute), | |
107 | returns the value of the field or an empty string. | |
108 | """ | |
109 | fields = name.split('__') | |
110 | ||
111 | name = fields[0] | |
112 | ||
113 | try: | |
114 | obj._meta.get_field(name) | |
115 | except (AttributeError, models.FieldDoesNotExist): | |
116 | # we catch attribute error because obj will not always be a model | |
117 | # specially when going through multiple relationships. | |
118 | value = getattr(obj, name, None) or '' | |
119 | else: | |
120 | value = getattr(obj, name) | |
121 | ||
122 | if len(fields) > 1: | |
123 | remaining = '__'.join(fields[1:]) | |
124 | return get_field_value(value, remaining) | |
125 | return value | |
126 | ||
127 | ||
128 | def get_model_path(model_or_string): | |
129 | if not isinstance(model_or_string, six.string_types): | |
130 | # it's a model class | |
131 | app_label = model_or_string._meta.app_label | |
132 | model_name = model_or_string._meta.object_name | |
133 | model_or_string = '{0}.{1}'.format(app_label, model_name) | |
134 | return model_or_string.lower() | |
135 | ||
136 | ||
137 | def strip_tags(value): | |
138 | """ | |
139 | Returns the given HTML with all tags stripped. | |
140 | We use lxml to strip all js tags and then hand the result to django's strip tags. | |
141 | """ | |
142 | # strip any new lines | |
143 | value = value.strip() | |
144 | ||
145 | if value: | |
146 | partial_strip = LxmlCleaner().clean_html(value) | |
147 | value = _strip_tags(partial_strip) | |
148 | return value |