Sfoglia il codice sorgente

Merge branch 'master' into flask_perimeter

Alexandre Flament 8 anni fa
parent
commit
e67dfaaac7
38 ha cambiato i file con 368 aggiunte e 564 eliminazioni
  1. 2
    2
      manage.sh
  2. 10
    0
      searx/engines/google.py
  3. 4
    4
      searx/engines/xpath.py
  4. 32
    0
      searx/exceptions.py
  5. 2
    1
      searx/preferences.py
  6. 4
    0
      searx/results.py
  7. 42
    23
      searx/search.py
  8. 11
    0
      searx/settings.yml
  9. 1
    1
      searx/static/themes/oscar/css/logicodev.min.css
  10. 1
    1
      searx/static/themes/oscar/css/pointhi.min.css
  11. 0
    23
      searx/static/themes/oscar/less/logicodev/advanced.less
  12. 20
    78
      searx/static/themes/oscar/less/logicodev/navbar.less
  13. 6
    5
      searx/static/themes/oscar/less/logicodev/results.less
  14. 23
    0
      searx/static/themes/oscar/less/logicodev/search.less
  15. 1
    0
      searx/static/themes/oscar/less/logicodev/variables.less
  16. 17
    25
      searx/static/themes/oscar/less/pointhi/navbar.less
  17. 62
    0
      searx/templates/__common__/about.html
  18. 0
    0
      searx/templates/__common__/opensearch.xml
  19. 6
    0
      searx/templates/__common__/opensearch_response_rss.xml
  20. 1
    62
      searx/templates/courgette/about.html
  21. 0
    28
      searx/templates/courgette/opensearch.xml
  22. 0
    23
      searx/templates/courgette/opensearch_response_rss.xml
  23. 1
    62
      searx/templates/legacy/about.html
  24. 0
    28
      searx/templates/legacy/opensearch.xml
  25. 0
    23
      searx/templates/legacy/opensearch_response_rss.xml
  26. 1
    62
      searx/templates/oscar/about.html
  27. 9
    3
      searx/templates/oscar/advanced.html
  28. 5
    0
      searx/templates/oscar/base.html
  29. 1
    1
      searx/templates/oscar/languages.html
  30. 3
    3
      searx/templates/oscar/macros.html
  31. 8
    13
      searx/templates/oscar/navbar.html
  32. 14
    9
      searx/templates/oscar/results.html
  33. 15
    3
      searx/templates/oscar/search.html
  34. 1
    1
      searx/templates/oscar/time-range.html
  35. 1
    62
      searx/templates/pix-art/about.html
  36. 2
    0
      searx/utils.py
  37. 59
    18
      searx/webapp.py
  38. 3
    0
      tests/unit/test_webapp.py

+ 2
- 2
manage.sh Vedi File

6
 ACTION=$1
6
 ACTION=$1
7
 
7
 
8
 update_packages() {
8
 update_packages() {
9
-    pip install --upgrade -r "$BASE_DIR/requirements.txt"
9
+    pip install -r "$BASE_DIR/requirements.txt"
10
 }
10
 }
11
 
11
 
12
 update_dev_packages() {
12
 update_dev_packages() {
13
     update_packages
13
     update_packages
14
-    pip install --upgrade -r "$BASE_DIR/requirements-dev.txt"
14
+    pip install -r "$BASE_DIR/requirements-dev.txt"
15
 }
15
 }
16
 
16
 
