mirror of
				https://github.com/searxng/searxng
				synced 2024-01-01 19:24:07 +01:00 
			
		
		
		
	Merge branch 'master' into searchpy2
This commit is contained in:
		
						commit
						e48f07a367
					
				
					 63 changed files with 392 additions and 201 deletions
				
			
		
							
								
								
									
										46
									
								
								searx/answerers/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								searx/answerers/__init__.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | ||||||
|  | from os import listdir | ||||||
|  | from os.path import realpath, dirname, join, isdir | ||||||
|  | from searx.utils import load_module | ||||||
|  | from collections import defaultdict | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | answerers_dir = dirname(realpath(__file__)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def load_answerers(): | ||||||
|  |     answerers = [] | ||||||
|  |     for filename in listdir(answerers_dir): | ||||||
|  |         if not isdir(join(answerers_dir, filename)): | ||||||
|  |             continue | ||||||
|  |         module = load_module('answerer.py', join(answerers_dir, filename)) | ||||||
|  |         if not hasattr(module, 'keywords') or not isinstance(module.keywords, tuple) or not len(module.keywords): | ||||||
|  |             exit(2) | ||||||
|  |         answerers.append(module) | ||||||
|  |     return answerers | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def get_answerers_by_keywords(answerers): | ||||||
|  |     by_keyword = defaultdict(list) | ||||||
|  |     for answerer in answerers: | ||||||
|  |         for keyword in answerer.keywords: | ||||||
|  |             for keyword in answerer.keywords: | ||||||
|  |                 by_keyword[keyword].append(answerer.answer) | ||||||
|  |     return by_keyword | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def ask(query): | ||||||
|  |     results = [] | ||||||
|  |     query_parts = filter(None, query.query.split()) | ||||||
|  | 
 | ||||||
|  |     if query_parts[0] not in answerers_by_keywords: | ||||||
|  |         return results | ||||||
|  | 
 | ||||||
|  |     for answerer in answerers_by_keywords[query_parts[0]]: | ||||||
|  |         result = answerer(query) | ||||||
|  |         if result: | ||||||
|  |             results.append(result) | ||||||
|  |     return results | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | answerers = load_answerers() | ||||||
|  | answerers_by_keywords = get_answerers_by_keywords(answerers) | ||||||
							
								
								
									
										50
									
								
								searx/answerers/random/answerer.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								searx/answerers/random/answerer.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | ||||||
|  | import random | ||||||
|  | import string | ||||||
|  | from flask_babel import gettext | ||||||
|  | 
 | ||||||
|  | # required answerer attribute | ||||||
|  | # specifies which search query keywords triggers this answerer | ||||||
|  | keywords = ('random',) | ||||||
|  | 
 | ||||||
|  | random_int_max = 2**31 | ||||||
|  | 
 | ||||||
|  | random_string_letters = string.lowercase + string.digits + string.uppercase | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def random_string(): | ||||||
|  |     return u''.join(random.choice(random_string_letters) | ||||||
|  |                     for _ in range(random.randint(8, 32))) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def random_float(): | ||||||
|  |     return unicode(random.random()) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def random_int(): | ||||||
|  |     return unicode(random.randint(-random_int_max, random_int_max)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | random_types = {u'string': random_string, | ||||||
|  |                 u'int': random_int, | ||||||
|  |                 u'float': random_float} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # required answerer function | ||||||
|  | # can return a list of results (any result type) for a given query | ||||||
|  | def answer(query): | ||||||
|  |     parts = query.query.split() | ||||||
|  |     if len(parts) != 2: | ||||||
|  |         return [] | ||||||
|  | 
 | ||||||
|  |     if parts[1] not in random_types: | ||||||
|  |         return [] | ||||||
|  | 
 | ||||||
|  |     return [{'answer': random_types[parts[1]]()}] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # required answerer function | ||||||
|  | # returns information about the answerer | ||||||
|  | def self_info(): | ||||||
|  |     return {'name': gettext('Random value generator'), | ||||||
|  |             'description': gettext('Generate different random values'), | ||||||
|  |             'examples': [u'random {}'.format(x) for x in random_types]} | ||||||
							
								
								
									
										51
									
								
								searx/answerers/statistics/answerer.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								searx/answerers/statistics/answerer.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | ||||||
|  | from functools import reduce | ||||||
|  | from operator import mul | ||||||
|  | 
 | ||||||
|  | from flask_babel import gettext | ||||||
|  | 
 | ||||||
|  | keywords = ('min', | ||||||
|  |             'max', | ||||||
|  |             'avg', | ||||||
|  |             'sum', | ||||||
|  |             'prod') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # required answerer function | ||||||
|  | # can return a list of results (any result type) for a given query | ||||||
|  | def answer(query): | ||||||
|  |     parts = query.query.split() | ||||||
|  | 
 | ||||||
|  |     if len(parts) < 2: | ||||||
|  |         return [] | ||||||
|  | 
 | ||||||
|  |     try: | ||||||
|  |         args = map(float, parts[1:]) | ||||||
|  |     except: | ||||||
|  |         return [] | ||||||
|  | 
 | ||||||
|  |     func = parts[0] | ||||||
|  |     answer = None | ||||||
|  | 
 | ||||||
|  |     if func == 'min': | ||||||
|  |         answer = min(args) | ||||||
|  |     elif func == 'max': | ||||||
|  |         answer = max(args) | ||||||
|  |     elif func == 'avg': | ||||||
|  |         answer = sum(args) / len(args) | ||||||
|  |     elif func == 'sum': | ||||||
|  |         answer = sum(args) | ||||||
|  |     elif func == 'prod': | ||||||
|  |         answer = reduce(mul, args, 1) | ||||||
|  | 
 | ||||||
|  |     if answer is None: | ||||||
|  |         return [] | ||||||
|  | 
 | ||||||
|  |     return [{'answer': unicode(answer)}] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # required answerer function | ||||||
|  | # returns information about the answerer | ||||||
|  | def self_info(): | ||||||
|  |     return {'name': gettext('Statistics functions'), | ||||||
|  |             'description': gettext('Compute {functions} of the arguments').format(functions='/'.join(keywords)), | ||||||
|  |             'examples': ['avg 123 548 2.04 24.2']} | ||||||
|  | @ -16,13 +16,13 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >. | ||||||
| (C) 2013- by Adam Tauber, <asciimoo@gmail.com> | (C) 2013- by Adam Tauber, <asciimoo@gmail.com> | ||||||
| ''' | ''' | ||||||
| 
 | 
 | ||||||
| from os.path import realpath, dirname, splitext, join | from os.path import realpath, dirname | ||||||
| import sys | import sys | ||||||
| from imp import load_source |  | ||||||
| from flask_babel import gettext | from flask_babel import gettext | ||||||
| from operator import itemgetter | from operator import itemgetter | ||||||
| from searx import settings | from searx import settings | ||||||
| from searx import logger | from searx import logger | ||||||
|  | from searx.utils import load_module | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| logger = logger.getChild('engines') | logger = logger.getChild('engines') | ||||||
|  | @ -32,6 +32,7 @@ engine_dir = dirname(realpath(__file__)) | ||||||
| engines = {} | engines = {} | ||||||
| 
 | 
 | ||||||
| categories = {'general': []} | categories = {'general': []} | ||||||
|  | _initialized = False | ||||||
| 
 | 
 | ||||||
| engine_shortcuts = {} | engine_shortcuts = {} | ||||||
| engine_default_args = {'paging': False, | engine_default_args = {'paging': False, | ||||||
|  | @ -46,16 +47,6 @@ engine_default_args = {'paging': False, | ||||||
|                        'time_range_support': False} |                        'time_range_support': False} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def load_module(filename): |  | ||||||
|     modname = splitext(filename)[0] |  | ||||||
|     if modname in sys.modules: |  | ||||||
|         del sys.modules[modname] |  | ||||||
|     filepath = join(engine_dir, filename) |  | ||||||
|     module = load_source(modname, filepath) |  | ||||||
|     module.name = modname |  | ||||||
|     return module |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def load_engine(engine_data): | def load_engine(engine_data): | ||||||
| 
 | 
 | ||||||
|     if '_' in engine_data['name']: |     if '_' in engine_data['name']: | ||||||
|  | @ -65,7 +56,7 @@ def load_engine(engine_data): | ||||||
|     engine_module = engine_data['engine'] |     engine_module = engine_data['engine'] | ||||||
| 
 | 
 | ||||||
|     try: |     try: | ||||||
|         engine = load_module(engine_module + '.py') |         engine = load_module(engine_module + '.py', engine_dir) | ||||||
|     except: |     except: | ||||||
|         logger.exception('Cannot load engine "{}"'.format(engine_module)) |         logger.exception('Cannot load engine "{}"'.format(engine_module)) | ||||||
|         return None |         return None | ||||||
|  |  | ||||||
|  | @ -12,7 +12,6 @@ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from urlparse import urljoin | from urlparse import urljoin | ||||||
| from cgi import escape |  | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
| from lxml import html | from lxml import html | ||||||
| from searx.engines.xpath import extract_text | from searx.engines.xpath import extract_text | ||||||
|  | @ -135,7 +134,7 @@ def response(resp): | ||||||
|     for result in dom.xpath(xpath_results): |     for result in dom.xpath(xpath_results): | ||||||
|         link = result.xpath(xpath_link)[0] |         link = result.xpath(xpath_link)[0] | ||||||
|         href = urljoin(base_url, link.attrib.get('href')) |         href = urljoin(base_url, link.attrib.get('href')) | ||||||
|         title = escape(extract_text(link)) |         title = extract_text(link) | ||||||
| 
 | 
 | ||||||
|         results.append({'url': href, |         results.append({'url': href, | ||||||
|                         'title': title}) |                         'title': title}) | ||||||
|  |  | ||||||
|  | @ -16,7 +16,6 @@ | ||||||
| from lxml import etree | from lxml import etree | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
| from searx.utils import searx_useragent | from searx.utils import searx_useragent | ||||||
| from cgi import escape |  | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
| import re | import re | ||||||
| 
 | 
 | ||||||
|  | @ -94,7 +93,7 @@ def response(resp): | ||||||
|                 url = item.text |                 url = item.text | ||||||
| 
 | 
 | ||||||
|             elif item.attrib["name"] == "dcdescription": |             elif item.attrib["name"] == "dcdescription": | ||||||
|                 content = escape(item.text[:300]) |                 content = item.text[:300] | ||||||
|                 if len(item.text) > 300: |                 if len(item.text) > 300: | ||||||
|                     content += "..." |                     content += "..." | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -14,7 +14,6 @@ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
| from cgi import escape |  | ||||||
| from lxml import html | from lxml import html | ||||||
| from searx.engines.xpath import extract_text | from searx.engines.xpath import extract_text | ||||||
| 
 | 
 | ||||||
|  | @ -32,18 +31,14 @@ search_string = 'search?{query}&first={offset}' | ||||||
| def request(query, params): | def request(query, params): | ||||||
|     offset = (params['pageno'] - 1) * 10 + 1 |     offset = (params['pageno'] - 1) * 10 + 1 | ||||||
| 
 | 
 | ||||||
|     if params['language'] == 'all': |     if params['language'] != 'all': | ||||||
|         language = 'en-US' |         query = u'language:{} {}'.format(params['language'].split('_')[0].upper(), | ||||||
|     else: |                                          query.decode('utf-8')).encode('utf-8') | ||||||
|         language = params['language'].replace('_', '-') |  | ||||||
| 
 | 
 | ||||||
|     search_path = search_string.format( |     search_path = search_string.format( | ||||||
|         query=urlencode({'q': query, 'setmkt': language}), |         query=urlencode({'q': query}), | ||||||
|         offset=offset) |         offset=offset) | ||||||
| 
 | 
 | ||||||
|     params['cookies']['SRCHHPGUSR'] = \ |  | ||||||
|         'NEWWND=0&NRSLT=-1&SRCHLANG=' + language.split('-')[0] |  | ||||||
| 
 |  | ||||||
|     params['url'] = base_url + search_path |     params['url'] = base_url + search_path | ||||||
|     return params |     return params | ||||||
| 
 | 
 | ||||||
|  | @ -65,7 +60,7 @@ def response(resp): | ||||||
|         link = result.xpath('.//h3/a')[0] |         link = result.xpath('.//h3/a')[0] | ||||||
|         url = link.attrib.get('href') |         url = link.attrib.get('href') | ||||||
|         title = extract_text(link) |         title = extract_text(link) | ||||||
|         content = escape(extract_text(result.xpath('.//p'))) |         content = extract_text(result.xpath('.//p')) | ||||||
| 
 | 
 | ||||||
|         # append result |         # append result | ||||||
|         results.append({'url': url, |         results.append({'url': url, | ||||||
|  | @ -77,7 +72,7 @@ def response(resp): | ||||||
|         link = result.xpath('.//h2/a')[0] |         link = result.xpath('.//h2/a')[0] | ||||||
|         url = link.attrib.get('href') |         url = link.attrib.get('href') | ||||||
|         title = extract_text(link) |         title = extract_text(link) | ||||||
|         content = escape(extract_text(result.xpath('.//p'))) |         content = extract_text(result.xpath('.//p')) | ||||||
| 
 | 
 | ||||||
|         # append result |         # append result | ||||||
|         results.append({'url': url, |         results.append({'url': url, | ||||||
|  |  | ||||||
|  | @ -11,7 +11,6 @@ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from urlparse import urljoin | from urlparse import urljoin | ||||||
| from cgi import escape |  | ||||||
| from urllib import quote | from urllib import quote | ||||||
| from lxml import html | from lxml import html | ||||||
| from operator import itemgetter | from operator import itemgetter | ||||||
|  | @ -51,8 +50,8 @@ def response(resp): | ||||||
|     for result in search_res: |     for result in search_res: | ||||||
|         link = result.xpath('.//td[@class="torrent_name"]//a')[0] |         link = result.xpath('.//td[@class="torrent_name"]//a')[0] | ||||||
|         href = urljoin(url, link.attrib.get('href')) |         href = urljoin(url, link.attrib.get('href')) | ||||||
|         title = escape(extract_text(link)) |         title = extract_text(link) | ||||||
|         content = escape(extract_text(result.xpath('.//pre[@class="snippet"]')[0])) |         content = extract_text(result.xpath('.//pre[@class="snippet"]')[0]) | ||||||
|         content = "<br />".join(content.split("\n")) |         content = "<br />".join(content.split("\n")) | ||||||
| 
 | 
 | ||||||
|         filesize = result.xpath('.//span[@class="attr_val"]/text()')[0].split()[0] |         filesize = result.xpath('.//span[@class="attr_val"]/text()')[0].split()[0] | ||||||
|  |  | ||||||
|  | @ -14,7 +14,6 @@ | ||||||
| 
 | 
 | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
| from json import loads | from json import loads | ||||||
| from cgi import escape |  | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
| 
 | 
 | ||||||
| # engine dependent config | # engine dependent config | ||||||
|  | @ -57,7 +56,7 @@ def response(resp): | ||||||
|     for res in search_res['list']: |     for res in search_res['list']: | ||||||
|         title = res['title'] |         title = res['title'] | ||||||
|         url = res['url'] |         url = res['url'] | ||||||
|         content = escape(res['description']) |         content = res['description'] | ||||||
|         thumbnail = res['thumbnail_360_url'] |         thumbnail = res['thumbnail_360_url'] | ||||||
|         publishedDate = datetime.fromtimestamp(res['created_time'], None) |         publishedDate = datetime.fromtimestamp(res['created_time'], None) | ||||||
|         embedded = embedded_url.format(videoid=res['id']) |         embedded = embedded_url.format(videoid=res['id']) | ||||||
|  |  | ||||||
|  | @ -51,10 +51,11 @@ def response(resp): | ||||||
|             if url.startswith('http://'): |             if url.startswith('http://'): | ||||||
|                 url = 'https' + url[4:] |                 url = 'https' + url[4:] | ||||||
| 
 | 
 | ||||||
|             content = result['artist']['name'] +\ |             content = '{} - {} - {}'.format( | ||||||
|                 " • " +\ |                 result['artist']['name'], | ||||||
|                 result['album']['title'] +\ |                 result['album']['title'], | ||||||
|                 " • " + result['title'] |                 result['title']) | ||||||
|  | 
 | ||||||
|             embedded = embedded_url.format(audioid=result['id']) |             embedded = embedded_url.format(audioid=result['id']) | ||||||
| 
 | 
 | ||||||
|             # append result |             # append result | ||||||
|  |  | ||||||
|  | @ -12,7 +12,6 @@ | ||||||
| import re | import re | ||||||
| from urlparse import urljoin | from urlparse import urljoin | ||||||
| from lxml import html | from lxml import html | ||||||
| from cgi import escape |  | ||||||
| from searx.utils import is_valid_lang | from searx.utils import is_valid_lang | ||||||
| 
 | 
 | ||||||
| categories = ['general'] | categories = ['general'] | ||||||
|  | @ -62,8 +61,8 @@ def response(resp): | ||||||
| 
 | 
 | ||||||
|         results.append({ |         results.append({ | ||||||
|             'url': urljoin(resp.url, '?%d' % k), |             'url': urljoin(resp.url, '?%d' % k), | ||||||
|             'title': escape(from_result.text_content()), |             'title': from_result.text_content(), | ||||||
|             'content': escape('; '.join(to_results)) |             'content': '; '.join(to_results) | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
|     return results |     return results | ||||||
|  |  | ||||||
|  | @ -13,7 +13,6 @@ | ||||||
| from urllib import quote_plus | from urllib import quote_plus | ||||||
| from json import loads | from json import loads | ||||||
| from lxml import html | from lxml import html | ||||||
| from cgi import escape |  | ||||||
| from dateutil import parser | from dateutil import parser | ||||||
| 
 | 
 | ||||||
| # engine dependent config | # engine dependent config | ||||||
|  | @ -56,7 +55,7 @@ def response(resp): | ||||||
|         url = result.attrib.get('data-contenturl') |         url = result.attrib.get('data-contenturl') | ||||||
|         thumbnail = result.xpath('.//img')[0].attrib.get('src') |         thumbnail = result.xpath('.//img')[0].attrib.get('src') | ||||||
|         title = ''.join(result.xpath(title_xpath)) |         title = ''.join(result.xpath(title_xpath)) | ||||||
|         content = escape(''.join(result.xpath(content_xpath))) |         content = ''.join(result.xpath(content_xpath)) | ||||||
|         pubdate = result.xpath(pubdate_xpath)[0].attrib.get('datetime') |         pubdate = result.xpath(pubdate_xpath)[0].attrib.get('datetime') | ||||||
|         publishedDate = parser.parse(pubdate) |         publishedDate = parser.parse(pubdate) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,7 +9,6 @@ | ||||||
|  @parse        url, title, content |  @parse        url, title, content | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from cgi import escape |  | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
| from searx.engines.xpath import extract_text | from searx.engines.xpath import extract_text | ||||||
| from lxml import html | from lxml import html | ||||||
|  | @ -43,7 +42,7 @@ def response(resp): | ||||||
|         img_src = app.xpath('.//img/@src')[0] |         img_src = app.xpath('.//img/@src')[0] | ||||||
| 
 | 
 | ||||||
|         content = extract_text(app.xpath('./p')[0]) |         content = extract_text(app.xpath('./p')[0]) | ||||||
|         content = escape(content.replace(title, '', 1).strip()) |         content = content.replace(title, '', 1).strip() | ||||||
| 
 | 
 | ||||||
|         results.append({'url': url, |         results.append({'url': url, | ||||||
|                         'title': title, |                         'title': title, | ||||||
|  |  | ||||||
|  | @ -77,21 +77,13 @@ def response(resp): | ||||||
| 
 | 
 | ||||||
|         url = build_flickr_url(photo['owner'], photo['id']) |         url = build_flickr_url(photo['owner'], photo['id']) | ||||||
| 
 | 
 | ||||||
|         title = photo['title'] |  | ||||||
| 
 |  | ||||||
|         content = '<span class="photo-author">' +\ |  | ||||||
|                   photo['ownername'] +\ |  | ||||||
|                   '</span><br />' +\ |  | ||||||
|                   '<span class="description">' +\ |  | ||||||
|                   photo['description']['_content'] +\ |  | ||||||
|                   '</span>' |  | ||||||
| 
 |  | ||||||
|         # append result |         # append result | ||||||
|         results.append({'url': url, |         results.append({'url': url, | ||||||
|                         'title': title, |                         'title': photo['title'], | ||||||
|                         'img_src': img_src, |                         'img_src': img_src, | ||||||
|                         'thumbnail_src': thumbnail_src, |                         'thumbnail_src': thumbnail_src, | ||||||
|                         'content': content, |                         'content': photo['description']['_content'], | ||||||
|  |                         'author': photo['ownername'], | ||||||
|                         'template': 'images.html'}) |                         'template': 'images.html'}) | ||||||
| 
 | 
 | ||||||
|     # return results |     # return results | ||||||
|  |  | ||||||
|  | @ -102,16 +102,15 @@ def response(resp): | ||||||
| 
 | 
 | ||||||
|         title = photo.get('title', '') |         title = photo.get('title', '') | ||||||
| 
 | 
 | ||||||
|         content = '<span class="photo-author">' +\ |         author = photo['username'] | ||||||
|                   photo['username'] +\ |  | ||||||
|                   '</span><br />' |  | ||||||
| 
 | 
 | ||||||
|         # append result |         # append result | ||||||
|         results.append({'url': url, |         results.append({'url': url, | ||||||
|                         'title': title, |                         'title': title, | ||||||
|                         'img_src': img_src, |                         'img_src': img_src, | ||||||
|                         'thumbnail_src': thumbnail_src, |                         'thumbnail_src': thumbnail_src, | ||||||
|                         'content': content, |                         'content': '', | ||||||
|  |                         'author': author, | ||||||
|                         'template': 'images.html'}) |                         'template': 'images.html'}) | ||||||
| 
 | 
 | ||||||
|     return results |     return results | ||||||
|  |  | ||||||
|  | @ -10,7 +10,6 @@ | ||||||
|  @parse       url, title, content |  @parse       url, title, content | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from cgi import escape |  | ||||||
| from json import loads | from json import loads | ||||||
| from random import randint | from random import randint | ||||||
| from time import time | from time import time | ||||||
|  | @ -78,8 +77,8 @@ def response(resp): | ||||||
|     for result in response_json['results']: |     for result in response_json['results']: | ||||||
|         # append result |         # append result | ||||||
|         results.append({'url': result['url'], |         results.append({'url': result['url'], | ||||||
|                         'title': escape(result['title']), |                         'title': result['title'], | ||||||
|                         'content': escape(result['sum'])}) |                         'content': result['sum']}) | ||||||
| 
 | 
 | ||||||
|     # return results |     # return results | ||||||
|     return results |     return results | ||||||
|  |  | ||||||
|  | @ -12,7 +12,6 @@ | ||||||
| 
 | 
 | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
| from json import loads | from json import loads | ||||||
| from cgi import escape |  | ||||||
| 
 | 
 | ||||||
| # engine dependent config | # engine dependent config | ||||||
| categories = ['it'] | categories = ['it'] | ||||||
|  | @ -48,7 +47,7 @@ def response(resp): | ||||||
|         url = res['html_url'] |         url = res['html_url'] | ||||||
| 
 | 
 | ||||||
|         if res['description']: |         if res['description']: | ||||||
|             content = escape(res['description'][:500]) |             content = res['description'][:500] | ||||||
|         else: |         else: | ||||||
|             content = '' |             content = '' | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,7 +9,6 @@ | ||||||
| # @parse       url, title, content, suggestion | # @parse       url, title, content, suggestion | ||||||
| 
 | 
 | ||||||
| import re | import re | ||||||
| from cgi import escape |  | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
| from urlparse import urlparse, parse_qsl | from urlparse import urlparse, parse_qsl | ||||||
| from lxml import html, etree | from lxml import html, etree | ||||||
|  | @ -155,7 +154,7 @@ def parse_url(url_string, google_hostname): | ||||||
| def extract_text_from_dom(result, xpath): | def extract_text_from_dom(result, xpath): | ||||||
|     r = result.xpath(xpath) |     r = result.xpath(xpath) | ||||||
|     if len(r) > 0: |     if len(r) > 0: | ||||||
|         return escape(extract_text(r[0])) |         return extract_text(r[0]) | ||||||
|     return None |     return None | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -264,7 +263,7 @@ def response(resp): | ||||||
|     # parse suggestion |     # parse suggestion | ||||||
|     for suggestion in dom.xpath(suggestion_xpath): |     for suggestion in dom.xpath(suggestion_xpath): | ||||||
|         # append suggestion |         # append suggestion | ||||||
|         results.append({'suggestion': escape(extract_text(suggestion))}) |         results.append({'suggestion': extract_text(suggestion)}) | ||||||
| 
 | 
 | ||||||
|     # return results |     # return results | ||||||
|     return results |     return results | ||||||
|  |  | ||||||
|  | @ -11,7 +11,6 @@ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from urlparse import urljoin | from urlparse import urljoin | ||||||
| from cgi import escape |  | ||||||
| from urllib import quote | from urllib import quote | ||||||
| from lxml import html | from lxml import html | ||||||
| from operator import itemgetter | from operator import itemgetter | ||||||
|  | @ -57,7 +56,7 @@ def response(resp): | ||||||
|         link = result.xpath('.//a[@class="cellMainLink"]')[0] |         link = result.xpath('.//a[@class="cellMainLink"]')[0] | ||||||
|         href = urljoin(url, link.attrib['href']) |         href = urljoin(url, link.attrib['href']) | ||||||
|         title = extract_text(link) |         title = extract_text(link) | ||||||
|         content = escape(extract_text(result.xpath(content_xpath))) |         content = extract_text(result.xpath(content_xpath)) | ||||||
|         seed = extract_text(result.xpath('.//td[contains(@class, "green")]')) |         seed = extract_text(result.xpath('.//td[contains(@class, "green")]')) | ||||||
|         leech = extract_text(result.xpath('.//td[contains(@class, "red")]')) |         leech = extract_text(result.xpath('.//td[contains(@class, "red")]')) | ||||||
|         filesize_info = extract_text(result.xpath('.//td[contains(@class, "nobr")]')) |         filesize_info = extract_text(result.xpath('.//td[contains(@class, "nobr")]')) | ||||||
|  |  | ||||||
|  | @ -9,7 +9,6 @@ | ||||||
|  @parse        url, title, content, seed, leech, torrentfile |  @parse        url, title, content, seed, leech, torrentfile | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from cgi import escape |  | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
| from lxml import html | from lxml import html | ||||||
| from searx.engines.xpath import extract_text | from searx.engines.xpath import extract_text | ||||||
|  | @ -78,7 +77,7 @@ def response(resp): | ||||||
| 
 | 
 | ||||||
|         # torrent title |         # torrent title | ||||||
|         page_a = result.xpath(xpath_title)[0] |         page_a = result.xpath(xpath_title)[0] | ||||||
|         title = escape(extract_text(page_a)) |         title = extract_text(page_a) | ||||||
| 
 | 
 | ||||||
|         # link to the page |         # link to the page | ||||||
|         href = page_a.attrib.get('href') |         href = page_a.attrib.get('href') | ||||||
|  | @ -90,7 +89,7 @@ def response(resp): | ||||||
|         try: |         try: | ||||||
|             file_size, suffix = result.xpath(xpath_filesize)[0].split(' ') |             file_size, suffix = result.xpath(xpath_filesize)[0].split(' ') | ||||||
|             file_size = int(float(file_size) * get_filesize_mul(suffix)) |             file_size = int(float(file_size) * get_filesize_mul(suffix)) | ||||||
|         except Exception as e: |         except: | ||||||
|             file_size = None |             file_size = None | ||||||
| 
 | 
 | ||||||
|         # seed count |         # seed count | ||||||
|  | @ -105,7 +104,6 @@ def response(resp): | ||||||
|         # content string contains all information not included into template |         # content string contains all information not included into template | ||||||
|         content = 'Category: "{category}". Downloaded {downloads} times.' |         content = 'Category: "{category}". Downloaded {downloads} times.' | ||||||
|         content = content.format(category=category, downloads=downloads) |         content = content.format(category=category, downloads=downloads) | ||||||
|         content = escape(content) |  | ||||||
| 
 | 
 | ||||||
|         results.append({'url': href, |         results.append({'url': href, | ||||||
|                         'title': title, |                         'title': title, | ||||||
|  |  | ||||||
|  | @ -43,7 +43,7 @@ def response(resp): | ||||||
|         if 'display_name' not in r: |         if 'display_name' not in r: | ||||||
|             continue |             continue | ||||||
| 
 | 
 | ||||||
|         title = r['display_name'] |         title = r['display_name'] or u'' | ||||||
|         osm_type = r.get('osm_type', r.get('type')) |         osm_type = r.get('osm_type', r.get('type')) | ||||||
|         url = result_base_url.format(osm_type=osm_type, |         url = result_base_url.format(osm_type=osm_type, | ||||||
|                                      osm_id=r['osm_id']) |                                      osm_id=r['osm_id']) | ||||||
|  |  | ||||||
|  | @ -9,7 +9,6 @@ | ||||||
| # @parse       url, title, content, seed, leech, magnetlink | # @parse       url, title, content, seed, leech, magnetlink | ||||||
| 
 | 
 | ||||||
| from urlparse import urljoin | from urlparse import urljoin | ||||||
| from cgi import escape |  | ||||||
| from urllib import quote | from urllib import quote | ||||||
| from lxml import html | from lxml import html | ||||||
| from operator import itemgetter | from operator import itemgetter | ||||||
|  | @ -62,7 +61,7 @@ def response(resp): | ||||||
|         link = result.xpath('.//div[@class="detName"]//a')[0] |         link = result.xpath('.//div[@class="detName"]//a')[0] | ||||||
|         href = urljoin(url, link.attrib.get('href')) |         href = urljoin(url, link.attrib.get('href')) | ||||||
|         title = extract_text(link) |         title = extract_text(link) | ||||||
|         content = escape(extract_text(result.xpath(content_xpath))) |         content = extract_text(result.xpath(content_xpath)) | ||||||
|         seed, leech = result.xpath('.//td[@align="right"]/text()')[:2] |         seed, leech = result.xpath('.//td[@align="right"]/text()')[:2] | ||||||
| 
 | 
 | ||||||
|         # convert seed to int if possible |         # convert seed to int if possible | ||||||
|  |  | ||||||
|  | @ -11,7 +11,6 @@ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| import json | import json | ||||||
| from cgi import escape |  | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
| from urlparse import urlparse, urljoin | from urlparse import urlparse, urljoin | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
|  | @ -68,7 +67,7 @@ def response(resp): | ||||||
|             img_results.append(params) |             img_results.append(params) | ||||||
|         else: |         else: | ||||||
|             created = datetime.fromtimestamp(data['created_utc']) |             created = datetime.fromtimestamp(data['created_utc']) | ||||||
|             content = escape(data['selftext']) |             content = data['selftext'] | ||||||
|             if len(content) > 500: |             if len(content) > 500: | ||||||
|                 content = content[:500] + '...' |                 content = content[:500] + '...' | ||||||
|             params['content'] = content |             params['content'] = content | ||||||
|  |  | ||||||
|  | @ -44,20 +44,12 @@ def response(resp): | ||||||
|     # parse results |     # parse results | ||||||
|     for result in search_results.get('results', []): |     for result in search_results.get('results', []): | ||||||
|         href = result['url'] |         href = result['url'] | ||||||
|         title = "[" + result['type'] + "] " +\ |         title = "[{}] {} {}".format(result['type'], result['namespace'], result['name']) | ||||||
|                 result['namespace'] +\ |  | ||||||
|                 " " + result['name'] |  | ||||||
|         content = '<span class="highlight">[' +\ |  | ||||||
|                   result['type'] + "] " +\ |  | ||||||
|                   result['name'] + " " +\ |  | ||||||
|                   result['synopsis'] +\ |  | ||||||
|                   "</span><br />" +\ |  | ||||||
|                   result['description'] |  | ||||||
| 
 | 
 | ||||||
|         # append result |         # append result | ||||||
|         results.append({'url': href, |         results.append({'url': href, | ||||||
|                         'title': title, |                         'title': title, | ||||||
|                         'content': content}) |                         'content': result['description']}) | ||||||
| 
 | 
 | ||||||
|     # return results |     # return results | ||||||
|     return results |     return results | ||||||
|  |  | ||||||
|  | @ -9,7 +9,6 @@ | ||||||
| # @parse       url, title, content, seed, leech, magnetlink | # @parse       url, title, content, seed, leech, magnetlink | ||||||
| 
 | 
 | ||||||
| from urlparse import urljoin | from urlparse import urljoin | ||||||
| from cgi import escape |  | ||||||
| from urllib import quote | from urllib import quote | ||||||
| from lxml import html | from lxml import html | ||||||
| from operator import itemgetter | from operator import itemgetter | ||||||
|  |  | ||||||
|  | @ -46,10 +46,11 @@ def response(resp): | ||||||
|         if result['type'] == 'track': |         if result['type'] == 'track': | ||||||
|             title = result['name'] |             title = result['name'] | ||||||
|             url = result['external_urls']['spotify'] |             url = result['external_urls']['spotify'] | ||||||
|             content = result['artists'][0]['name'] +\ |             content = '{} - {} - {}'.format( | ||||||
|                 " • " +\ |                 result['artists'][0]['name'], | ||||||
|                 result['album']['name'] +\ |                 result['album']['name'], | ||||||
|                 " • " + result['name'] |                 result['name']) | ||||||
|  | 
 | ||||||
|             embedded = embedded_url.format(audioid=result['id']) |             embedded = embedded_url.format(audioid=result['id']) | ||||||
| 
 | 
 | ||||||
|             # append result |             # append result | ||||||
|  |  | ||||||
|  | @ -11,7 +11,6 @@ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from urlparse import urljoin | from urlparse import urljoin | ||||||
| from cgi import escape |  | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
| from lxml import html | from lxml import html | ||||||
| from searx.engines.xpath import extract_text | from searx.engines.xpath import extract_text | ||||||
|  | @ -48,8 +47,8 @@ def response(resp): | ||||||
|     for result in dom.xpath(results_xpath): |     for result in dom.xpath(results_xpath): | ||||||
|         link = result.xpath(link_xpath)[0] |         link = result.xpath(link_xpath)[0] | ||||||
|         href = urljoin(url, link.attrib.get('href')) |         href = urljoin(url, link.attrib.get('href')) | ||||||
|         title = escape(extract_text(link)) |         title = extract_text(link) | ||||||
|         content = escape(extract_text(result.xpath(content_xpath))) |         content = extract_text(result.xpath(content_xpath)) | ||||||
| 
 | 
 | ||||||
|         # append result |         # append result | ||||||
|         results.append({'url': href, |         results.append({'url': href, | ||||||
|  |  | ||||||
|  | @ -11,7 +11,6 @@ | ||||||
| # @todo        paging | # @todo        paging | ||||||
| 
 | 
 | ||||||
| from lxml import html | from lxml import html | ||||||
| from cgi import escape |  | ||||||
| from dateutil import parser | from dateutil import parser | ||||||
| from datetime import datetime, timedelta | from datetime import datetime, timedelta | ||||||
| import re | import re | ||||||
|  | @ -79,10 +78,10 @@ def response(resp): | ||||||
|         if re.match(r"^http(s|)://(www\.)?ixquick\.com/do/search\?.*$", url): |         if re.match(r"^http(s|)://(www\.)?ixquick\.com/do/search\?.*$", url): | ||||||
|             continue |             continue | ||||||
| 
 | 
 | ||||||
|         title = escape(extract_text(link)) |         title = extract_text(link) | ||||||
| 
 | 
 | ||||||
|         if result.xpath('./p[@class="desc clk"]'): |         if result.xpath('./p[@class="desc clk"]'): | ||||||
|             content = escape(extract_text(result.xpath('./p[@class="desc clk"]'))) |             content = extract_text(result.xpath('./p[@class="desc clk"]')) | ||||||
|         else: |         else: | ||||||
|             content = '' |             content = '' | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -10,7 +10,6 @@ | ||||||
|  @parse       url, title, content |  @parse       url, title, content | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from cgi import escape |  | ||||||
| from urllib import quote_plus | from urllib import quote_plus | ||||||
| from lxml import html | from lxml import html | ||||||
| from searx.languages import language_codes | from searx.languages import language_codes | ||||||
|  | @ -59,7 +58,7 @@ def response(resp): | ||||||
|         elif search_lang: |         elif search_lang: | ||||||
|             href = href + search_lang + '/' |             href = href + search_lang + '/' | ||||||
| 
 | 
 | ||||||
|         title = escape(extract_text(link)) |         title = extract_text(link) | ||||||
| 
 | 
 | ||||||
|         content = extract_text(result.xpath('.//div[contains(@class,"red")]')) |         content = extract_text(result.xpath('.//div[contains(@class,"red")]')) | ||||||
|         content = content + " - " |         content = content + " - " | ||||||
|  | @ -75,7 +74,7 @@ def response(resp): | ||||||
|         # append result |         # append result | ||||||
|         results.append({'url': href, |         results.append({'url': href, | ||||||
|                         'title': title, |                         'title': title, | ||||||
|                         'content': escape(content)}) |                         'content': content}) | ||||||
| 
 | 
 | ||||||
|     # return results |     # return results | ||||||
|     return results |     return results | ||||||
|  |  | ||||||
|  | @ -10,7 +10,6 @@ | ||||||
|  @parse       url, title, content |  @parse       url, title, content | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from cgi import escape |  | ||||||
| from json import loads | from json import loads | ||||||
| from urllib import urlencode, unquote | from urllib import urlencode, unquote | ||||||
| import re | import re | ||||||
|  | @ -78,7 +77,7 @@ def response(resp): | ||||||
| 
 | 
 | ||||||
|             # append result |             # append result | ||||||
|             results.append({'url': result['SourceUrl'], |             results.append({'url': result['SourceUrl'], | ||||||
|                             'title': escape(result['Title']), |                             'title': result['Title'], | ||||||
|                             'content': '', |                             'content': '', | ||||||
|                             'img_src': img_url, |                             'img_src': img_url, | ||||||
|                             'template': 'images.html'}) |                             'template': 'images.html'}) | ||||||
|  | @ -90,8 +89,8 @@ def response(resp): | ||||||
| 
 | 
 | ||||||
|             # append result |             # append result | ||||||
|             results.append({'url': result_url, |             results.append({'url': result_url, | ||||||
|                             'title': escape(result_title), |                             'title': result_title, | ||||||
|                             'content': escape(result_content)}) |                             'content': result_content}) | ||||||
| 
 | 
 | ||||||
|     # parse images |     # parse images | ||||||
|     for result in json.get('Images', []): |     for result in json.get('Images', []): | ||||||
|  | @ -100,7 +99,7 @@ def response(resp): | ||||||
| 
 | 
 | ||||||
|         # append result |         # append result | ||||||
|         results.append({'url': result['SourceUrl'], |         results.append({'url': result['SourceUrl'], | ||||||
|                         'title': escape(result['Title']), |                         'title': result['Title'], | ||||||
|                         'content': '', |                         'content': '', | ||||||
|                         'img_src': img_url, |                         'img_src': img_url, | ||||||
|                         'template': 'images.html'}) |                         'template': 'images.html'}) | ||||||
|  |  | ||||||
|  | @ -11,7 +11,6 @@ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| import re | import re | ||||||
| from cgi import escape |  | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
| from lxml import html | from lxml import html | ||||||
| from searx.engines.xpath import extract_text | from searx.engines.xpath import extract_text | ||||||
|  |  | ||||||
|  | @ -12,7 +12,6 @@ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| import re | import re | ||||||
| from cgi import escape |  | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
| from lxml import html | from lxml import html | ||||||
| from searx.engines.xpath import extract_text | from searx.engines.xpath import extract_text | ||||||
|  |  | ||||||
|  | @ -9,7 +9,6 @@ | ||||||
|  @parse       url, title, content |  @parse       url, title, content | ||||||
| """ | """ | ||||||
| import re | import re | ||||||
| from cgi import escape |  | ||||||
| from searx.utils import is_valid_lang | from searx.utils import is_valid_lang | ||||||
| 
 | 
 | ||||||
| categories = ['general'] | categories = ['general'] | ||||||
|  | @ -52,14 +51,14 @@ def request(query, params): | ||||||
| def response(resp): | def response(resp): | ||||||
|     results = [] |     results = [] | ||||||
|     results.append({ |     results.append({ | ||||||
|         'url': escape(web_url.format( |         'url': web_url.format( | ||||||
|             from_lang=resp.search_params['from_lang'][2], |             from_lang=resp.search_params['from_lang'][2], | ||||||
|             to_lang=resp.search_params['to_lang'][2], |             to_lang=resp.search_params['to_lang'][2], | ||||||
|             query=resp.search_params['query'])), |             query=resp.search_params['query']), | ||||||
|         'title': escape('[{0}-{1}] {2}'.format( |         'title': '[{0}-{1}] {2}'.format( | ||||||
|             resp.search_params['from_lang'][1], |             resp.search_params['from_lang'][1], | ||||||
|             resp.search_params['to_lang'][1], |             resp.search_params['to_lang'][1], | ||||||
|             resp.search_params['query'])), |             resp.search_params['query']), | ||||||
|         'content': escape(resp.json()['responseData']['translatedText']) |         'content': resp.json()['responseData']['translatedText'] | ||||||
|     }) |     }) | ||||||
|     return results |     return results | ||||||
|  |  | ||||||
|  | @ -8,7 +8,6 @@ | ||||||
| # @stable      no | # @stable      no | ||||||
| # @parse       url, infobox | # @parse       url, infobox | ||||||
| 
 | 
 | ||||||
| from cgi import escape |  | ||||||
| from json import loads | from json import loads | ||||||
| from time import time | from time import time | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
|  |  | ||||||
|  | @ -9,7 +9,6 @@ | ||||||
|  @parse       url, title, content |  @parse       url, title, content | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from cgi import escape |  | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
| from lxml import html | from lxml import html | ||||||
| from searx.search import logger | from searx.search import logger | ||||||
|  | @ -52,8 +51,8 @@ def response(resp): | ||||||
|     for result in dom.xpath(results_xpath): |     for result in dom.xpath(results_xpath): | ||||||
|         try: |         try: | ||||||
|             res = {'url': result.xpath(url_xpath)[0], |             res = {'url': result.xpath(url_xpath)[0], | ||||||
|                    'title': escape(''.join(result.xpath(title_xpath))), |                    'title': ''.join(result.xpath(title_xpath)), | ||||||
|                    'content': escape(''.join(result.xpath(content_xpath)))} |                    'content': ''.join(result.xpath(content_xpath))} | ||||||
|         except: |         except: | ||||||
|             logger.exception('yandex parse crash') |             logger.exception('yandex parse crash') | ||||||
|             continue |             continue | ||||||
|  |  | ||||||
|  | @ -27,5 +27,5 @@ def on_result(request, search, result): | ||||||
|             if doi.endswith(suffix): |             if doi.endswith(suffix): | ||||||
|                 doi = doi[:-len(suffix)] |                 doi = doi[:-len(suffix)] | ||||||
|         result['url'] = 'http://doai.io/' + doi |         result['url'] = 'http://doai.io/' + doi | ||||||
|         result['parsed_url'] = urlparse(ctx['result']['url']) |         result['parsed_url'] = urlparse(result['url']) | ||||||
|     return True |     return True | ||||||
|  |  | ||||||
|  | @ -49,28 +49,32 @@ class StringSetting(Setting): | ||||||
| class EnumStringSetting(Setting): | class EnumStringSetting(Setting): | ||||||
|     """Setting of a value which can only come from the given choices""" |     """Setting of a value which can only come from the given choices""" | ||||||
| 
 | 
 | ||||||
|  |     def _validate_selection(self, selection): | ||||||
|  |         if selection not in self.choices: | ||||||
|  |             raise ValidationException('Invalid value: "{0}"'.format(selection)) | ||||||
|  | 
 | ||||||
|     def _post_init(self): |     def _post_init(self): | ||||||
|         if not hasattr(self, 'choices'): |         if not hasattr(self, 'choices'): | ||||||
|             raise MissingArgumentException('Missing argument: choices') |             raise MissingArgumentException('Missing argument: choices') | ||||||
| 
 |         self._validate_selection(self.value) | ||||||
|         if self.value != '' and self.value not in self.choices: |  | ||||||
|             raise ValidationException('Invalid default value: {0}'.format(self.value)) |  | ||||||
| 
 | 
 | ||||||
|     def parse(self, data): |     def parse(self, data): | ||||||
|         if data not in self.choices and data != self.value: |         self._validate_selection(data) | ||||||
|             raise ValidationException('Invalid choice: {0}'.format(data)) |  | ||||||
|         self.value = data |         self.value = data | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class MultipleChoiceSetting(EnumStringSetting): | class MultipleChoiceSetting(EnumStringSetting): | ||||||
|     """Setting of values which can only come from the given choices""" |     """Setting of values which can only come from the given choices""" | ||||||
| 
 | 
 | ||||||
|  |     def _validate_selections(self, selections): | ||||||
|  |         for item in selections: | ||||||
|  |             if item not in self.choices: | ||||||
|  |                 raise ValidationException('Invalid value: "{0}"'.format(selections)) | ||||||
|  | 
 | ||||||
|     def _post_init(self): |     def _post_init(self): | ||||||
|         if not hasattr(self, 'choices'): |         if not hasattr(self, 'choices'): | ||||||
|             raise MissingArgumentException('Missing argument: choices') |             raise MissingArgumentException('Missing argument: choices') | ||||||
|         for item in self.value: |         self._validate_selections(self.value) | ||||||
|             if item not in self.choices: |  | ||||||
|                 raise ValidationException('Invalid default value: {0}'.format(self.value)) |  | ||||||
| 
 | 
 | ||||||
|     def parse(self, data): |     def parse(self, data): | ||||||
|         if data == '': |         if data == '': | ||||||
|  | @ -78,9 +82,7 @@ class MultipleChoiceSetting(EnumStringSetting): | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         elements = data.split(',') |         elements = data.split(',') | ||||||
|         for item in elements: |         self._validate_selections(elements) | ||||||
|             if item not in self.choices: |  | ||||||
|                 raise ValidationException('Invalid choice: {0}'.format(item)) |  | ||||||
|         self.value = elements |         self.value = elements | ||||||
| 
 | 
 | ||||||
|     def parse_form(self, data): |     def parse_form(self, data): | ||||||
|  | @ -214,11 +216,12 @@ class Preferences(object): | ||||||
|         super(Preferences, self).__init__() |         super(Preferences, self).__init__() | ||||||
| 
 | 
 | ||||||
|         self.key_value_settings = {'categories': MultipleChoiceSetting(['general'], choices=categories), |         self.key_value_settings = {'categories': MultipleChoiceSetting(['general'], choices=categories), | ||||||
|                                    'language': EnumStringSetting('all', choices=LANGUAGE_CODES), |                                    'language': EnumStringSetting(settings['search']['language'], | ||||||
|  |                                                                  choices=LANGUAGE_CODES), | ||||||
|                                    'locale': EnumStringSetting(settings['ui']['default_locale'], |                                    'locale': EnumStringSetting(settings['ui']['default_locale'], | ||||||
|                                                                choices=settings['locales'].keys()), |                                                                choices=settings['locales'].keys() + ['']), | ||||||
|                                    'autocomplete': EnumStringSetting(settings['search']['autocomplete'], |                                    'autocomplete': EnumStringSetting(settings['search']['autocomplete'], | ||||||
|                                                                      choices=autocomplete.backends.keys()), |                                                                      choices=autocomplete.backends.keys() + ['']), | ||||||
|                                    'image_proxy': MapSetting(settings['server']['image_proxy'], |                                    'image_proxy': MapSetting(settings['server']['image_proxy'], | ||||||
|                                                              map={'': settings['server']['image_proxy'], |                                                              map={'': settings['server']['image_proxy'], | ||||||
|                                                                   '0': False, |                                                                   '0': False, | ||||||
|  |  | ||||||
|  | @ -146,16 +146,17 @@ class ResultContainer(object): | ||||||
|                 self._number_of_results.append(result['number_of_results']) |                 self._number_of_results.append(result['number_of_results']) | ||||||
|                 results.remove(result) |                 results.remove(result) | ||||||
| 
 | 
 | ||||||
|         with RLock(): |         if engine_name in engines: | ||||||
|             engines[engine_name].stats['search_count'] += 1 |             with RLock(): | ||||||
|             engines[engine_name].stats['result_count'] += len(results) |                 engines[engine_name].stats['search_count'] += 1 | ||||||
|  |                 engines[engine_name].stats['result_count'] += len(results) | ||||||
| 
 | 
 | ||||||
|         if not results: |         if not results: | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         self.results[engine_name].extend(results) |         self.results[engine_name].extend(results) | ||||||
| 
 | 
 | ||||||
|         if not self.paging and engines[engine_name].paging: |         if not self.paging and engine_name in engines and engines[engine_name].paging: | ||||||
|             self.paging = True |             self.paging = True | ||||||
| 
 | 
 | ||||||
|         for i, result in enumerate(results): |         for i, result in enumerate(results): | ||||||
|  |  | ||||||
|  | @ -24,6 +24,7 @@ import searx.poolrequests as requests_lib | ||||||
| from searx.engines import ( | from searx.engines import ( | ||||||
|     categories, engines |     categories, engines | ||||||
| ) | ) | ||||||
|  | from searx.answerers import ask | ||||||
| from searx.utils import gen_useragent | from searx.utils import gen_useragent | ||||||
| from searx.query import RawTextQuery, SearchQuery | from searx.query import RawTextQuery, SearchQuery | ||||||
| from searx.results import ResultContainer | from searx.results import ResultContainer | ||||||
|  | @ -300,6 +301,14 @@ class Search(object): | ||||||
|         # start time |         # start time | ||||||
|         start_time = time() |         start_time = time() | ||||||
| 
 | 
 | ||||||
|  |         # answeres ? | ||||||
|  |         answerers_results = ask(self.search_query) | ||||||
|  | 
 | ||||||
|  |         if answerers_results: | ||||||
|  |             for results in answerers_results: | ||||||
|  |                 self.result_container.extend('answer', results) | ||||||
|  |             return self.result_container | ||||||
|  | 
 | ||||||
|         # init vars |         # init vars | ||||||
|         requests = [] |         requests = [] | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ general: | ||||||
| search: | search: | ||||||
|     safe_search : 0 # Filter results. 0: None, 1: Moderate, 2: Strict |     safe_search : 0 # Filter results. 0: None, 1: Moderate, 2: Strict | ||||||
|     autocomplete : "" # Existing autocomplete backends: "dbpedia", "duckduckgo", "google", "startpage", "wikipedia" - leave blank to turn it off by default |     autocomplete : "" # Existing autocomplete backends: "dbpedia", "duckduckgo", "google", "startpage", "wikipedia" - leave blank to turn it off by default | ||||||
|  |     language : "all" | ||||||
| 
 | 
 | ||||||
| server: | server: | ||||||
|     port : 8888 |     port : 8888 | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ general: | ||||||
| search: | search: | ||||||
|     safe_search : 0 |     safe_search : 0 | ||||||
|     autocomplete : "" |     autocomplete : "" | ||||||
|  |     language: "all" | ||||||
| 
 | 
 | ||||||
| server: | server: | ||||||
|     port : 11111 |     port : 11111 | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ $(document).ready(function() { | ||||||
|             var formData = $('#pagination form:last').serialize(); |             var formData = $('#pagination form:last').serialize(); | ||||||
|             if (formData) { |             if (formData) { | ||||||
|                 $('#pagination').html('<div class="loading-spinner"></div>'); |                 $('#pagination').html('<div class="loading-spinner"></div>'); | ||||||
|                 $.post('/', formData, function (data) { |                 $.post('./', formData, function (data) { | ||||||
|                     var body = $(data); |                     var body = $(data); | ||||||
|                     $('#pagination').remove(); |                     $('#pagination').remove(); | ||||||
|                     $('#main_results').append('<hr/>'); |                     $('#main_results').append('<hr/>'); | ||||||
|  |  | ||||||
|  | @ -3,14 +3,14 @@ | ||||||
|      xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/" |      xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/" | ||||||
|      xmlns:atom="http://www.w3.org/2005/Atom"> |      xmlns:atom="http://www.w3.org/2005/Atom"> | ||||||
|   <channel> |   <channel> | ||||||
|     <title>Searx search: {{ q }}</title> |     <title>Searx search: {{ q|e }}</title> | ||||||
|     <link>{{ base_url }}?q={{ q }}</link> |     <link>{{ base_url }}?q={{ q|e }}</link> | ||||||
|     <description>Search results for "{{ q }}" - searx</description> |     <description>Search results for "{{ q|e }}" - searx</description> | ||||||
|     <opensearch:totalResults>{{ number_of_results }}</opensearch:totalResults> |     <opensearch:totalResults>{{ number_of_results }}</opensearch:totalResults> | ||||||
|     <opensearch:startIndex>1</opensearch:startIndex> |     <opensearch:startIndex>1</opensearch:startIndex> | ||||||
|     <opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage> |     <opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage> | ||||||
|     <atom:link rel="search" type="application/opensearchdescription+xml" href="{{ base_url }}opensearch.xml"/> |     <atom:link rel="search" type="application/opensearchdescription+xml" href="{{ base_url }}opensearch.xml"/> | ||||||
|     <opensearch:Query role="request" searchTerms="{{ q }}" startPage="1" /> |     <opensearch:Query role="request" searchTerms="{{ q|e }}" startPage="1" /> | ||||||
|     {% for r in results %} |     {% for r in results %} | ||||||
|     <item> |     <item> | ||||||
|       <title>{{ r.title }}</title> |       <title>{{ r.title }}</title> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| {% extends "courgette/base.html" %} | {% extends "courgette/base.html" %} | ||||||
| {% block title %}{{ q }} - {% endblock %} | {% block title %}{{ q|e }} - {% endblock %} | ||||||
| {% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&format=rss&{% for category in selected_categories %}category_{{ category }}=1&{% endfor %}pageno={{ pageno }}">{% endblock %} | {% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&format=rss&{% for category in selected_categories %}category_{{ category }}=1&{% endfor %}pageno={{ pageno }}">{% endblock %} | ||||||
| {% block content %} | {% block content %} | ||||||
| <div class="right"><a href="{{ url_for('preferences') }}" id="preferences"><span>{{ _('preferences') }}</span></a></div> | <div class="right"><a href="{{ url_for('preferences') }}" id="preferences"><span>{{ _('preferences') }}</span></a></div> | ||||||
| <div class="small search center"> | <div class="small search center"> | ||||||
|  | @ -17,7 +17,7 @@ | ||||||
|             {% for output_type in ('csv', 'json', 'rss') %} |             {% for output_type in ('csv', 'json', 'rss') %} | ||||||
|             <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}"> |             <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}"> | ||||||
|                 <div class="left"> |                 <div class="left"> | ||||||
|                     <input type="hidden" name="q" value="{{ q }}" /> |                     <input type="hidden" name="q" value="{{ q|e }}" /> | ||||||
|                     <input type="hidden" name="format" value="{{ output_type }}" /> |                     <input type="hidden" name="format" value="{{ output_type }}" /> | ||||||
|                     {% for category in selected_categories %} |                     {% for category in selected_categories %} | ||||||
|                     <input type="hidden" name="category_{{ category }}" value="1"/> |                     <input type="hidden" name="category_{{ category }}" value="1"/> | ||||||
|  | @ -62,7 +62,7 @@ | ||||||
|         {% if pageno > 1 %} |         {% if pageno > 1 %} | ||||||
|             <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}"> |             <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}"> | ||||||
|                 <div class="left"> |                 <div class="left"> | ||||||
|                     <input type="hidden" name="q" value="{{ q }}" /> |                     <input type="hidden" name="q" value="{{ q|e }}" /> | ||||||
|                     {% for category in selected_categories %} |                     {% for category in selected_categories %} | ||||||
|                     <input type="hidden" name="category_{{ category }}" value="1"/> |                     <input type="hidden" name="category_{{ category }}" value="1"/> | ||||||
|                     {% endfor %} |                     {% endfor %} | ||||||
|  | @ -76,7 +76,7 @@ | ||||||
|                 {% for category in selected_categories %} |                 {% for category in selected_categories %} | ||||||
|                 <input type="hidden" name="category_{{ category }}" value="1"/> |                 <input type="hidden" name="category_{{ category }}" value="1"/> | ||||||
|                 {% endfor %} |                 {% endfor %} | ||||||
|                 <input type="hidden" name="q" value="{{ q }}" /> |                 <input type="hidden" name="q" value="{{ q|e }}" /> | ||||||
|                 <input type="hidden" name="pageno" value="{{ pageno+1 }}" /> |                 <input type="hidden" name="pageno" value="{{ pageno+1 }}" /> | ||||||
|                 <input type="submit" value="{{ _('next page') }} >>" /> |                 <input type="submit" value="{{ _('next page') }} >>" /> | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|  | @ -3,14 +3,14 @@ | ||||||
|      xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/" |      xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/" | ||||||
|      xmlns:atom="http://www.w3.org/2005/Atom"> |      xmlns:atom="http://www.w3.org/2005/Atom"> | ||||||
|   <channel> |   <channel> | ||||||
|     <title>Searx search: {{ q }}</title> |     <title>Searx search: {{ q|e }}</title> | ||||||
|     <link>{{ base_url }}?q={{ q }}</link> |     <link>{{ base_url }}?q={{ q|e }}</link> | ||||||
|     <description>Search results for "{{ q }}" - searx</description> |     <description>Search results for "{{ q|e }}" - searx</description> | ||||||
|     <opensearch:totalResults>{{ number_of_results }}</opensearch:totalResults> |     <opensearch:totalResults>{{ number_of_results }}</opensearch:totalResults> | ||||||
|     <opensearch:startIndex>1</opensearch:startIndex> |     <opensearch:startIndex>1</opensearch:startIndex> | ||||||
|     <opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage> |     <opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage> | ||||||
|     <atom:link rel="search" type="application/opensearchdescription+xml" href="{{ base_url }}opensearch.xml"/> |     <atom:link rel="search" type="application/opensearchdescription+xml" href="{{ base_url }}opensearch.xml"/> | ||||||
|     <opensearch:Query role="request" searchTerms="{{ q }}" startPage="1" /> |     <opensearch:Query role="request" searchTerms="{{ q|e }}" startPage="1" /> | ||||||
|     {% for r in results %} |     {% for r in results %} | ||||||
|     <item> |     <item> | ||||||
|       <title>{{ r.title }}</title> |       <title>{{ r.title }}</title> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| {% extends "legacy/base.html" %} | {% extends "legacy/base.html" %} | ||||||
| {% block title %}{{ q }} - {% endblock %} | {% block title %}{{ q|e }} - {% endblock %} | ||||||
| {% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&format=rss&{% for category in selected_categories %}category_{{ category }}=1&{% endfor %}pageno={{ pageno }}">{% endblock %} | {% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&format=rss&{% for category in selected_categories %}category_{{ category }}=1&{% endfor %}pageno={{ pageno }}">{% endblock %} | ||||||
| {% block content %} | {% block content %} | ||||||
| <div class="preferences_container right"><a href="{{ url_for('preferences') }}" id="preferences"><span>preferences</span></a></div> | <div class="preferences_container right"><a href="{{ url_for('preferences') }}" id="preferences"><span>preferences</span></a></div> | ||||||
| <div class="small search center"> | <div class="small search center"> | ||||||
|  | @ -18,7 +18,7 @@ | ||||||
|         {% for output_type in ('csv', 'json', 'rss') %} |         {% for output_type in ('csv', 'json', 'rss') %} | ||||||
|         <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}"> |         <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}"> | ||||||
|             <div class="left"> |             <div class="left"> | ||||||
|             <input type="hidden" name="q" value="{{ q }}" /> |             <input type="hidden" name="q" value="{{ q|e }}" /> | ||||||
|             <input type="hidden" name="format" value="{{ output_type }}" /> |             <input type="hidden" name="format" value="{{ output_type }}" /> | ||||||
|             {% for category in selected_categories %} |             {% for category in selected_categories %} | ||||||
|             <input type="hidden" name="category_{{ category }}" value="1"/> |             <input type="hidden" name="category_{{ category }}" value="1"/> | ||||||
|  | @ -73,7 +73,7 @@ | ||||||
|         {% if pageno > 1 %} |         {% if pageno > 1 %} | ||||||
|             <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}"> |             <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}"> | ||||||
|                 <div class="{% if rtl %}right{% else %}left{% endif %}"> |                 <div class="{% if rtl %}right{% else %}left{% endif %}"> | ||||||
|                 <input type="hidden" name="q" value="{{ q }}" /> |                 <input type="hidden" name="q" value="{{ q|e }}" /> | ||||||
|                 {% for category in selected_categories %} |                 {% for category in selected_categories %} | ||||||
|                 <input type="hidden" name="category_{{ category }}" value="1"/> |                 <input type="hidden" name="category_{{ category }}" value="1"/> | ||||||
|                 {% endfor %} |                 {% endfor %} | ||||||
|  | @ -87,7 +87,7 @@ | ||||||
|                 {% for category in selected_categories %} |                 {% for category in selected_categories %} | ||||||
|                 <input type="hidden" name="category_{{ category }}" value="1"/> |                 <input type="hidden" name="category_{{ category }}" value="1"/> | ||||||
|                 {% endfor %} |                 {% endfor %} | ||||||
|                 <input type="hidden" name="q" value="{{ q }}" /> |                 <input type="hidden" name="q" value="{{ q|e }}" /> | ||||||
|                 <input type="hidden" name="pageno" value="{{ pageno+1 }}" /> |                 <input type="hidden" name="pageno" value="{{ pageno+1 }}" /> | ||||||
|                 <input type="submit" value="{{ _('next page') }} >>" /> |                 <input type="submit" value="{{ _('next page') }} >>" /> | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|  | @ -1,3 +1,4 @@ | ||||||
|  | {% from 'oscar/macros.html' import icon %} | ||||||
| <!DOCTYPE html> | <!DOCTYPE html> | ||||||
| <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"{% if rtl %} dir="rtl"{% endif %}> | <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"{% if rtl %} dir="rtl"{% endif %}> | ||||||
| <head> | <head> | ||||||
|  | @ -54,6 +55,20 @@ | ||||||
| <body> | <body> | ||||||
|     {% include 'oscar/navbar.html' %} |     {% include 'oscar/navbar.html' %} | ||||||
|     <div class="container"> |     <div class="container"> | ||||||
|  |     {% if errors %} | ||||||
|  |         <div class="alert alert-danger fade in" role="alert"> | ||||||
|  |             <button class="close" data-dismiss="alert" type="button"> | ||||||
|  |                 <span aria-hidden="true">×</span> | ||||||
|  |                 <span class="sr-only">{{ _('Close') }}</span> | ||||||
|  |             </button> | ||||||
|  |             <strong class="lead">{{ icon('info-sign') }} {{ _('Error!') }}</strong> | ||||||
|  |             <ul> | ||||||
|  |             {% for message in errors %} | ||||||
|  |                 <li>{{ message }}</li> | ||||||
|  |             {% endfor %} | ||||||
|  |             </ul> | ||||||
|  |         </div> | ||||||
|  |     {% endif %} | ||||||
| 
 | 
 | ||||||
|     {% block site_alert_error %} |     {% block site_alert_error %} | ||||||
|     {% endblock %} |     {% endblock %} | ||||||
|  |  | ||||||
|  | @ -3,14 +3,14 @@ | ||||||
|      xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/" |      xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/" | ||||||
|      xmlns:atom="http://www.w3.org/2005/Atom"> |      xmlns:atom="http://www.w3.org/2005/Atom"> | ||||||
|   <channel> |   <channel> | ||||||
|     <title>Searx search: {{ q }}</title> |     <title>Searx search: {{ q|e }}</title> | ||||||
|     <link>{{ base_url }}?q={{ q }}</link> |     <link>{{ base_url }}?q={{ q|e }}</link> | ||||||
|     <description>Search results for "{{ q }}" - searx</description> |     <description>Search results for "{{ q|e }}" - searx</description> | ||||||
|     <opensearch:totalResults>{{ number_of_results }}</opensearch:totalResults> |     <opensearch:totalResults>{{ number_of_results }}</opensearch:totalResults> | ||||||
|     <opensearch:startIndex>1</opensearch:startIndex> |     <opensearch:startIndex>1</opensearch:startIndex> | ||||||
|     <opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage> |     <opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage> | ||||||
|     <atom:link rel="search" type="application/opensearchdescription+xml" href="{{ base_url }}opensearch.xml"/> |     <atom:link rel="search" type="application/opensearchdescription+xml" href="{{ base_url }}opensearch.xml"/> | ||||||
|     <opensearch:Query role="request" searchTerms="{{ q }}" startPage="1" /> |     <opensearch:Query role="request" searchTerms="{{ q|e }}" startPage="1" /> | ||||||
|     {% for r in results %} |     {% for r in results %} | ||||||
|     <item> |     <item> | ||||||
|       <title>{{ r.title }}</title> |       <title>{{ r.title }}</title> | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ | ||||||
|           <li class="active"><a href="#tab_general" role="tab" data-toggle="tab">{{ _('General') }}</a></li> |           <li class="active"><a href="#tab_general" role="tab" data-toggle="tab">{{ _('General') }}</a></li> | ||||||
|           <li><a href="#tab_engine" role="tab" data-toggle="tab">{{ _('Engines') }}</a></li> |           <li><a href="#tab_engine" role="tab" data-toggle="tab">{{ _('Engines') }}</a></li> | ||||||
|           <li><a href="#tab_plugins" role="tab" data-toggle="tab">{{ _('Plugins') }}</a></li> |           <li><a href="#tab_plugins" role="tab" data-toggle="tab">{{ _('Plugins') }}</a></li> | ||||||
|  |           {% if answerers %}<li><a href="#tab_answerers" role="tab" data-toggle="tab">{{ _('Answerers') }}</a></li>{% endif %} | ||||||
|           <li><a href="#tab_cookies" role="tab" data-toggle="tab">{{ _('Cookies') }}</a></li> |           <li><a href="#tab_cookies" role="tab" data-toggle="tab">{{ _('Cookies') }}</a></li> | ||||||
|         </ul> |         </ul> | ||||||
| 
 | 
 | ||||||
|  | @ -224,6 +225,34 @@ | ||||||
|                 </fieldset> |                 </fieldset> | ||||||
|             </div> |             </div> | ||||||
| 
 | 
 | ||||||
|  |             {% if answerers %} | ||||||
|  |             <div class="tab-pane active_if_nojs" id="tab_answerers"> | ||||||
|  |                 <noscript> | ||||||
|  |                     <h3>{{ _('Answerers') }}</h3> | ||||||
|  |                 </noscript> | ||||||
|  |                 <p class="text-muted" style="margin:20px 0;"> | ||||||
|  |                     {{ _('This is the list of searx\'s instant answering modules.') }} | ||||||
|  |                 </p> | ||||||
|  |                 <table class="table table-striped"> | ||||||
|  |                     <tr> | ||||||
|  |                         <th class="text-muted">{{ _('Name') }}</th> | ||||||
|  |                         <th class="text-muted">{{ _('Keywords') }}</th> | ||||||
|  |                         <th class="text-muted">{{ _('Description') }}</th> | ||||||
|  |                         <th class="text-muted">{{ _('Examples') }}</th> | ||||||
|  |                     </tr> | ||||||
|  | 
 | ||||||
|  |                     {% for answerer in answerers %} | ||||||
|  |                     <tr> | ||||||
|  |                         <td class="text-muted">{{ answerer.info.name }}</td> | ||||||
|  |                         <td class="text-muted">{{ answerer.keywords|join(', ') }}</td> | ||||||
|  |                         <td class="text-muted">{{ answerer.info.description }}</td> | ||||||
|  |                         <td class="text-muted">{{ answerer.info.examples|join(', ') }}</td> | ||||||
|  |                     </tr> | ||||||
|  |                     {% endfor %} | ||||||
|  |                 </table> | ||||||
|  |             </div> | ||||||
|  |             {% endif %} | ||||||
|  | 
 | ||||||
|             <div class="tab-pane active_if_nojs" id="tab_cookies"> |             <div class="tab-pane active_if_nojs" id="tab_cookies"> | ||||||
|                 <noscript> |                 <noscript> | ||||||
|                     <h3>{{ _('Cookies') }}</h3> |                     <h3>{{ _('Cookies') }}</h3> | ||||||
|  |  | ||||||
|  | @ -13,7 +13,12 @@ | ||||||
|             </div> |             </div> | ||||||
|             <div class="modal-body"> |             <div class="modal-body"> | ||||||
|                 <img class="img-responsive center-block" src="{% if result.thumbnail_src %}{{ image_proxify(result.thumbnail_src) }}{% else %}{{ image_proxify(result.img_src) }}{% endif %}" alt="{{ result.title|striptags }}"> |                 <img class="img-responsive center-block" src="{% if result.thumbnail_src %}{{ image_proxify(result.thumbnail_src) }}{% else %}{{ image_proxify(result.img_src) }}{% endif %}" alt="{{ result.title|striptags }}"> | ||||||
|                 {% if result.content %}<p class="result-content">{{ result.content|safe }}</p>{% endif %} |                 {% if result.author %}<span class="photo-author">{{ result.author }}</span><br />{% endif %} | ||||||
|  |                 {% if result.content %} | ||||||
|  |                     <p class="result-content"> | ||||||
|  |                         {{ result.content }} | ||||||
|  |                     </p> | ||||||
|  |                 {% endif %} | ||||||
|             </div> |             </div> | ||||||
|             <div class="modal-footer"> |             <div class="modal-footer"> | ||||||
|                 <div class="clearfix"></div> |                 <div class="clearfix"></div> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| {% extends "oscar/base.html" %} | {% extends "oscar/base.html" %} | ||||||
| {% block title %}{{ q }} - {% endblock %} | {% block title %}{{ q|e }} - {% endblock %} | ||||||
| {% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&format=rss&{% for category in selected_categories %}category_{{ category }}=1&{% endfor %}pageno={{ pageno }}&time_range={{ time_range }}">{% endblock %} | {% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&format=rss&{% for category in selected_categories %}category_{{ category }}=1&{% endfor %}pageno={{ pageno }}&time_range={{ time_range }}">{% endblock %} | ||||||
| {% block content %} | {% block content %} | ||||||
|     <div class="row"> |     <div class="row"> | ||||||
|         <div class="col-sm-8" id="main_results"> |         <div class="col-sm-8" id="main_results"> | ||||||
|  | @ -37,9 +37,9 @@ | ||||||
|             <div id="pagination"> |             <div id="pagination"> | ||||||
|                 <div class="pull-left"> |                 <div class="pull-left"> | ||||||
|                     <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="pull-left"> |                     <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="pull-left"> | ||||||
|                         <input type="hidden" name="q" value="{{ q }}" /> |                         <input type="hidden" name="q" value="{{ q|e }}" /> | ||||||
|                         {% for category in selected_categories %}<input type="hidden" name="category_{{ category }}" value="1"/>{% endfor %} |                         {% for category in selected_categories %}<input type="hidden" name="category_{{ category }}" value="1"/>{% endfor %} | ||||||
|                         <input type="hidden" name="q" value="{{ q }}" /> |                         <input type="hidden" name="q" value="{{ q|e }}" /> | ||||||
|                         <input type="hidden" name="pageno" value="{{ pageno+1 }}" /> |                         <input type="hidden" name="pageno" value="{{ pageno+1 }}" /> | ||||||
|                         <input type="hidden" name="time_range" value="{{ time_range }}" /> |                         <input type="hidden" name="time_range" value="{{ time_range }}" /> | ||||||
|                         <button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-backward"></span> {{ _('next page') }}</button> |                         <button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-backward"></span> {{ _('next page') }}</button> | ||||||
|  | @ -59,7 +59,7 @@ | ||||||
|             <div id="pagination"> |             <div id="pagination"> | ||||||
|                 <div class="pull-left"> |                 <div class="pull-left"> | ||||||
|                     <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="pull-left"> |                     <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="pull-left"> | ||||||
|                         <input type="hidden" name="q" value="{{ q }}" /> |                         <input type="hidden" name="q" value="{{ q|e }}" /> | ||||||
|                         {% for category in selected_categories %}<input type="hidden" name="category_{{ category }}" value="1"/>{% endfor %} |                         {% for category in selected_categories %}<input type="hidden" name="category_{{ category }}" value="1"/>{% endfor %} | ||||||
|                         <input type="hidden" name="pageno" value="{{ pageno-1 }}" /> |                         <input type="hidden" name="pageno" value="{{ pageno-1 }}" /> | ||||||
|                         <input type="hidden" name="time_range" value="{{ time_range }}" /> |                         <input type="hidden" name="time_range" value="{{ time_range }}" /> | ||||||
|  | @ -69,7 +69,7 @@ | ||||||
|                 <div class="pull-right"> |                 <div class="pull-right"> | ||||||
|                     <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}"  class="pull-left"> |                     <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}"  class="pull-left"> | ||||||
|                         {% for category in selected_categories %}<input type="hidden" name="category_{{ category }}" value="1"/>{% endfor %} |                         {% for category in selected_categories %}<input type="hidden" name="category_{{ category }}" value="1"/>{% endfor %} | ||||||
|                         <input type="hidden" name="q" value="{{ q }}" /> |                         <input type="hidden" name="q" value="{{ q|e }}" /> | ||||||
|                         <input type="hidden" name="pageno" value="{{ pageno+1 }}" /> |                         <input type="hidden" name="pageno" value="{{ pageno+1 }}" /> | ||||||
|                         <input type="hidden" name="time_range" value="{{ time_range }}" /> |                         <input type="hidden" name="time_range" value="{{ time_range }}" /> | ||||||
|                         <button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-forward"></span> {{ _('next page') }}</button> |                         <button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-forward"></span> {{ _('next page') }}</button> | ||||||
|  | @ -130,7 +130,7 @@ | ||||||
|                     <div class="clearfix"></div> |                     <div class="clearfix"></div> | ||||||
|                     {% for output_type in ('csv', 'json', 'rss') %} |                     {% for output_type in ('csv', 'json', 'rss') %} | ||||||
|                     <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="form-inline pull-{% if rtl %}right{% else %}left{% endif %} result_download"> |                     <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="form-inline pull-{% if rtl %}right{% else %}left{% endif %} result_download"> | ||||||
|                         <input type="hidden" name="q" value="{{ q }}"> |                         <input type="hidden" name="q" value="{{ q|e }}"> | ||||||
|                         <input type="hidden" name="format" value="{{ output_type }}"> |                         <input type="hidden" name="format" value="{{ output_type }}"> | ||||||
|                         {% for category in selected_categories %}<input type="hidden" name="category_{{ category }}" value="1">{% endfor %} |                         {% for category in selected_categories %}<input type="hidden" name="category_{{ category }}" value="1">{% endfor %} | ||||||
|                         <input type="hidden" name="pageno" value="{{ pageno }}"> |                         <input type="hidden" name="pageno" value="{{ pageno }}"> | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
|     {% endfor %} |     {% endfor %} | ||||||
| {% else %} | {% else %} | ||||||
| {% extends "pix-art/base.html" %} | {% extends "pix-art/base.html" %} | ||||||
| {% block title %}{{ q }} - {% endblock %} | {% block title %}{{ q|e }} - {% endblock %} | ||||||
| {% block meta %}{% endblock %} | {% block meta %}{% endblock %} | ||||||
| {% block content %} | {% block content %} | ||||||
| <div id="logo"><a href="./"><img src="{{ url_for('static', filename='img/searx-pixel-small.png') }}" alt="searx Logo"/></a></div> | <div id="logo"><a href="./"><img src="{{ url_for('static', filename='img/searx-pixel-small.png') }}" alt="searx Logo"/></a></div> | ||||||
|  | @ -25,7 +25,7 @@ | ||||||
|     </span> |     </span> | ||||||
|     <div id="pagination"> |     <div id="pagination"> | ||||||
|         <br /> |         <br /> | ||||||
|         <input type="button" onclick="load_more('{{ q }}', {{ pageno+1 }})" id="load_more" value="{{ _('Load more...') }}" /> |         <input type="button" onclick="load_more('{{ q|e }}', {{ pageno+1 }})" id="load_more" value="{{ _('Load more...') }}" /> | ||||||
|     </div> |     </div> | ||||||
| </div> | </div> | ||||||
| {% endblock %} | {% endblock %} | ||||||
|  |  | ||||||
|  | @ -6,7 +6,10 @@ import re | ||||||
| from babel.dates import format_date | from babel.dates import format_date | ||||||
| from codecs import getincrementalencoder | from codecs import getincrementalencoder | ||||||
| from HTMLParser import HTMLParser | from HTMLParser import HTMLParser | ||||||
|  | from imp import load_source | ||||||
|  | from os.path import splitext, join | ||||||
| from random import choice | from random import choice | ||||||
|  | import sys | ||||||
| 
 | 
 | ||||||
| from searx.version import VERSION_STRING | from searx.version import VERSION_STRING | ||||||
| from searx.languages import language_codes | from searx.languages import language_codes | ||||||
|  | @ -285,3 +288,13 @@ def is_valid_lang(lang): | ||||||
|             if l[1].lower() == lang.lower(): |             if l[1].lower() == lang.lower(): | ||||||
|                 return (True, l[0][:2], l[1].lower()) |                 return (True, l[0][:2], l[1].lower()) | ||||||
|         return False |         return False | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def load_module(filename, module_dir): | ||||||
|  |     modname = splitext(filename)[0] | ||||||
|  |     if modname in sys.modules: | ||||||
|  |         del sys.modules[modname] | ||||||
|  |     filepath = join(module_dir, filename) | ||||||
|  |     module = load_source(modname, filepath) | ||||||
|  |     module.name = modname | ||||||
|  |     return module | ||||||
|  |  | ||||||
|  | @ -40,7 +40,7 @@ except: | ||||||
|     logger.critical("cannot import dependency: pygments") |     logger.critical("cannot import dependency: pygments") | ||||||
|     from sys import exit |     from sys import exit | ||||||
|     exit(1) |     exit(1) | ||||||
| 
 | from cgi import escape | ||||||
| from datetime import datetime, timedelta | from datetime import datetime, timedelta | ||||||
| from urllib import urlencode | from urllib import urlencode | ||||||
| from urlparse import urlparse, urljoin | from urlparse import urlparse, urljoin | ||||||
|  | @ -62,11 +62,12 @@ from searx.utils import ( | ||||||
| ) | ) | ||||||
| from searx.version import VERSION_STRING | from searx.version import VERSION_STRING | ||||||
| from searx.languages import language_codes | from searx.languages import language_codes | ||||||
| from searx.search import Search, SearchWithPlugins, get_search_query_from_webapp | from searx.search import SearchWithPlugins, get_search_query_from_webapp | ||||||
| from searx.query import RawTextQuery, SearchQuery | from searx.query import RawTextQuery | ||||||
| from searx.autocomplete import searx_bang, backends as autocomplete_backends | from searx.autocomplete import searx_bang, backends as autocomplete_backends | ||||||
| from searx.plugins import plugins | from searx.plugins import plugins | ||||||
| from searx.preferences import Preferences, ValidationException | from searx.preferences import Preferences, ValidationException | ||||||
|  | from searx.answerers import answerers | ||||||
| 
 | 
 | ||||||
| # check if the pyopenssl, ndg-httpsclient, pyasn1 packages are installed. | # check if the pyopenssl, ndg-httpsclient, pyasn1 packages are installed. | ||||||
| # They are needed for SSL connection without trouble, see #298 | # They are needed for SSL connection without trouble, see #298 | ||||||
|  | @ -344,6 +345,8 @@ def render(template_name, override_theme=None, **kwargs): | ||||||
| 
 | 
 | ||||||
|     kwargs['cookies'] = request.cookies |     kwargs['cookies'] = request.cookies | ||||||
| 
 | 
 | ||||||
|  |     kwargs['errors'] = request.errors | ||||||
|  | 
 | ||||||
|     kwargs['instance_name'] = settings['general']['instance_name'] |     kwargs['instance_name'] = settings['general']['instance_name'] | ||||||
| 
 | 
 | ||||||
|     kwargs['results_on_new_tab'] = request.preferences.get_value('results_on_new_tab') |     kwargs['results_on_new_tab'] = request.preferences.get_value('results_on_new_tab') | ||||||
|  | @ -364,15 +367,16 @@ def render(template_name, override_theme=None, **kwargs): | ||||||
| 
 | 
 | ||||||
| @app.before_request | @app.before_request | ||||||
| def pre_request(): | def pre_request(): | ||||||
|     # merge GET, POST vars |     request.errors = [] | ||||||
|  | 
 | ||||||
|     preferences = Preferences(themes, categories.keys(), engines, plugins) |     preferences = Preferences(themes, categories.keys(), engines, plugins) | ||||||
|  |     request.preferences = preferences | ||||||
|     try: |     try: | ||||||
|         preferences.parse_cookies(request.cookies) |         preferences.parse_cookies(request.cookies) | ||||||
|     except: |     except: | ||||||
|         # TODO throw error message to the user |         request.errors.append(gettext('Invalid settings, please edit your preferences')) | ||||||
|         logger.warning('Invalid config') |  | ||||||
|     request.preferences = preferences |  | ||||||
| 
 | 
 | ||||||
|  |     # merge GET, POST vars | ||||||
|     # request.form |     # request.form | ||||||
|     request.form = dict(request.form.items()) |     request.form = dict(request.form.items()) | ||||||
|     for k, v in request.args.items(): |     for k, v in request.args.items(): | ||||||
|  | @ -397,7 +401,7 @@ def index(): | ||||||
|     Supported outputs: html, json, csv, rss. |     Supported outputs: html, json, csv, rss. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     if not request.args and not request.form: |     if request.form.get('q') is None: | ||||||
|         return render( |         return render( | ||||||
|             'index.html', |             'index.html', | ||||||
|         ) |         ) | ||||||
|  | @ -411,6 +415,8 @@ def index(): | ||||||
|         search = SearchWithPlugins(search_query, request) |         search = SearchWithPlugins(search_query, request) | ||||||
|         result_container = search.search() |         result_container = search.search() | ||||||
|     except: |     except: | ||||||
|  |         request.errors.append(gettext('search error')) | ||||||
|  |         logger.exception('search error') | ||||||
|         return render( |         return render( | ||||||
|             'index.html', |             'index.html', | ||||||
|         ) |         ) | ||||||
|  | @ -427,8 +433,10 @@ def index(): | ||||||
|     for result in results: |     for result in results: | ||||||
|         if output_format == 'html': |         if output_format == 'html': | ||||||
|             if 'content' in result and result['content']: |             if 'content' in result and result['content']: | ||||||
|                 result['content'] = highlight_content(result['content'][:1024], search_query.query.encode('utf-8')) |                 result['content'] = highlight_content(escape(result['content'][:1024]), | ||||||
|             result['title'] = highlight_content(result['title'], search_query.query.encode('utf-8')) |                                                       search_query.query.encode('utf-8')) | ||||||
|  |             result['title'] = highlight_content(escape(result['title'] or u''), | ||||||
|  |                                                 search_query.query.encode('utf-8')) | ||||||
|         else: |         else: | ||||||
|             if result.get('content'): |             if result.get('content'): | ||||||
|                 result['content'] = html_to_text(result['content']).strip() |                 result['content'] = html_to_text(result['content']).strip() | ||||||
|  | @ -572,7 +580,7 @@ def preferences(): | ||||||
|         try: |         try: | ||||||
|             request.preferences.parse_form(request.form) |             request.preferences.parse_form(request.form) | ||||||
|         except ValidationException: |         except ValidationException: | ||||||
|             # TODO use flash feature of flask |             request.errors.append(gettext('Invalid settings, please edit your preferences')) | ||||||
|             return resp |             return resp | ||||||
|         return request.preferences.save(resp) |         return request.preferences.save(resp) | ||||||
| 
 | 
 | ||||||
|  | @ -609,6 +617,7 @@ def preferences(): | ||||||
|                   language_codes=language_codes, |                   language_codes=language_codes, | ||||||
|                   engines_by_category=categories, |                   engines_by_category=categories, | ||||||
|                   stats=stats, |                   stats=stats, | ||||||
|  |                   answerers=[{'info': a.self_info(), 'keywords': a.keywords} for a in answerers], | ||||||
|                   disabled_engines=disabled_engines, |                   disabled_engines=disabled_engines, | ||||||
|                   autocomplete_backends=autocomplete_backends, |                   autocomplete_backends=autocomplete_backends, | ||||||
|                   shortcuts={y: x for x, y in engine_shortcuts.items()}, |                   shortcuts={y: x for x, y in engine_shortcuts.items()}, | ||||||
|  |  | ||||||
|  | @ -14,14 +14,12 @@ class TestBingEngine(SearxTestCase): | ||||||
|         params = bing.request(query, dicto) |         params = bing.request(query, dicto) | ||||||
|         self.assertTrue('url' in params) |         self.assertTrue('url' in params) | ||||||
|         self.assertTrue(query in params['url']) |         self.assertTrue(query in params['url']) | ||||||
|  |         self.assertTrue('language%3AFR' in params['url']) | ||||||
|         self.assertTrue('bing.com' in params['url']) |         self.assertTrue('bing.com' in params['url']) | ||||||
|         self.assertTrue('SRCHHPGUSR' in params['cookies']) |  | ||||||
|         self.assertTrue('fr' in params['cookies']['SRCHHPGUSR']) |  | ||||||
| 
 | 
 | ||||||
|         dicto['language'] = 'all' |         dicto['language'] = 'all' | ||||||
|         params = bing.request(query, dicto) |         params = bing.request(query, dicto) | ||||||
|         self.assertTrue('SRCHHPGUSR' in params['cookies']) |         self.assertTrue('language' not in params['url']) | ||||||
|         self.assertTrue('en' in params['cookies']['SRCHHPGUSR']) |  | ||||||
| 
 | 
 | ||||||
|     def test_response(self): |     def test_response(self): | ||||||
|         self.assertRaises(AttributeError, bing.response, None) |         self.assertRaises(AttributeError, bing.response, None) | ||||||
|  |  | ||||||
|  | @ -42,7 +42,7 @@ class TestDeezerEngine(SearxTestCase): | ||||||
|         self.assertEqual(len(results), 1) |         self.assertEqual(len(results), 1) | ||||||
|         self.assertEqual(results[0]['title'], 'Title of track') |         self.assertEqual(results[0]['title'], 'Title of track') | ||||||
|         self.assertEqual(results[0]['url'], 'https://www.deezer.com/track/1094042') |         self.assertEqual(results[0]['url'], 'https://www.deezer.com/track/1094042') | ||||||
|         self.assertEqual(results[0]['content'], 'Artist Name • Album Title • Title of track') |         self.assertEqual(results[0]['content'], 'Artist Name - Album Title - Title of track') | ||||||
|         self.assertTrue('100' in results[0]['embedded']) |         self.assertTrue('100' in results[0]['embedded']) | ||||||
| 
 | 
 | ||||||
|         json = r""" |         json = r""" | ||||||
|  |  | ||||||
|  | @ -52,7 +52,7 @@ class TestFlickrEngine(SearxTestCase): | ||||||
|         self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/66847915@N08/15751017054') |         self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/66847915@N08/15751017054') | ||||||
|         self.assertTrue('o.jpg' in results[0]['img_src']) |         self.assertTrue('o.jpg' in results[0]['img_src']) | ||||||
|         self.assertTrue('n.jpg' in results[0]['thumbnail_src']) |         self.assertTrue('n.jpg' in results[0]['thumbnail_src']) | ||||||
|         self.assertTrue('Owner' in results[0]['content']) |         self.assertTrue('Owner' in results[0]['author']) | ||||||
|         self.assertTrue('Description' in results[0]['content']) |         self.assertTrue('Description' in results[0]['content']) | ||||||
| 
 | 
 | ||||||
|         json = r""" |         json = r""" | ||||||
|  | @ -76,7 +76,7 @@ class TestFlickrEngine(SearxTestCase): | ||||||
|         self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/66847915@N08/15751017054') |         self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/66847915@N08/15751017054') | ||||||
|         self.assertTrue('z.jpg' in results[0]['img_src']) |         self.assertTrue('z.jpg' in results[0]['img_src']) | ||||||
|         self.assertTrue('z.jpg' in results[0]['thumbnail_src']) |         self.assertTrue('z.jpg' in results[0]['thumbnail_src']) | ||||||
|         self.assertTrue('Owner' in results[0]['content']) |         self.assertTrue('Owner' in results[0]['author']) | ||||||
|         self.assertTrue('Description' in results[0]['content']) |         self.assertTrue('Description' in results[0]['content']) | ||||||
| 
 | 
 | ||||||
|         json = r""" |         json = r""" | ||||||
|  | @ -100,7 +100,7 @@ class TestFlickrEngine(SearxTestCase): | ||||||
|         self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/66847915@N08/15751017054') |         self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/66847915@N08/15751017054') | ||||||
|         self.assertTrue('o.jpg' in results[0]['img_src']) |         self.assertTrue('o.jpg' in results[0]['img_src']) | ||||||
|         self.assertTrue('o.jpg' in results[0]['thumbnail_src']) |         self.assertTrue('o.jpg' in results[0]['thumbnail_src']) | ||||||
|         self.assertTrue('Owner' in results[0]['content']) |         self.assertTrue('Owner' in results[0]['author']) | ||||||
|         self.assertTrue('Description' in results[0]['content']) |         self.assertTrue('Description' in results[0]['content']) | ||||||
| 
 | 
 | ||||||
|         json = r""" |         json = r""" | ||||||
|  |  | ||||||
|  | @ -145,7 +145,7 @@ class TestFlickrNoapiEngine(SearxTestCase): | ||||||
|         self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/59729010@N00/14001294434') |         self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/59729010@N00/14001294434') | ||||||
|         self.assertIn('k.jpg', results[0]['img_src']) |         self.assertIn('k.jpg', results[0]['img_src']) | ||||||
|         self.assertIn('n.jpg', results[0]['thumbnail_src']) |         self.assertIn('n.jpg', results[0]['thumbnail_src']) | ||||||
|         self.assertIn('Owner', results[0]['content']) |         self.assertIn('Owner', results[0]['author']) | ||||||
| 
 | 
 | ||||||
|         # no n size, only the z size |         # no n size, only the z size | ||||||
|         json = """ |         json = """ | ||||||
|  | @ -188,7 +188,7 @@ class TestFlickrNoapiEngine(SearxTestCase): | ||||||
|         self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/59729010@N00/14001294434') |         self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/59729010@N00/14001294434') | ||||||
|         self.assertIn('z.jpg', results[0]['img_src']) |         self.assertIn('z.jpg', results[0]['img_src']) | ||||||
|         self.assertIn('z.jpg', results[0]['thumbnail_src']) |         self.assertIn('z.jpg', results[0]['thumbnail_src']) | ||||||
|         self.assertIn('Owner', results[0]['content']) |         self.assertIn('Owner', results[0]['author']) | ||||||
| 
 | 
 | ||||||
|         # no z or n size |         # no z or n size | ||||||
|         json = """ |         json = """ | ||||||
|  | @ -231,7 +231,7 @@ class TestFlickrNoapiEngine(SearxTestCase): | ||||||
|         self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/59729010@N00/14001294434') |         self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/59729010@N00/14001294434') | ||||||
|         self.assertIn('o.jpg', results[0]['img_src']) |         self.assertIn('o.jpg', results[0]['img_src']) | ||||||
|         self.assertIn('o.jpg', results[0]['thumbnail_src']) |         self.assertIn('o.jpg', results[0]['thumbnail_src']) | ||||||
|         self.assertIn('Owner', results[0]['content']) |         self.assertIn('Owner', results[0]['author']) | ||||||
| 
 | 
 | ||||||
|         # no image test |         # no image test | ||||||
|         json = """ |         json = """ | ||||||
|  |  | ||||||
|  | @ -98,7 +98,7 @@ class TestKickassEngine(SearxTestCase): | ||||||
|         self.assertEqual(len(results), 1) |         self.assertEqual(len(results), 1) | ||||||
|         self.assertEqual(results[0]['title'], 'This should be the title') |         self.assertEqual(results[0]['title'], 'This should be the title') | ||||||
|         self.assertEqual(results[0]['url'], 'https://kickass.cd/url.html') |         self.assertEqual(results[0]['url'], 'https://kickass.cd/url.html') | ||||||
|         self.assertEqual(results[0]['content'], 'Posted by riri in Other > Unsorted') |         self.assertEqual(results[0]['content'], 'Posted by riri in Other > Unsorted') | ||||||
|         self.assertEqual(results[0]['seed'], 10) |         self.assertEqual(results[0]['seed'], 10) | ||||||
|         self.assertEqual(results[0]['leech'], 1) |         self.assertEqual(results[0]['leech'], 1) | ||||||
|         self.assertEqual(results[0]['filesize'], 449) |         self.assertEqual(results[0]['filesize'], 449) | ||||||
|  | @ -381,7 +381,7 @@ class TestKickassEngine(SearxTestCase): | ||||||
|         self.assertEqual(len(results), 5) |         self.assertEqual(len(results), 5) | ||||||
|         self.assertEqual(results[0]['title'], 'This should be the title') |         self.assertEqual(results[0]['title'], 'This should be the title') | ||||||
|         self.assertEqual(results[0]['url'], 'https://kickass.cd/url.html') |         self.assertEqual(results[0]['url'], 'https://kickass.cd/url.html') | ||||||
|         self.assertEqual(results[0]['content'], 'Posted by riri in Other > Unsorted') |         self.assertEqual(results[0]['content'], 'Posted by riri in Other > Unsorted') | ||||||
|         self.assertEqual(results[0]['seed'], 10) |         self.assertEqual(results[0]['seed'], 10) | ||||||
|         self.assertEqual(results[0]['leech'], 1) |         self.assertEqual(results[0]['leech'], 1) | ||||||
|         self.assertEqual(results[0]['files'], 4) |         self.assertEqual(results[0]['files'], 4) | ||||||
|  |  | ||||||
|  | @ -56,9 +56,6 @@ class TestSearchcodeDocEngine(SearxTestCase): | ||||||
|         self.assertEqual(len(results), 1) |         self.assertEqual(len(results), 1) | ||||||
|         self.assertEqual(results[0]['title'], '[Type] Namespace test') |         self.assertEqual(results[0]['title'], '[Type] Namespace test') | ||||||
|         self.assertEqual(results[0]['url'], 'http://url') |         self.assertEqual(results[0]['url'], 'http://url') | ||||||
|         self.assertIn('Synopsis', results[0]['content']) |  | ||||||
|         self.assertIn('Type', results[0]['content']) |  | ||||||
|         self.assertIn('test', results[0]['content']) |  | ||||||
|         self.assertIn('Description', results[0]['content']) |         self.assertIn('Description', results[0]['content']) | ||||||
| 
 | 
 | ||||||
|         json = r""" |         json = r""" | ||||||
|  |  | ||||||
|  | @ -90,7 +90,7 @@ class TestSpotifyEngine(SearxTestCase): | ||||||
|         self.assertEqual(len(results), 1) |         self.assertEqual(len(results), 1) | ||||||
|         self.assertEqual(results[0]['title'], 'Title of track') |         self.assertEqual(results[0]['title'], 'Title of track') | ||||||
|         self.assertEqual(results[0]['url'], 'https://open.spotify.com/track/2GzvFiedqW8hgqUpWcASZa') |         self.assertEqual(results[0]['url'], 'https://open.spotify.com/track/2GzvFiedqW8hgqUpWcASZa') | ||||||
|         self.assertEqual(results[0]['content'], 'Artist Name • Album Title • Title of track') |         self.assertEqual(results[0]['content'], 'Artist Name - Album Title - Title of track') | ||||||
|         self.assertIn('1000', results[0]['embedded']) |         self.assertIn('1000', results[0]['embedded']) | ||||||
| 
 | 
 | ||||||
|         json = """ |         json = """ | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								tests/unit/test_answerers.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tests/unit/test_answerers.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | 
 | ||||||
|  | from mock import Mock | ||||||
|  | 
 | ||||||
|  | from searx.answerers import answerers | ||||||
|  | from searx.testing import SearxTestCase | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class AnswererTest(SearxTestCase): | ||||||
|  | 
 | ||||||
|  |     def test_unicode_input(self): | ||||||
|  |         query = Mock() | ||||||
|  |         unicode_payload = u'árvíztűrő tükörfúrógép' | ||||||
|  |         for answerer in answerers: | ||||||
|  |             query.query = u'{} {}'.format(answerer.keywords[0], unicode_payload) | ||||||
|  |             self.assertTrue(isinstance(answerer.answer(query), list)) | ||||||
|  | @ -5,6 +5,7 @@ from mock import Mock | ||||||
| from urlparse import ParseResult | from urlparse import ParseResult | ||||||
| from searx import webapp | from searx import webapp | ||||||
| from searx.testing import SearxTestCase | from searx.testing import SearxTestCase | ||||||
|  | from searx.search import Search | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class ViewsTestCase(SearxTestCase): | class ViewsTestCase(SearxTestCase): | ||||||
|  | @ -41,7 +42,7 @@ class ViewsTestCase(SearxTestCase): | ||||||
|                                                 results_number=lambda: 3, |                                                 results_number=lambda: 3, | ||||||
|                                                 results_length=lambda: len(self.test_results)) |                                                 results_length=lambda: len(self.test_results)) | ||||||
| 
 | 
 | ||||||
|         webapp.Search.search = search_mock |         Search.search = search_mock | ||||||
| 
 | 
 | ||||||
|         def get_current_theme_name_mock(override=None): |         def get_current_theme_name_mock(override=None): | ||||||
|             return 'legacy' |             return 'legacy' | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Alexandre Flament
						Alexandre Flament