17
 check_geckodriver() {
17
 check_geckodriver() {

+ 10
- 0
searx/engines/google.py Vedi File

112
 content_xpath = './/span[@class="st"]'
112
 content_xpath = './/span[@class="st"]'
113
 content_misc_xpath = './/div[@class="f slp"]'
113
 content_misc_xpath = './/div[@class="f slp"]'
114
 suggestion_xpath = '//p[@class="_Bmc"]'
114
 suggestion_xpath = '//p[@class="_Bmc"]'
115
+spelling_suggestion_xpath = '//a[@class="spell"]'
115
 
116
 
116
 # map : detail location
117
 # map : detail location
117
 map_address_xpath = './/div[@class="s"]//table//td[2]/span/text()'
118
 map_address_xpath = './/div[@class="s"]//table//td[2]/span/text()'
220
     instant_answer = dom.xpath('//div[@id="_vBb"]//text()')
221
     instant_answer = dom.xpath('//div[@id="_vBb"]//text()')
221
     if instant_answer:
222
     if instant_answer:
222
         results.append({'answer': u' '.join(instant_answer)})
223
         results.append({'answer': u' '.join(instant_answer)})
224
+    try:
225
+        results_num = int(dom.xpath('//div[@id="resultStats"]//text()')[0]
226
+                          .split()[1].replace(',', ''))
227
+        results.append({'number_of_results': results_num})
228
+    except:
229
+        pass
223
 
230
 
224
     # parse results
231
     # parse results
225
     for result in dom.xpath(results_xpath):
232
     for result in dom.xpath(results_xpath):
275
         # append suggestion
282
         # append suggestion
276
         results.append({'suggestion': extract_text(suggestion)})
283
         results.append({'suggestion': extract_text(suggestion)})
277
 
284
 
285
+    for correction in dom.xpath(spelling_suggestion_xpath):
286
+        results.append({'correction': extract_text(correction)})
287
+
278
     # return results
288
     # return results
279
     return results
289
     return results
280
 
290
 

+ 4
- 4
searx/engines/xpath.py Vedi File

31
 def extract_text(xpath_results):
31
 def extract_text(xpath_results):
32
     if type(xpath_results) == list:
32
     if type(xpath_results) == list:
33
         # it's list of result : concat everything using recursive call
33
         # it's list of result : concat everything using recursive call
34
-        if not xpath_results:
35
-            raise Exception('Empty url resultset')
36
         result = ''
34
         result = ''
37
         for e in xpath_results:
35
         for e in xpath_results:
38
             result = result + extract_text(e)
36
             result = result + extract_text(e)
48
 
46
 
49
 
47
 
50
 def extract_url(xpath_results, search_url):
48
 def extract_url(xpath_results, search_url):
49
+    if xpath_results == []:
50
+        raise Exception('Empty url resultset')
51
     url = extract_text(xpath_results)
51
     url = extract_text(xpath_results)
52
 
52
 
53
     if url.startswith('//'):
53
     if url.startswith('//'):
103
     if results_xpath:
103
     if results_xpath:
104
         for result in dom.xpath(results_xpath):
104
         for result in dom.xpath(results_xpath):
105
             url = extract_url(result.xpath(url_xpath), search_url)
105
             url = extract_url(result.xpath(url_xpath), search_url)
106
-            title = extract_text(result.xpath(title_xpath)[0])
107
-            content = extract_text(result.xpath(content_xpath)[0])
106
+            title = extract_text(result.xpath(title_xpath))
107
+            content = extract_text(result.xpath(content_xpath))
108
             results.append({'url': url, 'title': title, 'content': content})
108
             results.append({'url': url, 'title': title, 'content': content})
109
     else:
109
     else:
110
         for url, title, content in zip(
110
         for url, title, content in zip(

+ 32
- 0
searx/exceptions.py Vedi File

1
+'''
2
+searx is free software: you can redistribute it and/or modify
3
+it under the terms of the GNU Affero General Public License as published by
4
+the Free Software Foundation, either version 3 of the License, or
5
+(at your option) any later version.
6
+
7
+searx is distributed in the hope that it will be useful,
8
+but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
+GNU Affero General Public License for more details.
11
+
12
+You should have received a copy of the GNU Affero General Public License
13
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
14
+
15
+(C) 2017- by Alexandre Flament, <alex@al-f.net>
16
+'''
17
+
18
+
19
+class SearxException(Exception):
20
+    pass
21
+
22
+
23
+class SearxParameterException(SearxException):
24
+
25
+    def __init__(self, name, value):
26
+        if value == '' or value is None:
27
+            message = 'Empty ' + name + ' parameter'
28
+        else:
29
+            message = 'Invalid value "' + value + '" for parameter ' + name
30
+        super(SearxParameterException, self).__init__(message)
31
+        self.parameter_name = name
32
+        self.parameter_value = value

+ 2
- 1
searx/preferences.py Vedi File

130
         self.key = data
130
         self.key = data
131
 
131
 
132
     def save(self, name, resp):
132
     def save(self, name, resp):
133
-        resp.set_cookie(name, bytes(self.key), max_age=COOKIE_MAX_AGE)
133
+        if hasattr(self, 'key'):
134
+            resp.set_cookie(name, bytes(self.key), max_age=COOKIE_MAX_AGE)
134
 
135
 
135
 
136
 
136
 class SwitchableSetting(Setting):
137
 class SwitchableSetting(Setting):

+ 4
- 0
searx/results.py Vedi File

127
         self.infoboxes = []
127
         self.infoboxes = []
128
         self.suggestions = set()
128
         self.suggestions = set()
129
         self.answers = set()
129
         self.answers = set()
130
+        self.corrections = set()
130
         self._number_of_results = []
131
         self._number_of_results = []
131
         self._ordered = False
132
         self._ordered = False
132
         self.paging = False
133
         self.paging = False
140
             elif 'answer' in result:
141
             elif 'answer' in result:
141
                 self.answers.add(result['answer'])
142
                 self.answers.add(result['answer'])
142
                 results.remove(result)
143
                 results.remove(result)
144
+            elif 'correction' in result:
145
+                self.corrections.add(result['correction'])
146
+                results.remove(result)
143
             elif 'infobox' in result:
147
             elif 'infobox' in result:
144
                 self._merge_infobox(result)
148
                 self._merge_infobox(result)
145
                 results.remove(result)
149
                 results.remove(result)

+ 42
- 23
searx/search.py Vedi File

31
 from searx.results import ResultContainer
31
 from searx.results import ResultContainer
32
 from searx import logger
32
 from searx import logger
33
 from searx.plugins import plugins
33
 from searx.plugins import plugins
34
+from searx.languages import language_codes
35
+from searx.exceptions import SearxParameterException
34
 
36
 
35
 logger = logger.getChild('search')
37
 logger = logger.getChild('search')
36
 
38
 
37
 number_of_searches = 0
39
 number_of_searches = 0
38
 
40
 
41
+language_code_set = set(l[0].lower() for l in language_codes)
42
+language_code_set.add('all')
43
+
39
 
44
 
40
 def send_http_request(engine, request_params, start_time, timeout_limit):
45
 def send_http_request(engine, request_params, start_time, timeout_limit):
41
     # for page_load_time stats
46
     # for page_load_time stats
182
 
187
 
183
 
188
 
184
 def get_search_query_from_webapp(preferences, form):
189
 def get_search_query_from_webapp(preferences, form):
185
-    query = None
186
-    query_engines = []
187
-    query_categories = []
188
-    query_pageno = 1
189
-    query_lang = 'all'
190
-    query_time_range = None
190
+    # no text for the query ?
191
+    if not form.get('q'):
192
+        raise SearxParameterException('q', '')
191
 
193
 
192
     # set blocked engines
194
     # set blocked engines
193
     disabled_engines = preferences.engines.get_disabled()
195
     disabled_engines = preferences.engines.get_disabled()
194
 
196
 
195
-    # set specific language if set
196
-    query_lang = preferences.get_value('language')
197
-
198
-    # safesearch
199
-    query_safesearch = preferences.get_value('safesearch')
200
-
201
-    # TODO better exceptions
202
-    if not form.get('q'):
203
-        raise Exception('noquery')
204
-
205
-    # set pagenumber
206
-    pageno_param = form.get('pageno', '1')
207
-    if not pageno_param.isdigit() or int(pageno_param) < 1:
208
-        pageno_param = 1
209
-
210
-    query_pageno = int(pageno_param)
211
-
212
     # parse query, if tags are set, which change
197
     # parse query, if tags are set, which change
213
     # the serch engine or search-language
198
     # the serch engine or search-language
214
     raw_text_query = RawTextQuery(form['q'], disabled_engines)
199
     raw_text_query = RawTextQuery(form['q'], disabled_engines)
217
     # set query
202
     # set query
218
     query = raw_text_query.getSearchQuery()
203
     query = raw_text_query.getSearchQuery()
219
 
204
 
205
+    # get and check page number
206
+    pageno_param = form.get('pageno', '1')
207
+    if not pageno_param.isdigit() or int(pageno_param) < 1:
208
+        raise SearxParameterException('pageno', pageno_param)
209
+    query_pageno = int(pageno_param)
210
+
211
+    # get language
220
     # set specific language if set on request, query or preferences
212
     # set specific language if set on request, query or preferences
221
     # TODO support search with multible languages
213
     # TODO support search with multible languages
222
     if len(raw_text_query.languages):
214
     if len(raw_text_query.languages):
226
     else:
218
     else:
227
         query_lang = preferences.get_value('language')
219
         query_lang = preferences.get_value('language')
228
 
220
 
221
+    # check language
222
+    if query_lang.lower() not in language_code_set:
223
+        raise SearxParameterException('language', query_lang)
224
+
225
+    # get safesearch
226
+    if 'safesearch' in form:
227
+        query_safesearch = form.get('safesearch')
228
+        # first check safesearch
229
+        if not query_safesearch.isdigit():
230
+            raise SearxParameterException('safesearch', query_safesearch)
231
+        query_safesearch = int(query_safesearch)
232
+    else:
233
+        query_safesearch = preferences.get_value('safesearch')
234
+
235
+    # safesearch : second check
236
+    if query_safesearch < 0 or query_safesearch > 2:
237
+        raise SearxParameterException('safesearch', query_safesearch)
238
+
239
+    # get time_range
229
     query_time_range = form.get('time_range')
240
     query_time_range = form.get('time_range')
230
 
241
 
242
+    # check time_range
243
+    if query_time_range not in ('None', None, '', 'day', 'week', 'month', 'year'):
244
+        raise SearxParameterException('time_range', query_time_range)
245
+
246
+    # query_engines
231
     query_engines = raw_text_query.engines
247
     query_engines = raw_text_query.engines
232
 
248
 
249
+    # query_categories
250
+    query_categories = []
251
+
233
     # if engines are calculated from query,
252
     # if engines are calculated from query,
234
     # set categories by using that informations
253
     # set categories by using that informations
235
     if query_engines and raw_text_query.specific:
254
     if query_engines and raw_text_query.specific:

+ 11
- 0
searx/settings.yml Vedi File

462
 #        - ...
462
 #        - ...
463
 #    disabled : True
463
 #    disabled : True
464
 
464
 
465
+  - name : semantic scholar
466
+    engine : xpath
467
+    paging : True
468
+    search_url : https://www.semanticscholar.org/search?q={query}&sort=relevance&page={pageno}&ae=false
469
+    results_xpath : //article
470
+    url_xpath : .//div[@class="search-result-title"]/a/@href
471
+    title_xpath : .//div[@class="search-result-title"]/a
472
+    content_xpath : .//div[@class="search-result-abstract"]
473
+    shortcut : se
474
+    categories : science
475
+
465
   - name : spotify
476
   - name : spotify
466
     engine : spotify
477
     engine : spotify
467
     shortcut : stf
478
     shortcut : stf

+ 1
- 1
searx/static/themes/oscar/css/logicodev.min.css
File diff suppressed because it is too large
Vedi File


+ 1
- 1
searx/static/themes/oscar/css/pointhi.min.css
File diff suppressed because it is too large
Vedi File


+ 0
- 23
searx/static/themes/oscar/less/logicodev/advanced.less Vedi File

29
         font-weight: bold;
29
         font-weight: bold;
30
         border-bottom: @light-green 5px solid;
30
         border-bottom: @light-green 5px solid;
31
     }
31
     }
32
-    select {
33
-        appearance: none;
34
-        -webkit-appearance: none;
35
-        -moz-appearance: none;
36
-        font-size: 1.2rem;
37
-        font-weight:normal;
38
-        background-color: white;
39
-        border: @mild-gray 1px solid;
40
-        color: @dark-gray;
41
-        padding-bottom: 0.4rem;
42
-        padding-top: 0.4rem;
43
-        padding-left: 1rem;
44
-        padding-right: 5rem;
45
-        margin-right: 0.5rem;
46
-        background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAQAAACR313BAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
47
-AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZ
48
-cwAABFkAAARZAVnbJUkAAAAHdElNRQfgBxgLDwB20OFsAAAAbElEQVQY073OsQ3CMAAEwJMYwJGn
49
-sAehpoXJItltBkmcdZBYgIIiQoLglnz3ui+eP+bk5uneteTMZJa6OJuIqvYzSJoqwqBq8gdmTTW8
50
-6/dghxAUq4xsVYT9laBYXCw93Aajh7GPEF23t4fkBYevGFTANkPRAAAAJXRFWHRkYXRlOmNyZWF0
51
-ZQAyMDE2LTA3LTI0VDExOjU1OjU4KzAyOjAwRFqFOQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNi0w
52
-Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
53
-7jwaAAAAAElFTkSuQmCC) 96% no-repeat;
54
-    }
55
 }
32
 }
56
 
33
 
57
 #check-advanced {
34
 #check-advanced {

+ 20
- 78
searx/static/themes/oscar/less/logicodev/navbar.less Vedi File

1
-.navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus{
1
+.searx-navbar {
2
     background: @black;
2
     background: @black;
3
-    color: @light-green;
4
-}
5
-
6
-.navbar > li > a {
7
-    padding: 0;
8
-    margin: 0;
9
-}
10
-
11
-.navbar-nav > li > a {
12
-    background: @black;
13
-    padding: 0 8px;
14
-    margin: 0;
15
-    line-height: 30px;
16
-}
17
-
18
-.navbar, .navbar-default, .menu {
19
-    background-color: @black;
20
-    border: none;
21
-    border-top: 4px solid @light-green;
22
-    padding-top: 5px;
23
-    color: @dim-gray !important;
24
-    font-weight: 700;
25
-    font-size: 1.1em;
26
-    text-transform: lowercase;
27
-    margin-bottom: 24px;
28
-    height: 30px;
29
-    line-height: 30px;
30
-
31
-    .navbar-nav > li > a{
32
-        color: @dim-gray;
3
+    height: 2.3rem;
4
+    font-size: 1.3rem;
5
+    line-height: 1.3rem;
6
+    padding: 0.5rem;
7
+    font-weight: bold;
8
+    margin-bottom: 0.8rem;
9
+
10
+    a, a:hover {
11
+        margin-right: 2.0rem;
12
+        color: white;
13
+        text-decoration: none;
33
     }
14
     }
34
 
15
 
35
-    .navbar-brand{
36
-        font-weight: 700;
16
+    .instance a {
37
         color: @light-green;
17
         color: @light-green;
38
-        line-height: 30px;
39
-        padding: 0 30px;
40
-        margin: 0;
18
+        margin-left: 2.0rem;
41
     }
19
     }
42
-    z-index: 10;
43
 }
20
 }
44
 
21
 
45
-// Hover color
46
-// http://stackoverflow.com/users/114029/leniel-macaferi
47
-.navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus {
48
-    color: @light-green;
49
-    background: @black;
50
-}
51
-
52
-.navbar-toggle {
53
-    margin-top: 0;
54
-}
22
+#main-logo {
23
+    margin-top: 20vh;
24
+    margin-bottom: 25px;
55
 
25
 
56
-.menu {
57
-    margin: 0;
58
-    padding: 0;
59
-    position: absolute;
60
-    top: 4px;
61
-    border: 0;
62
-    z-index: 1000000000;
63
-    height: 40px;
64
-    line-height: 40px;
65
-    ul {
66
-        padding: 0;
67
-        margin: 0;
68
-        li {
69
-            padding: 0 0.6em;
70
-            margin: 0;
71
-            float: left;
72
-            list-style: none;
73
-            a {
74
-                color: @dim-gray;
75
-            }
76
-        }
77
-        li.active a {
78
-            color: @light-green;
79
-        }
26
+    & > img {
27
+        max-width: 350px;
28
+        width: 80%;
80
     }
29
     }
81
 }
30
 }
82
 
31
 
83
-.menu-right {
84
-    right: 2em;
85
-}
86
-
87
-.menu-left {
88
-    left: 2em;
89
-}

+ 6
- 5
searx/static/themes/oscar/less/logicodev/results.less Vedi File

1
 .result_header {
1
 .result_header {
2
-    margin-top: 6px;
3
-    margin-bottom: 4px;
2
+    margin-top: 0px;
3
+    margin-bottom: 2px;
4
     font-size: 16px;
4
     font-size: 16px;
5
 
5
 
6
     .favicon {
6
     .favicon {
41
 
41
 
42
 }
42
 }
43
 
43
 
44
-.external-link, .external-link a{
45
-    color: @green;
44
+.external-link {
45
+    color: @dark-green;
46
+    font-size: 12px;
46
 
47
 
47
-    a{
48
+    a {
48
         margin-right: 3px;
49
         margin-right: 3px;
49
     }
50
     }
50
 }
51
 }

+ 23
- 0
searx/static/themes/oscar/less/logicodev/search.less Vedi File

54
      background-color: @green;
54
      background-color: @green;
55
      color: white;
55
      color: white;
56
  }
56
  }
57
+
58
+.custom-select {
59
+    appearance: none;
60
+    -webkit-appearance: none;
61
+    -moz-appearance: none;
62
+    font-size: 1.2rem;
63
+    font-weight:normal;
64
+    background-color: white;
65
+    border: @mild-gray 1px solid;
66
+    color: @dark-gray;
67
+    background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAQAAACR313BAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
68
+AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZ
69
+cwAABFkAAARZAVnbJUkAAAAHdElNRQfgBxgLDwB20OFsAAAAbElEQVQY073OsQ3CMAAEwJMYwJGn
70
+sAehpoXJItltBkmcdZBYgIIiQoLglnz3ui+eP+bk5uneteTMZJa6OJuIqvYzSJoqwqBq8gdmTTW8
71
+6/dghxAUq4xsVYT9laBYXCw93Aajh7GPEF23t4fkBYevGFTANkPRAAAAJXRFWHRkYXRlOmNyZWF0
72
+ZQAyMDE2LTA3LTI0VDExOjU1OjU4KzAyOjAwRFqFOQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNi0w
73
+Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
74
+7jwaAAAAAElFTkSuQmCC) 96% no-repeat;
75
+}
76
+
77
+.search-margin {
78
+    margin-bottom: 0.6em;
79
+}

+ 1
- 0
searx/static/themes/oscar/less/logicodev/variables.less Vedi File

7
 @blue: #0088CC; 
7
 @blue: #0088CC; 
8
 @red: #F35E77;
8
 @red: #F35E77;
9
 @violet: #684898;
9
 @violet: #684898;
10
+@dark-green: #069025;
10
 @green: #2ecc71;
11
 @green: #2ecc71;
11
 @light-green: #01D7D4; 
12
 @light-green: #01D7D4; 
12
 @orange: #FFA92F;
13
 @orange: #FFA92F;

+ 17
- 25
searx/static/themes/oscar/less/pointhi/navbar.less Vedi File

1
-.menu {
2
-    margin: 0;
3
-    padding: 0;
4
-    position: absolute;
5
-    top: 4px;
6
-    border: 0;
7
-    z-index: 1000000000;
8
-    height: 40px;
9
-    line-height: 40px;
10
-    ul {
11
-        padding: 0;
12
-        margin: 0;
13
-        li {
14
-            padding: 0 0.6em;
15
-            margin: 0;
16
-            float: left;
17
-            list-style: none;
18
-        }
19
-    }
20
-}
1
+.searx-navbar {
2
+    background: #eee;
3
+    color: #aaa;
4
+    height: 2.3rem;
5
+    font-size: 1.3rem;
6
+    line-height: 1.3rem;
7
+    padding: 0.5rem;
8
+    font-weight: bold;
9
+    margin-bottom: 1.3rem;
21
 
10
 
22
-.menu-right {
23
-    right: 2em;
24
-}
11
+    a, a:hover {
12
+        margin-right: 2.0rem;
13
+        text-decoration: none;
14
+    }
25
 
15
 
26
-.menu-left {
27
-    left: 2em;
16
+    .instance a {
17
+        color: #444;
18
+        margin-left: 2.0rem;
19
+    }
28
 }
20
 }

+ 62
- 0
searx/templates/__common__/about.html Vedi File

1
+<div{% if rtl %} dir="ltr"{% endif %}>
2
+    <h1>About <a href="{{ url_for('index') }}">searx</a></h1>
3
+
4
+    <p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>, aggregating the results of other <a href="{{ url_for('preferences') }}">search engines</a> while not storing information about its users.
5
+    </p>
6
+    <h2>Why use searx?</h2>
7
+    <ul>
8
+        <li>searx may not offer you as personalised results as Google, but it doesn't generate a profile about you</li>
9
+        <li>searx doesn't care about what you search for, never shares anything with a third party, and it can't be used to compromise you</li>
10
+        <li>searx is free software, the code is 100% open and you can help to make it better. See more on <a href="https://github.com/asciimoo/searx">github</a></li>
11
+    </ul>
12
+    <p>If you do care about privacy, want to be a conscious user, or otherwise believe
13
+    in digital freedom, make searx your default search engine or run it on your own server</p>
14
+
15
+<h2>Technical details - How does it work?</h2>
16
+
17
+<p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>,
18
+inspired by the <a href="https://beniz.github.io/seeks/">seeks project</a>.<br />
19
+It provides basic privacy by mixing your queries with searches on other platforms without storing search data. Queries are made using a POST request on every browser (except chrome*). Therefore they show up in neither our logs, nor your url history. In case of Chrome* users there is an exception, searx uses the search bar to perform GET requests.<br />
20
+Searx can be added to your browser's search bar; moreover, it can be set as the default search engine.
21
+</p>
22
+
23
+<h2>How can I make it my own?</h2>
24
+
25
+<p>Searx appreciates your concern regarding logs, so take the <a href="https://github.com/asciimoo/searx">code</a> and run it yourself! <br />Add your Searx to this <a href="https://github.com/asciimoo/searx/wiki/Searx-instances">list</a> to help other people reclaim their privacy and make the Internet freer!
26
+<br />The more decentralized the Internet is, the more freedom we have!</p>
27
+
28
+
29
+<h2>More about searx</h2>
30
+
31
+<ul>
32
+    <li><a href="https://github.com/asciimoo/searx">github</a></li>
33
+    <li><a href="https://www.ohloh.net/p/searx/">ohloh</a></li>
34
+    <li><a href="https://twitter.com/Searx_engine">twitter</a></li>
35
+    <li>IRC: #searx @ freenode (<a href="https://kiwiirc.com/client/irc.freenode.com/searx">webclient</a>)</li>
36
+    <li><a href="https://www.transifex.com/projects/p/searx/">transifex</a></li>
37
+</ul>
38
+
39
+
40
+<hr />
41
+
42
+<h2 id="faq">FAQ</h2>
43
+
44
+<h3>How to add to firefox?</h3>
45
+<p><a href="#" onclick="window.external.AddSearchProvider(window.location.protocol + '//' + window.location.host + '{{ url_for('opensearch') }}');">Install</a> searx as a search engine on any version of Firefox! (javascript required)</p>
46
+
47
+<h2 id="dev_faq">Developer FAQ</h2>
48
+
49
+<h3>New engines?</h3>
50
+<ul>
51
+    <li>Edit your <a href="https://raw.github.com/asciimoo/searx/master/searx/settings.yml">settings.yml</a></li>
52
+    <li>Create your custom engine module, check the <a href="https://github.com/asciimoo/searx/blob/master/examples/basic_engine.py">example engine</a></li>
53
+</ul>
54
+<p>Don't forget to restart searx after config edit!</p>
55
+
56
+<h3>Installation/WSGI support?</h3>
57
+<p>See the <a href="https://github.com/asciimoo/searx/wiki/Installation">installation and setup</a> wiki page</p>
58
+
59
+<h3>How to debug engines?</h3>
60
+<p><a href="{{ url_for('stats') }}">Stats page</a> contains some useful data about the engines used.</p>
61
+
62
+</div>

searx/templates/oscar/opensearch.xml → searx/templates/__common__/opensearch.xml Vedi File


searx/templates/oscar/opensearch_response_rss.xml → searx/templates/__common__/opensearch_response_rss.xml Vedi File

11
     <opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage>
11
     <opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage>
12
     <atom:link rel="search" type="application/opensearchdescription+xml" href="{{ base_url }}opensearch.xml"/>
12
     <atom:link rel="search" type="application/opensearchdescription+xml" href="{{ base_url }}opensearch.xml"/>
13
     <opensearch:Query role="request" searchTerms="{{ q|e }}" startPage="1" />
13
     <opensearch:Query role="request" searchTerms="{{ q|e }}" startPage="1" />
14
+    {% if error_message %}
15
+    <item>
16
+      <title>Error</title>
17
+      <description>{{ error_message|e }}</description>
18
+    </item>
19
+    {% endif %}
14
     {% for r in results %}
20
     {% for r in results %}
15
     <item>
21
     <item>
16
       <title>{{ r.title }}</title>
22
       <title>{{ r.title }}</title>

+ 1
- 62
searx/templates/courgette/about.html Vedi File

1
 {% extends 'courgette/base.html' %}
1
 {% extends 'courgette/base.html' %}
2
 {% block content %}
2
 {% block content %}
3
 {% include 'courgette/github_ribbon.html' %}
3
 {% include 'courgette/github_ribbon.html' %}
4
-<div class="row"{% if rtl %} dir="ltr"{% endif %}>
5
-    <h1>About <a href="{{ url_for('index') }}">searx</a></h1>
6
-
7
-    <p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>, aggregating the results of other <a href="{{ url_for('preferences') }}">search engines</a> while not storing information about its users.
8
-    </p>
9
-    <h2>Why use searx?</h2>
10
-    <ul>
11
-        <li>searx may not offer you as personalised results as Google, but it doesn't generate a profile about you</li>
12
-        <li>searx doesn't care about what you search for, never shares anything with a third party, and it can't be used to compromise you</li>
13
-        <li>searx is free software, the code is 100% open and you can help to make it better. See more on <a href="https://github.com/asciimoo/searx">github</a></li>
14
-    </ul>
15
-    <p>If you do care about privacy, want to be a conscious user, or otherwise believe
16
-    in digital freedom, make searx your default search engine or run it on your own server</p>
17
-
18
-<h2>Technical details - How does it work?</h2>
19
-
20
-<p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>,
21
-inspired by the <a href="https://beniz.github.io/seeks/">seeks project</a>.<br />
22
-It provides basic privacy by mixing your queries with searches on other platforms without storing search data. Queries are made using a POST request on every browser (except chrome*). Therefore they show up in neither our logs, nor your url history. In case of Chrome* users there is an exception, searx uses the search bar to perform GET requests.<br />
23
-Searx can be added to your browser's search bar; moreover, it can be set as the default search engine.
24
-</p>
25
-
26
-<h2>How can I make it my own?</h2>
27
-
28
-<p>Searx appreciates your concern regarding logs, so take the <a href="https://github.com/asciimoo/searx">code</a> and run it yourself! <br />Add your Searx to this <a href="https://github.com/asciimoo/searx/wiki/Searx-instances">list</a> to help other people reclaim their privacy and make the Internet freer!
29
-<br />The more decentralized the Internet, is the more freedom we have!</p>
30
-
31
-
32
-<h2>More about searx</h2>
33
-
34
-<ul>
35
-    <li><a href="https://github.com/asciimoo/searx">github</a></li>
36
-    <li><a href="https://www.ohloh.net/p/searx/">ohloh</a></li>
37
-    <li><a href="https://twitter.com/Searx_engine">twitter</a></li>
38
-    <li>IRC: #searx @ freenode (<a href="https://kiwiirc.com/client/irc.freenode.com/searx">webclient</a>)</li>
39
-    <li><a href="https://www.transifex.com/projects/p/searx/">transifex</a></li>
40
-</ul>
41
-
42
-
43
-<hr />
44
-
45
-<h2 id="faq">FAQ</h2>
46
-
47
-<h3>How to add to firefox?</h3>
48
-<p><a href="#" onclick="window.external.AddSearchProvider(window.location.protocol + '//' + window.location.host + '{{ url_for('opensearch') }}');">Install</a> searx as a search engine on any version of Firefox! (javascript required)</p>
49
-
50
-<h2 id="dev_faq">Developer FAQ</h2>
51
-
52
-<h3>New engines?</h3>
53
-<ul>
54
-    <li>Edit your <a href="https://raw.github.com/asciimoo/searx/master/searx/settings.yml">settings.yml</a></li>
55
-    <li>Create your custom engine module, check the <a href="https://github.com/asciimoo/searx/blob/master/examples/basic_engine.py">example engine</a></li>
56
-</ul>
57
-<p>Don't forget to restart searx after config edit!</p>
58
-
59
-<h3>Installation/WSGI support?</h3>
60
-<p>See the <a href="https://github.com/asciimoo/searx/wiki/Installation">installation and setup</a> wiki page</p>
61
-
62
-<h3>How to debug engines?</h3>
63
-<p><a href="{{ url_for('stats') }}">Stats page</a> contains some useful data about the engines used.</p>
64
-
65
-</div>
4
+{% include '__common__/about.html' %}
66
 {% endblock %}
5
 {% endblock %}

+ 0
- 28
searx/templates/courgette/opensearch.xml Vedi File

1
-<?xml version="1.0" encoding="utf-8"?>
2
-<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
3
-  <ShortName>{{ instance_name }}</ShortName>
4
-  <Description>a privacy-respecting, hackable metasearch engine</Description>
5
-  <InputEncoding>UTF-8</InputEncoding>
6
-  <Image>{{ urljoin(host, url_for('static', filename='img/favicon.png')) }}</Image>
7
-  <LongName>searx metasearch</LongName>
8
-  {% if opensearch_method == 'get' %}
9
-    <Url type="text/html" method="get" template="{{ host }}search?q={searchTerms}"/>
10
-    {% if autocomplete %}
11
-    <Url type="application/x-suggestions+json" method="get" template="{{ host }}autocompleter">
12
-        <Param name="format" value="x-suggestions" />
13
-        <Param name="q" value="{searchTerms}" />
14
-    </Url>
15
-    {% endif %}
16
-  {% else %}
17
-    <Url type="text/html" method="post" template="{{ host }}">
18
-        <Param name="q" value="{searchTerms}" />
19
-    </Url>
20
-    {% if autocomplete %}
21
-    <!-- TODO, POST REQUEST doesn't work -->
22
-    <Url type="application/x-suggestions+json" method="get" template="{{ host }}autocompleter">
23
-        <Param name="format" value="x-suggestions" />
24
-        <Param name="q" value="{searchTerms}" />
25
-    </Url>
26
-    {% endif %}
27
-  {% endif %}
28
-</OpenSearchDescription>

+ 0
- 23
searx/templates/courgette/opensearch_response_rss.xml Vedi File

1
-<?xml version="1.0" encoding="UTF-8"?>
2
-<rss version="2.0"
3
-     xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"
4
-     xmlns:atom="http://www.w3.org/2005/Atom">
5
-  <channel>
6
-    <title>Searx search: {{ q|e }}</title>
7
-    <link>{{ base_url }}?q={{ q|e }}</link>
8
-    <description>Search results for "{{ q|e }}" - searx</description>
9
-    <opensearch:totalResults>{{ number_of_results }}</opensearch:totalResults>
10
-    <opensearch:startIndex>1</opensearch:startIndex>
11
-    <opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage>
12
-    <atom:link rel="search" type="application/opensearchdescription+xml" href="{{ base_url }}opensearch.xml"/>
13
-    <opensearch:Query role="request" searchTerms="{{ q|e }}" startPage="1" />
14
-    {% for r in results %}
15
-    <item>
16
-      <title>{{ r.title }}</title>
17
-      <link>{{ r.url }}</link>
18
-      <description>{{ r.content }}</description>
19
-      {% if r.pubdate %}<pubDate>{{ r.pubdate }}</pubDate>{% endif %}
20
-    </item>
21
-    {% endfor %}
22
-  </channel>
23
-</rss>

+ 1
- 62
searx/templates/legacy/about.html Vedi File

1
 {% extends 'legacy/base.html' %}
1
 {% extends 'legacy/base.html' %}
2
 {% block content %}
2
 {% block content %}
3
 {% include 'legacy/github_ribbon.html' %}
3
 {% include 'legacy/github_ribbon.html' %}
4
-<div class="row"{% if rtl %} dir="ltr"{% endif %}>
5
-    <h1>About <a href="{{ url_for('index') }}">searx</a></h1>
6
-
7
-    <p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>, aggregating the results of other <a href="{{ url_for('preferences') }}">search engines</a> while not storing information about its users.
8
-    </p>
9
-    <h2>Why use searx?</h2>
10
-    <ul>
11
-        <li>searx may not offer you as personalised results as Google, but it doesn't generate a profile about you</li>
12
-        <li>searx doesn't care about what you search for, never shares anything with a third party, and it can't be used to compromise you</li>
13
-        <li>searx is free software, the code is 100% open and you can help to make it better. See more on <a href="https://github.com/asciimoo/searx">github</a></li>
14
-    </ul>
15
-    <p>If you do care about privacy, want to be a conscious user, or otherwise believe
16
-    in digital freedom, make searx your default search engine or run it on your own server</p>
17
-
18
-<h2>Technical details - How does it work?</h2>
19
-
20
-<p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>,
21
-inspired by the <a href="https://beniz.github.io/seeks/">seeks project</a>.<br />
22
-It provides basic privacy by mixing your queries with searches on other platforms without storing search data. Queries are made using a POST request on every browser (except chrome*). Therefore they show up in neither our logs, nor your url history. In case of Chrome* users there is an exception, if searx used from the search bar it performs GET requests.<br />
23
-Searx can be added to your browser's search bar; moreover, it can be set as the default search engine.
24
-</p>
25
-
26
-<h2>How can I make it my own?</h2>
27
-
28
-<p>Searx appreciates your concern regarding logs, so take the <a href="https://github.com/asciimoo/searx">code</a> and run it yourself! <br />Add your Searx to this <a href="https://github.com/asciimoo/searx/wiki/Searx-instances">list</a> to help other people reclaim their privacy and make the Internet freer!
29
-<br />The more decentralized Internet is the more freedom we have!</p>
30
-
31
-
32
-<h2>More about searx</h2>
33
-
34
-<ul>
35
-    <li><a href="https://github.com/asciimoo/searx">github</a></li>
36
-    <li><a href="https://www.ohloh.net/p/searx/">ohloh</a></li>
37
-    <li><a href="https://twitter.com/Searx_engine">twitter</a></li>
38
-    <li>IRC: #searx @ freenode (<a href="https://kiwiirc.com/client/irc.freenode.com/searx">webclient</a>)</li>
39
-    <li><a href="https://www.transifex.com/projects/p/searx/">transifex</a></li>
40
-</ul>
41
-
42
-
43
-<hr />
44
-
45
-<h2 id="faq">FAQ</h2>
46
-
47
-<h3>How to add to firefox?</h3>
48
-<p><a href="#" onclick="window.external.AddSearchProvider(window.location.protocol + '//' + window.location.host + '{{ url_for('opensearch') }}');">Install</a> searx as a search engine on any version of Firefox! (javascript required)</p>
49
-
50
-<h2 id="dev_faq">Developer FAQ</h2>
51
-
52
-<h3>New engines?</h3>
53
-<ul>
54
-    <li>Edit your <a href="https://raw.github.com/asciimoo/searx/master/searx/settings.yml">settings.yml</a></li>
55
-    <li>Create your custom engine module, check the <a href="https://github.com/asciimoo/searx/blob/master/examples/basic_engine.py">example engine</a></li>
56
-</ul>
57
-<p>Don't forget to restart searx after config edit!</p>
58
-
59
-<h3>Installation/WSGI support?</h3>
60
-<p>See the <a href="https://github.com/asciimoo/searx/wiki/Installation">installation and setup</a> wiki page</p>
61
-
62
-<h3>How to debug engines?</h3>
63
-<p><a href="{{ url_for('stats') }}">Stats page</a> contains some useful data about the engines used.</p>
64
-
65
-</div>
4
+{% include '__common__/about.html' %}
66
 {% endblock %}
5
 {% endblock %}

+ 0
- 28
searx/templates/legacy/opensearch.xml Vedi File

1
-<?xml version="1.0" encoding="utf-8"?>
2
-<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
3
-  <ShortName>{{ instance_name }}</ShortName>
4
-  <Description>a privacy-respecting, hackable metasearch engine</Description>
5
-  <InputEncoding>UTF-8</InputEncoding>
6
-  <Image>{{ urljoin(host, url_for('static', filename='img/favicon.png')) }}</Image>
7
-  <LongName>searx metasearch</LongName>
8
-  {% if opensearch_method == 'get' %}
9
-    <Url type="text/html" method="get" template="{{ host }}search?q={searchTerms}"/>
10
-    {% if autocomplete %}
11
-    <Url type="application/x-suggestions+json" method="get" template="{{ host }}autocompleter">
12
-        <Param name="format" value="x-suggestions" />
13
-        <Param name="q" value="{searchTerms}" />
14
-    </Url>
15
-    {% endif %}
16
-  {% else %}
17
-    <Url type="text/html" method="post" template="{{ host }}">
18
-        <Param name="q" value="{searchTerms}" />
19
-    </Url>
20
-    {% if autocomplete %}
21
-    <!-- TODO, POST REQUEST doesn't work -->
22
-    <Url type="application/x-suggestions+json" method="get" template="{{ host }}autocompleter">
23
-        <Param name="format" value="x-suggestions" />
24
-        <Param name="q" value="{searchTerms}" />
25
-    </Url>
26
-    {% endif %}
27
-  {% endif %}
28
-</OpenSearchDescription>

+ 0
- 23
searx/templates/legacy/opensearch_response_rss.xml Vedi File

1
-<?xml version="1.0" encoding="UTF-8"?>
2
-<rss version="2.0"
3
-     xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"
4
-     xmlns:atom="http://www.w3.org/2005/Atom">
5
-  <channel>
6
-    <title>Searx search: {{ q|e }}</title>
7
-    <link>{{ base_url }}?q={{ q|e }}</link>
8
-    <description>Search results for "{{ q|e }}" - searx</description>
9
-    <opensearch:totalResults>{{ number_of_results }}</opensearch:totalResults>
10
-    <opensearch:startIndex>1</opensearch:startIndex>
11
-    <opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage>
12
-    <atom:link rel="search" type="application/opensearchdescription+xml" href="{{ base_url }}opensearch.xml"/>
13
-    <opensearch:Query role="request" searchTerms="{{ q|e }}" startPage="1" />
14
-    {% for r in results %}
15
-    <item>
16
-      <title>{{ r.title }}</title>
17
-      <link>{{ r.url }}</link>
18
-      <description>{{ r.content }}</description>
19
-      {% if r.pubdate %}<pubDate>{{ r.pubdate }}</pubDate>{% endif %}
20
-    </item>
21
-    {% endfor %}
22
-  </channel>
23
-</rss>

+ 1
- 62
searx/templates/oscar/about.html Vedi File

1
 {% extends "oscar/base.html" %}
1
 {% extends "oscar/base.html" %}
2
 {% block title %}{{ _('about') }} - {% endblock %}
2
 {% block title %}{{ _('about') }} - {% endblock %}
3
 {% block content %}
3
 {% block content %}
4
-<div{% if rtl %} dir="ltr"{% endif %}>
5
-    <h1>About <a href="{{ url_for('index') }}">searx</a></h1>
6
-
7
-    <p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>, aggregating the results of other <a href="{{ url_for('preferences') }}">search engines</a> while not storing information about its users.
8
-    </p>
9
-    <h2>Why use searx?</h2>
10
-    <ul>
11
-        <li>searx may not offer you as personalised results as Google, but it doesn't generate a profile about you</li>
12
-        <li>searx doesn't care about what you search for, never shares anything with a third party, and it can't be used to compromise you</li>
13
-        <li>searx is free software, the code is 100% open and you can help to make it better. See more on <a href="https://github.com/asciimoo/searx">github</a></li>
14
-    </ul>
15
-    <p>If you do care about privacy, want to be a conscious user, or otherwise believe
16
-    in digital freedom, make searx your default search engine or run it on your own server</p>
17
-
18
-<h2>Technical details - How does it work?</h2>
19
-
20
-<p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>,
21
-inspired by the <a href="https://beniz.github.io/seeks/">seeks project</a>.<br />
22
-It provides basic privacy by mixing your queries with searches on other platforms without storing search data. Queries are made using a POST request on every browser (except chrome*). Therefore they show up in neither our logs, nor your url history. In case of Chrome* users there is an exception, searx uses the search bar to perform GET requests.<br />
23
-Searx can be added to your browser's search bar; moreover, it can be set as the default search engine.
24
-</p>
25
-
26
-<h2>How can I make it my own?</h2>
27
-
28
-<p>Searx appreciates your concern regarding logs, so take the <a href="https://github.com/asciimoo/searx">code</a> and run it yourself! <br />Add your Searx to this <a href="https://github.com/asciimoo/searx/wiki/Searx-instances">list</a> to help other people reclaim their privacy and make the Internet freer!
29
-<br />The more decentralized the Internet is, the more freedom we have!</p>
30
-
31
-
32
-<h2>More about searx</h2>
33
-
34
-<ul>
35
-    <li><a href="https://github.com/asciimoo/searx">github</a></li>
36
-    <li><a href="https://www.ohloh.net/p/searx/">ohloh</a></li>
37
-    <li><a href="https://twitter.com/Searx_engine">twitter</a></li>
38
-    <li>IRC: #searx @ freenode (<a href="https://kiwiirc.com/client/irc.freenode.com/searx">webclient</a>)</li>
39
-    <li><a href="https://www.transifex.com/projects/p/searx/">transifex</a></li>
40
-</ul>
41
-
42
-
43
-<hr />
44
-
45
-<h2 id="faq">FAQ</h2>
46
-
47
-<h3>How to add to firefox?</h3>
48
-<p><a href="#" onclick="window.external.AddSearchProvider(window.location.protocol + '//' + window.location.host + '{{ url_for('opensearch') }}');">Install</a> searx as a search engine on any version of Firefox! (javascript required)</p>
49
-
50
-<h2 id="dev_faq">Developer FAQ</h2>
51
-
52
-<h3>New engines?</h3>
53
-<ul>
54
-    <li>Edit your <a href="https://raw.github.com/asciimoo/searx/master/searx/settings.yml">settings.yml</a></li>
55
-    <li>Create your custom engine module, check the <a href="https://github.com/asciimoo/searx/blob/master/examples/basic_engine.py">example engine</a></li>
56
-</ul>
57
-<p>Don't forget to restart searx after config edit!</p>
58
-
59
-<h3>Installation/WSGI support?</h3>
60
-<p>See the <a href="https://github.com/asciimoo/searx/wiki/Installation">installation and setup</a> wiki page</p>
61
-
62
-<h3>How to debug engines?</h3>
63
-<p><a href="{{ url_for('stats') }}">Stats page</a> contains some useful data about the engines used.</p>
64
-
65
-</div>
4
+{% include '__common__/about.html' %}
66
 {% endblock %}
5
 {% endblock %}

+ 9
- 3
searx/templates/oscar/advanced.html Vedi File

4
     {{ _('Advanced settings') }}
4
     {{ _('Advanced settings') }}
5
 </label>
5
 </label>
6
 <div id="advanced-search-container">
6
 <div id="advanced-search-container">
7
-    {% include 'oscar/categories.html' %}
8
-    {% include 'oscar/time-range.html' %}
9
-    {% include 'oscar/languages.html' %}
7
+  {% include 'oscar/categories.html' %}
8
+  <div class="row">
9
+    <div class="col-xs-6">
10
+      {% include 'oscar/time-range.html' %}
11
+    </div>
12
+    <div class="col-xs-6">
13
+      {% include 'oscar/languages.html' %}
14
+    </div>
15
+  </div>
10
 </div>
16
 </div>

+ 5
- 0
searx/templates/oscar/base.html Vedi File

98
     {% for script in scripts %}
98
     {% for script in scripts %}
99
         <script src="{{ url_for('static', filename=script) }}"></script>
99
         <script src="{{ url_for('static', filename=script) }}"></script>
100
     {% endfor %}
100
     {% endfor %}
101
+    <noscript>
102
+      <style>
103
+        .glyphicon { display: none; }
104
+      </style>
105
+    </noscript>
101
 </body>
106
 </body>
102
 </html>
107
 </html>

+ 1
- 1
searx/templates/oscar/languages.html Vedi File

1
 {% if preferences %}
1
 {% if preferences %}
2
 <select class="form-control" name='language'>
2
 <select class="form-control" name='language'>
3
 {% else %}
3
 {% else %}
4
-<select class="time_range" id='language' name='language'>
4
+<select class="time_range custom-select form-control" id='language' name='language'>
5
 {% endif %}
5
 {% endif %}
6
 	<option value="all" {% if current_language == 'all' %}selected="selected"{% endif %}>{{ _('Default language') }}</option>
6
 	<option value="all" {% if current_language == 'all' %}selected="selected"{% endif %}>{{ _('Default language') }}</option>
7
 		{% for lang_id,lang_name,country_name,english_name in language_codes | sort(attribute=1) %}
7
 		{% for lang_id,lang_name,country_name,english_name in language_codes | sort(attribute=1) %}

+ 3
- 3
searx/templates/oscar/macros.html Vedi File

10
 {%- endmacro %}
10
 {%- endmacro %}
11
 
11
 
12
 {%- macro result_link(url, title, classes='') -%}
12
 {%- macro result_link(url, title, classes='') -%}
13
-<a href="{{ url }}" {% if classes %}class="{{ classes }} "{% endif %}{% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ title }}</a>
13
+<a href="{{ url }}" {% if classes %}class="{{ classes }}" {% endif %}{% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ title }}</a>
14
 {%- endmacro -%}
14
 {%- endmacro -%}
15
 
15
 
16
 <!-- Draw result header -->
16
 <!-- Draw result header -->
37
     <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
37
     <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
38
     {% endif %}
38
     {% endif %}
39
 </div>
39
 </div>
40
-    <div class="text-muted"><small>{{ result.pretty_url }}</small></div>
40
+<div class="external-link">{{ result.pretty_url }}</div>
41
 {%- endmacro %}
41
 {%- endmacro %}
42
 
42
 
43
 <!-- Draw result footer -->
43
 <!-- Draw result footer -->
50
     {% if proxify %}
50
     {% if proxify %}
51
     <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
51
     <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
52
     {% endif %}
52
     {% endif %}
53
-    <div class="text-muted"><small>{{ result.pretty_url }}</small></div>
53
+    <div class="external-link">{{ result.pretty_url }}</div>
54
 {%- endmacro %}
54
 {%- endmacro %}
55
 
55
 
56
 {% macro preferences_item_header(info, label, rtl) -%}
56
 {% macro preferences_item_header(info, label, rtl) -%}

+ 8
- 13
searx/templates/oscar/navbar.html Vedi File

1
-<!-- Static navbar -->
2
-<div class="navbar navbar-default" role="navigation">
3
-    <div class="container-fluid">
4
-        <div class="navbar-header{% if rtl %} navbar-right{% endif %}">
5
-            <a class="navbar-brand" href="{{ url_for('index') }}">{{ instance_name }}</a>
6
-        </div>
7
-    </div><!--/.container-fluid -->
1
+<div class="searx-navbar">
2
+    <span class="instance {% if rtl %}pull-right{% else %}pull-left{% endif%}">
3
+        <a href="{{ url_for('index') }}">{{ instance_name }}</a>
4
+    </span>
5
+    <span class="{% if rtl %}pull-left{% else %}pull-right{% endif %}">
6
+        <a href="{{ url_for('about') }}">{{ _('about') }}</a>
7
+        <a href="{{ url_for('preferences') }}">{{ _('preferences') }}</a>
8
+    </span>
8
 </div>
9
 </div>
9
-<div class="menu menu-{% if rtl %}left{% else %}right{% endif %}">
10
-    <ul> <!-- results.html -->
11
-        <li{% if template_name == 'about.html' %} class="active"{% endif %}><a href="{{ url_for('about') }}" class="hmarg">{{ _('about') }}</a></li>
12
-        <li{% if template_name == 'preferences.html' %} class="active"{% endif %}><a href="{{ url_for('preferences') }}" class="hmarg">{{ _('preferences') }}</a></li>
13
-    </ul>
14
-</div><!--/.nav-collapse -->

+ 14
- 9
searx/templates/oscar/results.html Vedi File

11
 {% block title %}{{ q|e }} - {% endblock %}
11
 {% block title %}{{ q|e }} - {% endblock %}
12
 {% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ search_url() }}&amp;format=rss">{% endblock %}
12
 {% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ search_url() }}&amp;format=rss">{% endblock %}
13
 {% block content %}
13
 {% block content %}
14
+    {% include 'oscar/search.html' %}
14
     <div class="row">
15
     <div class="row">
15
         <div class="col-sm-8" id="main_results">
16
         <div class="col-sm-8" id="main_results">
16
             <h1 class="sr-only">{{ _('Search results') }}</h1>
17
             <h1 class="sr-only">{{ _('Search results') }}</h1>
17
-            {% include 'oscar/search.html' %}
18
+
19
+            {% if corrections %}
20
+            <div class="result">
21
+                <span class="result_header text-muted form-inline pull-left suggestion_item">{{ _('Try searching for:') }}</span>
22
+                {% for correction in corrections %}
23
+                    <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" role="navigation" class="form-inline pull-left suggestion_item">
24
+                        <input type="hidden" name="q" value="{{ correction }}">
25
+                        <button type="submit" class="btn btn-default btn-xs">{{ correction }}</button>
26
+                    </form>
27
+                {% endfor %}
28
+            </div>
29
+            {% endif %}
18
 
30
 
19
             {% if answers %}
31
             {% if answers %}
20
             {% for answer in answers %}
32
             {% for answer in answers %}
80
 
92
 
81
         <div class="col-sm-4" id="sidebar_results">
93
         <div class="col-sm-4" id="sidebar_results">
82
             {% if number_of_results != '0' %}
94
             {% if number_of_results != '0' %}
83
-            <div class="panel panel-default">
84
-                <div class="panel-heading">
85
-                    <h4 class="panel-title">{{ _('Number of results') }}</h4>
86
-                </div>
87
-                <div class="panel-body">
88
-                    {{ number_of_results }}
89
-                </div>
90
-            </div>
95
+                <p><small>{{ _('Number of results') }}: {{ number_of_results }}</small></p>
91
             {% endif %}
96
             {% endif %}
92
             {% if infoboxes %}
97
             {% if infoboxes %}
93
                 {% for infobox in infoboxes %}
98
                 {% for infobox in infoboxes %}

+ 15
- 3
searx/templates/oscar/search.html Vedi File

1
 {% from 'oscar/macros.html' import icon %}
1
 {% from 'oscar/macros.html' import icon %}
2
 <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form" role="search">
2
 <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form" role="search">
3
-    <div class="input-group col-sm-12">
3
+  <div class="row">
4
+    <div class="col-xs-12 col-md-8">
5
+      <div class="input-group search-margin">
4
         <input type="search" name="q" class="form-control" id="q" placeholder="{{ _('Search for...') }}" autocomplete="off" value="{{ q }}">
6
         <input type="search" name="q" class="form-control" id="q" placeholder="{{ _('Search for...') }}" autocomplete="off" value="{{ q }}">
5
         <span class="input-group-btn">
7
         <span class="input-group-btn">
6
             <button type="submit" class="btn btn-default"><span class="hide_if_nojs">{{ icon('search') }}</span><span class="hidden active_if_nojs">{{ _('Start search') }}</span></button>
8
             <button type="submit" class="btn btn-default"><span class="hide_if_nojs">{{ icon('search') }}</span><span class="hidden active_if_nojs">{{ _('Start search') }}</span></button>
7
         </span>
9
         </span>
10
+      </div>
8
     </div>
11
     </div>
9
-    <div class="input-group col-sm-12 advanced">
10
-        {% include 'oscar/advanced.html' %}
12
+    <div class="col-xs-6 col-md-2 search-margin">
13
+        {% include 'oscar/time-range.html' %}
11
     </div>
14
     </div>
15
+    <div class="col-xs-6 col-md-2 search-margin">
16
+        {% include 'oscar/languages.html' %}
17
+    </div>
18
+  </div>
19
+  <div class="row">
20
+    <div class="col-sm-12">
21
+        {% include 'oscar/categories.html' %}
22
+    </div>
23
+  </div>
12
 </form><!-- / #search_form_full -->
24
 </form><!-- / #search_form_full -->

+ 1
- 1
searx/templates/oscar/time-range.html Vedi File

1
-<select name="time_range" id="time-range">
1
+<select name="time_range" id="time-range" class="custom-select form-control">
2
     <option id="time-range-anytime" value="" {{ "selected" if time_range=="" or not time_range  else ""}}>
2
     <option id="time-range-anytime" value="" {{ "selected" if time_range=="" or not time_range  else ""}}>
3
         {{ _('Anytime') }}
3
         {{ _('Anytime') }}
4
     </option>
4
     </option>

+ 1
- 62
searx/templates/pix-art/about.html Vedi File

1
 {% extends 'pix-art/base.html' %}
1
 {% extends 'pix-art/base.html' %}
2
 {% block content %}
2
 {% block content %}
3
-<div class="row"{% if rtl %} dir="ltr"{% endif %}>
4
-    <h1>About <a href="{{ url_for('index') }}">searx</a></h1>
5
-
6
-    <p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>, aggregating the results of other <a href="{{ url_for('preferences') }}">search engines</a> while not storing information about its users.
7
-    </p>
8
-    <h2>Why use searx?</h2>
9
-    <ul>
10
-        <li>searx may not offer you as personalised results as Google, but it doesn't generate a profile about you</li>
11
-        <li>searx doesn't care about what you search for, never shares anything with a third party, and it can't be used to compromise you</li>
12
-        <li>searx is free software, the code is 100% open and you can help to make it better. See more on <a href="https://github.com/asciimoo/searx">github</a></li>
13
-    </ul>
14
-    <p>If you do care about privacy, want to be a conscious user, or otherwise believe
15
-    in digital freedom, make searx your default search engine or run it on your own server</p>
16
-
17
-<h2>Technical details - How does it work?</h2>
18
-
19
-<p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>,
20
-inspired by the <a href="https://beniz.github.io/seeks/">seeks project</a>.<br />
21
-It provides basic privacy by mixing your queries with searches on other platforms without storing search data. Queries are made using a POST request on every browser (except chrome*). Therefore they show up in neither our logs, nor your url history. In case of Chrome* users there is an exception, if searx used from the search bar it performs GET requests.<br />
22
-Searx can be added to your browser's search bar; moreover, it can be set as the default search engine.
23
-</p>
24
-
25
-<h2>How can I make it my own?</h2>
26
-
27
-<p>Searx appreciates your concern regarding logs, so take the <a href="https://github.com/asciimoo/searx">code</a> and run it yourself! <br />Add your Searx to this <a href="https://github.com/asciimoo/searx/wiki/Searx-instances">list</a> to help other people reclaim their privacy and make the Internet freer!
28
-<br />The more decentralized Internet is the more freedom we have!</p>
29
-
30
-
31
-<h2>More about searx</h2>
32
-
33
-<ul>
34
-    <li><a href="https://github.com/asciimoo/searx">github</a></li>
35
-    <li><a href="https://www.ohloh.net/p/searx/">ohloh</a></li>
36
-    <li><a href="https://twitter.com/Searx_engine">twitter</a></li>
37
-    <li>IRC: #searx @ freenode (<a href="https://kiwiirc.com/client/irc.freenode.com/searx">webclient</a>)</li>
38
-    <li><a href="https://www.transifex.com/projects/p/searx/">transifex</a></li>
39
-</ul>
40
-
41
-
42
-<hr />
43
-
44
-<h2 id="faq">FAQ</h2>
45
-
46
-<h3>How to add to firefox?</h3>
47
-<p><a href="#" onclick="window.external.AddSearchProvider(window.location.protocol + '//' + window.location.host + '{{ url_for('opensearch') }}');">Install</a> searx as a search engine on any version of Firefox! (javascript required)</p>
48
-
49
-<h2 id="dev_faq">Developer FAQ</h2>
50
-
51
-<h3>New engines?</h3>
52
-<ul>
53
-    <li>Edit your <a href="https://raw.github.com/asciimoo/searx/master/searx/settings.yml">settings.yml</a></li>
54
-    <li>Create your custom engine module, check the <a href="https://github.com/asciimoo/searx/blob/master/examples/basic_engine.py">example engine</a></li>
55
-</ul>
56
-<p>Don't forget to restart searx after config edit!</p>
57
-
58
-<h3>Installation/WSGI support?</h3>
59
-<p>See the <a href="https://github.com/asciimoo/searx/wiki/Installation">installation and setup</a> wiki page</p>
60
-
61
-<h3>How to debug engines?</h3>
62
-<p><a href="{{ url_for('stats') }}">Stats page</a> contains some useful data about the engines used.</p>
63
-
64
-</div>
3
+{% include '__common__/about.html' %}
65
 {% endblock %}
4
 {% endblock %}

+ 2
- 0
searx/utils.py Vedi File

175
     templates_path = os.path.join(root, 'templates')
175
     templates_path = os.path.join(root, 'templates')
176
 
176
 
177
     themes = os.listdir(os.path.join(static_path, 'themes'))
177
     themes = os.listdir(os.path.join(static_path, 'themes'))
178
+    if '__common__' in themes:
179
+        themes.remove('__common__')
178
     return static_path, templates_path, themes
180
     return static_path, templates_path, themes
179
 
181
 
180
 
182
 

+ 59
- 18
searx/webapp.py Vedi File

52
 from flask_babel import Babel, gettext, format_date, format_decimal
52
 from flask_babel import Babel, gettext, format_date, format_decimal
53
 from flask.json import jsonify
53
 from flask.json import jsonify
54
 from searx import settings, searx_dir, searx_debug
54
 from searx import settings, searx_dir, searx_debug
55
+from searx.exceptions import SearxException, SearxParameterException
55
 from searx.engines import (
56
 from searx.engines import (
56
     categories, engines, engine_shortcuts, get_engines_stats, initialize_engines
57
     categories, engines, engine_shortcuts, get_engines_stats, initialize_engines
57
 )
58
 )
226
     2. cookies
227
     2. cookies
227
     3. settings"""
228
     3. settings"""
228
 
229
 
229
-    if override and override in themes:
230
+    if override and (override in themes or override == '__common__'):
230
         return override
231
         return override
231
     theme_name = request.args.get('theme', request.preferences.get_value('theme'))
232
     theme_name = request.args.get('theme', request.preferences.get_value('theme'))
232
     if theme_name not in themes:
233
     if theme_name not in themes:
400
             request.user_plugins.append(plugin)
401
             request.user_plugins.append(plugin)
401
 
402
 
402
 
403
 
404
+def index_error(output_format, error_message):
405
+    if output_format == 'json':
406
+        return Response(json.dumps({'error': error_message}),
407
+                        mimetype='application/json')
408
+    elif output_format == 'csv':
409
+        response = Response('', mimetype='application/csv')
410
+        cont_disp = 'attachment;Filename=searx.csv'
411
+        response.headers.add('Content-Disposition', cont_disp)
412
+        return response
413
+    elif output_format == 'rss':
414
+        response_rss = render(
415
+            'opensearch_response_rss.xml',
416
+            results=[],
417
+            q=request.form['q'] if 'q' in request.form else '',
418
+            number_of_results=0,
419
+            base_url=get_base_url(),
420
+            error_message=error_message
421
+        )
422
+        return Response(response_rss, mimetype='text/xml')
423
+    else:
424
+        # html
425
+        request.errors.append(gettext('search error'))
426
+        return render(
427
+            'index.html',
428
+        )
429
+
430
+
403
 @app.route('/search', methods=['GET', 'POST'])
431
 @app.route('/search', methods=['GET', 'POST'])
404
 @app.route('/', methods=['GET', 'POST'])
432
 @app.route('/', methods=['GET', 'POST'])
405
 def index():
433
 def index():
408
     Supported outputs: html, json, csv, rss.
436
     Supported outputs: html, json, csv, rss.
409
     """
437
     """
410
 
438
 
439
+    # output_format
440
+    output_format = request.form.get('format', 'html')
441
+    if output_format not in ['html', 'csv', 'json', 'rss']:
442
+        output_format = 'html'
443
+
444
+    # check if there is query
411
     if request.form.get('q') is None:
445
     if request.form.get('q') is None:
412
-        return render(
413
-            'index.html',
414
-        )
446
+        if output_format == 'html':
447
+            return render(
448
+                'index.html',
449
+            )
450
+        else:
451
+            return index_error(output_format, 'No query'), 400
415
 
452
 
416
     # search
453
     # search
417
     search_query = None
454
     search_query = None
421
         # search = Search(search_query) #  without plugins
458
         # search = Search(search_query) #  without plugins
422
         search = SearchWithPlugins(search_query, request.user_plugins, request)
459
         search = SearchWithPlugins(search_query, request.user_plugins, request)
423
         result_container = search.search()
460
         result_container = search.search()
424
-    except:
425
-        request.errors.append(gettext('search error'))
461
+    except Exception as e:
462
+        # log exception
426
         logger.exception('search error')
463
         logger.exception('search error')
427
-        return render(
428
-            'index.html',
429
-        )
430
 
464
 
465
+        # is it an invalid input parameter or something else ?
466
+        if (issubclass(e.__class__, SearxParameterException)):
467
+            return index_error(output_format, e.message), 400
468
+        else:
469
+            return index_error(output_format, gettext('search error')), 500
470
+
471
+    # results
431
     results = result_container.get_ordered_results()
472
     results = result_container.get_ordered_results()
473
+    number_of_results = result_container.results_number()
474
+    if number_of_results < result_container.results_length():
475
+        number_of_results = 0
432
 
476
 
433
     # UI
477
     # UI
434
     advanced_search = request.form.get('advanced_search', None)
478
     advanced_search = request.form.get('advanced_search', None)
435
-    output_format = request.form.get('format', 'html')
436
-    if output_format not in ['html', 'csv', 'json', 'rss']:
437
-        output_format = 'html'
438
 
479
 
439
     # output
480
     # output
440
     for result in results:
481
     for result in results:
470
                 else:
511
                 else:
471
                     result['publishedDate'] = format_date(result['publishedDate'])
512
                     result['publishedDate'] = format_date(result['publishedDate'])
472
 
513
 
473
-    number_of_results = result_container.results_number()
474
-    if number_of_results < result_container.results_length():
475
-        number_of_results = 0
476
-
477
     if output_format == 'json':
514
     if output_format == 'json':
478
         return Response(json.dumps({'query': search_query.query,
515
         return Response(json.dumps({'query': search_query.query,
479
                                     'number_of_results': number_of_results,
516
                                     'number_of_results': number_of_results,
480
                                     'results': results,
517
                                     'results': results,
481
                                     'answers': list(result_container.answers),
518
                                     'answers': list(result_container.answers),
519
+                                    'corrections': list(result_container.corrections),
482
                                     'infoboxes': result_container.infoboxes,
520
                                     'infoboxes': result_container.infoboxes,
483
                                     'suggestions': list(result_container.suggestions)}),
521
                                     'suggestions': list(result_container.suggestions)}),
484
                         mimetype='application/json')
522
                         mimetype='application/json')
500
             results=results,
538
             results=results,
501
             q=request.form['q'],
539
             q=request.form['q'],
502
             number_of_results=number_of_results,
540
             number_of_results=number_of_results,
503
-            base_url=get_base_url()
541
+            base_url=get_base_url(),
542
+            override_theme='__common__',
504
         )
543
         )
505
         return Response(response_rss, mimetype='text/xml')
544
         return Response(response_rss, mimetype='text/xml')
506
 
545
 
515
         advanced_search=advanced_search,
554
         advanced_search=advanced_search,
516
         suggestions=result_container.suggestions,
555
         suggestions=result_container.suggestions,
517
         answers=result_container.answers,
556
         answers=result_container.answers,
557
+        corrections=result_container.corrections,
518
         infoboxes=result_container.infoboxes,
558
         infoboxes=result_container.infoboxes,
519
         paging=result_container.paging,
559
         paging=result_container.paging,
520
         current_language=search_query.lang,
560
         current_language=search_query.lang,
720
     ret = render('opensearch.xml',
760
     ret = render('opensearch.xml',
721
                  opensearch_method=method,
761
                  opensearch_method=method,
722
                  host=get_base_url(),
762
                  host=get_base_url(),
723
-                 urljoin=urljoin)
763
+                 urljoin=urljoin,
764
+                 override_theme='__common__')
724
 
765
 
725
     resp = Response(response=ret,
766
     resp = Response(response=ret,
726
                     status=200,
767
                     status=200,

+ 3
- 0
tests/unit/test_webapp.py Vedi File

36
         def search_mock(search_self, *args):
36
         def search_mock(search_self, *args):
37
             search_self.result_container = Mock(get_ordered_results=lambda: self.test_results,
37
             search_self.result_container = Mock(get_ordered_results=lambda: self.test_results,
38
                                                 answers=set(),
38
                                                 answers=set(),
39
+                                                corrections=set(),
39
                                                 suggestions=set(),
40
                                                 suggestions=set(),
40
                                                 infoboxes=[],
41
                                                 infoboxes=[],
41
                                                 results=self.test_results,
42
                                                 results=self.test_results,
45
         Search.search = search_mock
46
         Search.search = search_mock
46
 
47
 
47
         def get_current_theme_name_mock(override=None):
48
         def get_current_theme_name_mock(override=None):
49
+            if override:
50
+                return override
48
             return 'legacy'
51
             return 'legacy'
49
 
52
 
50
         webapp.get_current_theme_name = get_current_theme_name_mock
53
         webapp.get_current_theme_name = get_current_theme_name_mock