Merge branch 'master' into flask_perimeter

This commit is contained in:
Alexandre Flament 2017-01-27 13:47:59 +00:00 committed by GitHub
commit e67dfaaac7
38 changed files with 370 additions and 566 deletions

View File

@ -6,12 +6,12 @@ SEARX_DIR="$BASE_DIR/searx"
ACTION=$1 ACTION=$1
update_packages() { update_packages() {
pip install --upgrade -r "$BASE_DIR/requirements.txt" pip install -r "$BASE_DIR/requirements.txt"
} }
update_dev_packages() { update_dev_packages() {
update_packages update_packages
pip install --upgrade -r "$BASE_DIR/requirements-dev.txt" pip install -r "$BASE_DIR/requirements-dev.txt"
} }
check_geckodriver() { check_geckodriver() {

View File

@ -112,6 +112,7 @@ title_xpath = './/h3'
content_xpath = './/span[@class="st"]' content_xpath = './/span[@class="st"]'
content_misc_xpath = './/div[@class="f slp"]' content_misc_xpath = './/div[@class="f slp"]'
suggestion_xpath = '//p[@class="_Bmc"]' suggestion_xpath = '//p[@class="_Bmc"]'
spelling_suggestion_xpath = '//a[@class="spell"]'
# map : detail location # map : detail location
map_address_xpath = './/div[@class="s"]//table//td[2]/span/text()' map_address_xpath = './/div[@class="s"]//table//td[2]/span/text()'
@ -220,6 +221,12 @@ def response(resp):
instant_answer = dom.xpath('//div[@id="_vBb"]//text()') instant_answer = dom.xpath('//div[@id="_vBb"]//text()')
if instant_answer: if instant_answer:
results.append({'answer': u' '.join(instant_answer)}) results.append({'answer': u' '.join(instant_answer)})
try:
results_num = int(dom.xpath('//div[@id="resultStats"]//text()')[0]
.split()[1].replace(',', ''))
results.append({'number_of_results': results_num})
except:
pass
# parse results # parse results
for result in dom.xpath(results_xpath): for result in dom.xpath(results_xpath):
@ -275,6 +282,9 @@ def response(resp):
# append suggestion # append suggestion
results.append({'suggestion': extract_text(suggestion)}) results.append({'suggestion': extract_text(suggestion)})
for correction in dom.xpath(spelling_suggestion_xpath):
results.append({'correction': extract_text(correction)})
# return results # return results
return results return results

View File

@ -31,8 +31,6 @@ if xpath_results is a string element, then it's already done
def extract_text(xpath_results): def extract_text(xpath_results):
if type(xpath_results) == list: if type(xpath_results) == list:
# it's list of result : concat everything using recursive call # it's list of result : concat everything using recursive call
if not xpath_results:
raise Exception('Empty url resultset')
result = '' result = ''
for e in xpath_results: for e in xpath_results:
result = result + extract_text(e) result = result + extract_text(e)
@ -48,6 +46,8 @@ def extract_text(xpath_results):
def extract_url(xpath_results, search_url): def extract_url(xpath_results, search_url):
if xpath_results == []:
raise Exception('Empty url resultset')
url = extract_text(xpath_results) url = extract_text(xpath_results)
if url.startswith('//'): if url.startswith('//'):
@ -103,8 +103,8 @@ def response(resp):
if results_xpath: if results_xpath:
for result in dom.xpath(results_xpath): for result in dom.xpath(results_xpath):
url = extract_url(result.xpath(url_xpath), search_url) url = extract_url(result.xpath(url_xpath), search_url)
title = extract_text(result.xpath(title_xpath)[0]) title = extract_text(result.xpath(title_xpath))
content = extract_text(result.xpath(content_xpath)[0]) content = extract_text(result.xpath(content_xpath))
results.append({'url': url, 'title': title, 'content': content}) results.append({'url': url, 'title': title, 'content': content})
else: else:
for url, title, content in zip( for url, title, content in zip(

32
searx/exceptions.py Normal file
View File

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

View File

@ -130,7 +130,8 @@ class MapSetting(Setting):
self.key = data self.key = data
def save(self, name, resp): def save(self, name, resp):
resp.set_cookie(name, bytes(self.key), max_age=COOKIE_MAX_AGE) if hasattr(self, 'key'):
resp.set_cookie(name, bytes(self.key), max_age=COOKIE_MAX_AGE)
class SwitchableSetting(Setting): class SwitchableSetting(Setting):

View File

@ -127,6 +127,7 @@ class ResultContainer(object):
self.infoboxes = [] self.infoboxes = []
self.suggestions = set() self.suggestions = set()
self.answers = set() self.answers = set()
self.corrections = set()
self._number_of_results = [] self._number_of_results = []
self._ordered = False self._ordered = False
self.paging = False self.paging = False
@ -140,6 +141,9 @@ class ResultContainer(object):
elif 'answer' in result: elif 'answer' in result:
self.answers.add(result['answer']) self.answers.add(result['answer'])
results.remove(result) results.remove(result)
elif 'correction' in result:
self.corrections.add(result['correction'])
results.remove(result)
elif 'infobox' in result: elif 'infobox' in result:
self._merge_infobox(result) self._merge_infobox(result)
results.remove(result) results.remove(result)

View File

@ -31,11 +31,16 @@ from searx.query import RawTextQuery, SearchQuery
from searx.results import ResultContainer from searx.results import ResultContainer
from searx import logger from searx import logger
from searx.plugins import plugins from searx.plugins import plugins
from searx.languages import language_codes
from searx.exceptions import SearxParameterException
logger = logger.getChild('search') logger = logger.getChild('search')
number_of_searches = 0 number_of_searches = 0
language_code_set = set(l[0].lower() for l in language_codes)
language_code_set.add('all')
def send_http_request(engine, request_params, start_time, timeout_limit): def send_http_request(engine, request_params, start_time, timeout_limit):
# for page_load_time stats # for page_load_time stats
@ -182,33 +187,13 @@ def default_request_params():
def get_search_query_from_webapp(preferences, form): def get_search_query_from_webapp(preferences, form):
query = None # no text for the query ?
query_engines = [] if not form.get('q'):
query_categories = [] raise SearxParameterException('q', '')
query_pageno = 1
query_lang = 'all'
query_time_range = None
# set blocked engines # set blocked engines
disabled_engines = preferences.engines.get_disabled() disabled_engines = preferences.engines.get_disabled()
# set specific language if set
query_lang = preferences.get_value('language')
# safesearch
query_safesearch = preferences.get_value('safesearch')
# TODO better exceptions
if not form.get('q'):
raise Exception('noquery')
# set pagenumber
pageno_param = form.get('pageno', '1')
if not pageno_param.isdigit() or int(pageno_param) < 1:
pageno_param = 1
query_pageno = int(pageno_param)
# parse query, if tags are set, which change # parse query, if tags are set, which change
# the serch engine or search-language # the serch engine or search-language
raw_text_query = RawTextQuery(form['q'], disabled_engines) raw_text_query = RawTextQuery(form['q'], disabled_engines)
@ -217,6 +202,13 @@ def get_search_query_from_webapp(preferences, form):
# set query # set query
query = raw_text_query.getSearchQuery() query = raw_text_query.getSearchQuery()
# get and check page number
pageno_param = form.get('pageno', '1')
if not pageno_param.isdigit() or int(pageno_param) < 1:
raise SearxParameterException('pageno', pageno_param)
query_pageno = int(pageno_param)
# get language
# set specific language if set on request, query or preferences # set specific language if set on request, query or preferences
# TODO support search with multible languages # TODO support search with multible languages
if len(raw_text_query.languages): if len(raw_text_query.languages):
@ -226,10 +218,37 @@ def get_search_query_from_webapp(preferences, form):
else: else:
query_lang = preferences.get_value('language') query_lang = preferences.get_value('language')
# check language
if query_lang.lower() not in language_code_set:
raise SearxParameterException('language', query_lang)
# get safesearch
if 'safesearch' in form:
query_safesearch = form.get('safesearch')
# first check safesearch
if not query_safesearch.isdigit():
raise SearxParameterException('safesearch', query_safesearch)
query_safesearch = int(query_safesearch)
else:
query_safesearch = preferences.get_value('safesearch')
# safesearch : second check
if query_safesearch < 0 or query_safesearch > 2:
raise SearxParameterException('safesearch', query_safesearch)
# get time_range
query_time_range = form.get('time_range') query_time_range = form.get('time_range')
# check time_range
if query_time_range not in ('None', None, '', 'day', 'week', 'month', 'year'):
raise SearxParameterException('time_range', query_time_range)
# query_engines
query_engines = raw_text_query.engines query_engines = raw_text_query.engines
# query_categories
query_categories = []
# if engines are calculated from query, # if engines are calculated from query,
# set categories by using that informations # set categories by using that informations
if query_engines and raw_text_query.specific: if query_engines and raw_text_query.specific:

View File

@ -462,6 +462,17 @@ engines:
# - ... # - ...
# disabled : True # disabled : True
- name : semantic scholar
engine : xpath
paging : True
search_url : https://www.semanticscholar.org/search?q={query}&sort=relevance&page={pageno}&ae=false
results_xpath : //article
url_xpath : .//div[@class="search-result-title"]/a/@href
title_xpath : .//div[@class="search-result-title"]/a
content_xpath : .//div[@class="search-result-abstract"]
shortcut : se
categories : science
- name : spotify - name : spotify
engine : spotify engine : spotify
shortcut : stf shortcut : stf

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -29,29 +29,6 @@
font-weight: bold; font-weight: bold;
border-bottom: @light-green 5px solid; border-bottom: @light-green 5px solid;
} }
select {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
font-size: 1.2rem;
font-weight:normal;
background-color: white;
border: @mild-gray 1px solid;
color: @dark-gray;
padding-bottom: 0.4rem;
padding-top: 0.4rem;
padding-left: 1rem;
padding-right: 5rem;
margin-right: 0.5rem;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAQAAACR313BAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZ
cwAABFkAAARZAVnbJUkAAAAHdElNRQfgBxgLDwB20OFsAAAAbElEQVQY073OsQ3CMAAEwJMYwJGn
sAehpoXJItltBkmcdZBYgIIiQoLglnz3ui+eP+bk5uneteTMZJa6OJuIqvYzSJoqwqBq8gdmTTW8
6/dghxAUq4xsVYT9laBYXCw93Aajh7GPEF23t4fkBYevGFTANkPRAAAAJXRFWHRkYXRlOmNyZWF0
ZQAyMDE2LTA3LTI0VDExOjU1OjU4KzAyOjAwRFqFOQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNi0w
Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
7jwaAAAAAElFTkSuQmCC) 96% no-repeat;
}
} }
#check-advanced { #check-advanced {

View File

@ -1,89 +1,31 @@
.navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus{ .searx-navbar {
background: @black; background: @black;
color: @light-green; height: 2.3rem;
} font-size: 1.3rem;
line-height: 1.3rem;
padding: 0.5rem;
font-weight: bold;
margin-bottom: 0.8rem;
.navbar > li > a { a, a:hover {
padding: 0; margin-right: 2.0rem;
margin: 0; color: white;
} text-decoration: none;
.navbar-nav > li > a {
background: @black;
padding: 0 8px;
margin: 0;
line-height: 30px;
}
.navbar, .navbar-default, .menu {
background-color: @black;
border: none;
border-top: 4px solid @light-green;
padding-top: 5px;
color: @dim-gray !important;
font-weight: 700;
font-size: 1.1em;
text-transform: lowercase;
margin-bottom: 24px;
height: 30px;
line-height: 30px;
.navbar-nav > li > a{
color: @dim-gray;
} }
.navbar-brand{ .instance a {
font-weight: 700;
color: @light-green; color: @light-green;
line-height: 30px; margin-left: 2.0rem;
padding: 0 30px;
margin: 0;
}
z-index: 10;
}
// Hover color
// http://stackoverflow.com/users/114029/leniel-macaferi
.navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus {
color: @light-green;
background: @black;
}
.navbar-toggle {
margin-top: 0;
}
.menu {
margin: 0;
padding: 0;
position: absolute;
top: 4px;
border: 0;
z-index: 1000000000;
height: 40px;
line-height: 40px;
ul {
padding: 0;
margin: 0;
li {
padding: 0 0.6em;
margin: 0;
float: left;
list-style: none;
a {
color: @dim-gray;
}
}
li.active a {
color: @light-green;
}
} }
} }
.menu-right { #main-logo {
right: 2em; margin-top: 20vh;
margin-bottom: 25px;
& > img {
max-width: 350px;
width: 80%;
}
} }
.menu-left {
left: 2em;
}

View File

@ -1,6 +1,6 @@
.result_header { .result_header {
margin-top: 6px; margin-top: 0px;
margin-bottom: 4px; margin-bottom: 2px;
font-size: 16px; font-size: 16px;
.favicon { .favicon {
@ -41,10 +41,11 @@
} }
.external-link, .external-link a{ .external-link {
color: @green; color: @dark-green;
font-size: 12px;
a{ a {
margin-right: 3px; margin-right: 3px;
} }
} }

View File

@ -54,3 +54,26 @@
background-color: @green; background-color: @green;
color: white; color: white;
} }
.custom-select {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
font-size: 1.2rem;
font-weight:normal;
background-color: white;
border: @mild-gray 1px solid;
color: @dark-gray;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAQAAACR313BAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZ
cwAABFkAAARZAVnbJUkAAAAHdElNRQfgBxgLDwB20OFsAAAAbElEQVQY073OsQ3CMAAEwJMYwJGn
sAehpoXJItltBkmcdZBYgIIiQoLglnz3ui+eP+bk5uneteTMZJa6OJuIqvYzSJoqwqBq8gdmTTW8
6/dghxAUq4xsVYT9laBYXCw93Aajh7GPEF23t4fkBYevGFTANkPRAAAAJXRFWHRkYXRlOmNyZWF0
ZQAyMDE2LTA3LTI0VDExOjU1OjU4KzAyOjAwRFqFOQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNi0w
Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
7jwaAAAAAElFTkSuQmCC) 96% no-repeat;
}
.search-margin {
margin-bottom: 0.6em;
}

View File

@ -7,6 +7,7 @@
@blue: #0088CC; @blue: #0088CC;
@red: #F35E77; @red: #F35E77;
@violet: #684898; @violet: #684898;
@dark-green: #069025;
@green: #2ecc71; @green: #2ecc71;
@light-green: #01D7D4; @light-green: #01D7D4;
@orange: #FFA92F; @orange: #FFA92F;

View File

@ -1,28 +1,20 @@
.menu { .searx-navbar {
margin: 0; background: #eee;
padding: 0; color: #aaa;
position: absolute; height: 2.3rem;
top: 4px; font-size: 1.3rem;
border: 0; line-height: 1.3rem;
z-index: 1000000000; padding: 0.5rem;
height: 40px; font-weight: bold;
line-height: 40px; margin-bottom: 1.3rem;
ul {
padding: 0; a, a:hover {
margin: 0; margin-right: 2.0rem;
li { text-decoration: none;
padding: 0 0.6em; }
margin: 0;
float: left; .instance a {
list-style: none; color: #444;
} margin-left: 2.0rem;
} }
} }
.menu-right {
right: 2em;
}
.menu-left {
left: 2em;
}

View File

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

View File

@ -11,6 +11,12 @@
<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|e }}" startPage="1" /> <opensearch:Query role="request" searchTerms="{{ q|e }}" startPage="1" />
{% if error_message %}
<item>
<title>Error</title>
<description>{{ error_message|e }}</description>
</item>
{% endif %}
{% for r in results %} {% for r in results %}
<item> <item>
<title>{{ r.title }}</title> <title>{{ r.title }}</title>

View File

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

View File

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

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>{{ instance_name }}</ShortName>
<Description>a privacy-respecting, hackable metasearch engine</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image>{{ urljoin(host, url_for('static', filename='img/favicon.png')) }}</Image>
<LongName>searx metasearch</LongName>
{% if opensearch_method == 'get' %}
<Url type="text/html" method="get" template="{{ host }}search?q={searchTerms}"/>
{% if autocomplete %}
<Url type="application/x-suggestions+json" method="get" template="{{ host }}autocompleter">
<Param name="format" value="x-suggestions" />
<Param name="q" value="{searchTerms}" />
</Url>
{% endif %}
{% else %}
<Url type="text/html" method="post" template="{{ host }}">
<Param name="q" value="{searchTerms}" />
</Url>
{% if autocomplete %}
<!-- TODO, POST REQUEST doesn't work -->
<Url type="application/x-suggestions+json" method="get" template="{{ host }}autocompleter">
<Param name="format" value="x-suggestions" />
<Param name="q" value="{searchTerms}" />
</Url>
{% endif %}
{% endif %}
</OpenSearchDescription>

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Searx search: {{ q|e }}</title>
<link>{{ base_url }}?q={{ q|e }}</link>
<description>Search results for "{{ q|e }}" - searx</description>
<opensearch:totalResults>{{ number_of_results }}</opensearch:totalResults>
<opensearch:startIndex>1</opensearch:startIndex>
<opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage>
<atom:link rel="search" type="application/opensearchdescription+xml" href="{{ base_url }}opensearch.xml"/>
<opensearch:Query role="request" searchTerms="{{ q|e }}" startPage="1" />
{% for r in results %}
<item>
<title>{{ r.title }}</title>
<link>{{ r.url }}</link>
<description>{{ r.content }}</description>
{% if r.pubdate %}<pubDate>{{ r.pubdate }}</pubDate>{% endif %}
</item>
{% endfor %}
</channel>
</rss>

View File

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

View File

@ -4,7 +4,13 @@
{{ _('Advanced settings') }} {{ _('Advanced settings') }}
</label> </label>
<div id="advanced-search-container"> <div id="advanced-search-container">
{% include 'oscar/categories.html' %} {% include 'oscar/categories.html' %}
{% include 'oscar/time-range.html' %} <div class="row">
{% include 'oscar/languages.html' %} <div class="col-xs-6">
{% include 'oscar/time-range.html' %}
</div>
<div class="col-xs-6">
{% include 'oscar/languages.html' %}
</div>
</div>
</div> </div>

View File

@ -98,5 +98,10 @@
{% for script in scripts %} {% for script in scripts %}
<script src="{{ url_for('static', filename=script) }}"></script> <script src="{{ url_for('static', filename=script) }}"></script>
{% endfor %} {% endfor %}
<noscript>
<style>
.glyphicon { display: none; }
</style>
</noscript>
</body> </body>
</html> </html>

View File

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

View File

@ -10,7 +10,7 @@
{%- endmacro %} {%- endmacro %}
{%- macro result_link(url, title, classes='') -%} {%- macro result_link(url, title, classes='') -%}
<a href="{{ url }}" {% if classes %}class="{{ classes }} "{% endif %}{% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ title }}</a> <a href="{{ url }}" {% if classes %}class="{{ classes }}" {% endif %}{% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ title }}</a>
{%- endmacro -%} {%- endmacro -%}
<!-- Draw result header --> <!-- Draw result header -->
@ -37,7 +37,7 @@
<small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small> <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
{% endif %} {% endif %}
</div> </div>
<div class="text-muted"><small>{{ result.pretty_url }}</small></div> <div class="external-link">{{ result.pretty_url }}</div>
{%- endmacro %} {%- endmacro %}
<!-- Draw result footer --> <!-- Draw result footer -->
@ -50,7 +50,7 @@
{% if proxify %} {% if proxify %}
<small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small> <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
{% endif %} {% endif %}
<div class="text-muted"><small>{{ result.pretty_url }}</small></div> <div class="external-link">{{ result.pretty_url }}</div>
{%- endmacro %} {%- endmacro %}
{% macro preferences_item_header(info, label, rtl) -%} {% macro preferences_item_header(info, label, rtl) -%}

View File

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

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>{{ instance_name }}</ShortName>
<Description>a privacy-respecting, hackable metasearch engine</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image>{{ urljoin(host, url_for('static', filename='img/favicon.png')) }}</Image>
<LongName>searx metasearch</LongName>
{% if opensearch_method == 'get' %}
<Url type="text/html" method="get" template="{{ host }}search?q={searchTerms}"/>
{% if autocomplete %}
<Url type="application/x-suggestions+json" method="get" template="{{ host }}autocompleter">
<Param name="format" value="x-suggestions" />
<Param name="q" value="{searchTerms}" />
</Url>
{% endif %}
{% else %}
<Url type="text/html" method="post" template="{{ host }}">
<Param name="q" value="{searchTerms}" />
</Url>
{% if autocomplete %}
<!-- TODO, POST REQUEST doesn't work -->
<Url type="application/x-suggestions+json" method="get" template="{{ host }}autocompleter">
<Param name="format" value="x-suggestions" />
<Param name="q" value="{searchTerms}" />
</Url>
{% endif %}
{% endif %}
</OpenSearchDescription>

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Searx search: {{ q|e }}</title>
<link>{{ base_url }}?q={{ q|e }}</link>
<description>Search results for "{{ q|e }}" - searx</description>
<opensearch:totalResults>{{ number_of_results }}</opensearch:totalResults>
<opensearch:startIndex>1</opensearch:startIndex>
<opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage>
<atom:link rel="search" type="application/opensearchdescription+xml" href="{{ base_url }}opensearch.xml"/>
<opensearch:Query role="request" searchTerms="{{ q|e }}" startPage="1" />
{% for r in results %}
<item>
<title>{{ r.title }}</title>
<link>{{ r.url }}</link>
<description>{{ r.content }}</description>
{% if r.pubdate %}<pubDate>{{ r.pubdate }}</pubDate>{% endif %}
</item>
{% endfor %}
</channel>
</rss>

View File

@ -11,10 +11,22 @@
{% block title %}{{ q|e }} - {% endblock %} {% block title %}{{ q|e }} - {% endblock %}
{% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ search_url() }}&amp;format=rss">{% endblock %} {% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ search_url() }}&amp;format=rss">{% endblock %}
{% block content %} {% block content %}
{% include 'oscar/search.html' %}
<div class="row"> <div class="row">
<div class="col-sm-8" id="main_results"> <div class="col-sm-8" id="main_results">
<h1 class="sr-only">{{ _('Search results') }}</h1> <h1 class="sr-only">{{ _('Search results') }}</h1>
{% include 'oscar/search.html' %}
{% if corrections %}
<div class="result">
<span class="result_header text-muted form-inline pull-left suggestion_item">{{ _('Try searching for:') }}</span>
{% for correction in corrections %}
<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" role="navigation" class="form-inline pull-left suggestion_item">
<input type="hidden" name="q" value="{{ correction }}">
<button type="submit" class="btn btn-default btn-xs">{{ correction }}</button>
</form>
{% endfor %}
</div>
{% endif %}
{% if answers %} {% if answers %}
{% for answer in answers %} {% for answer in answers %}
@ -80,14 +92,7 @@
<div class="col-sm-4" id="sidebar_results"> <div class="col-sm-4" id="sidebar_results">
{% if number_of_results != '0' %} {% if number_of_results != '0' %}
<div class="panel panel-default"> <p><small>{{ _('Number of results') }}: {{ number_of_results }}</small></p>
<div class="panel-heading">
<h4 class="panel-title">{{ _('Number of results') }}</h4>
</div>
<div class="panel-body">
{{ number_of_results }}
</div>
</div>
{% endif %} {% endif %}
{% if infoboxes %} {% if infoboxes %}
{% for infobox in infoboxes %} {% for infobox in infoboxes %}

View File

@ -1,12 +1,24 @@
{% from 'oscar/macros.html' import icon %} {% from 'oscar/macros.html' import icon %}
<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form" role="search"> <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form" role="search">
<div class="input-group col-sm-12"> <div class="row">
<div class="col-xs-12 col-md-8">
<div class="input-group search-margin">
<input type="search" name="q" class="form-control" id="q" placeholder="{{ _('Search for...') }}" autocomplete="off" value="{{ q }}"> <input type="search" name="q" class="form-control" id="q" placeholder="{{ _('Search for...') }}" autocomplete="off" value="{{ q }}">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="submit" class="btn btn-default"><span class="hide_if_nojs">{{ icon('search') }}</span><span class="hidden active_if_nojs">{{ _('Start search') }}</span></button> <button type="submit" class="btn btn-default"><span class="hide_if_nojs">{{ icon('search') }}</span><span class="hidden active_if_nojs">{{ _('Start search') }}</span></button>
</span> </span>
</div>
</div> </div>
<div class="input-group col-sm-12 advanced"> <div class="col-xs-6 col-md-2 search-margin">
{% include 'oscar/advanced.html' %} {% include 'oscar/time-range.html' %}
</div> </div>
<div class="col-xs-6 col-md-2 search-margin">
{% include 'oscar/languages.html' %}
</div>
</div>
<div class="row">
<div class="col-sm-12">
{% include 'oscar/categories.html' %}
</div>
</div>
</form><!-- / #search_form_full --> </form><!-- / #search_form_full -->

View File

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

View File

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

View File

@ -175,6 +175,8 @@ def get_themes(root):
templates_path = os.path.join(root, 'templates') templates_path = os.path.join(root, 'templates')
themes = os.listdir(os.path.join(static_path, 'themes')) themes = os.listdir(os.path.join(static_path, 'themes'))
if '__common__' in themes:
themes.remove('__common__')
return static_path, templates_path, themes return static_path, templates_path, themes

View File

@ -52,6 +52,7 @@ from flask import (
from flask_babel import Babel, gettext, format_date, format_decimal from flask_babel import Babel, gettext, format_date, format_decimal
from flask.json import jsonify from flask.json import jsonify
from searx import settings, searx_dir, searx_debug from searx import settings, searx_dir, searx_debug
from searx.exceptions import SearxException, SearxParameterException
from searx.engines import ( from searx.engines import (
categories, engines, engine_shortcuts, get_engines_stats, initialize_engines categories, engines, engine_shortcuts, get_engines_stats, initialize_engines
) )
@ -226,7 +227,7 @@ def get_current_theme_name(override=None):
2. cookies 2. cookies
3. settings""" 3. settings"""
if override and override in themes: if override and (override in themes or override == '__common__'):
return override return override
theme_name = request.args.get('theme', request.preferences.get_value('theme')) theme_name = request.args.get('theme', request.preferences.get_value('theme'))
if theme_name not in themes: if theme_name not in themes:
@ -400,6 +401,33 @@ def pre_request():
request.user_plugins.append(plugin) request.user_plugins.append(plugin)
def index_error(output_format, error_message):
if output_format == 'json':
return Response(json.dumps({'error': error_message}),
mimetype='application/json')
elif output_format == 'csv':
response = Response('', mimetype='application/csv')
cont_disp = 'attachment;Filename=searx.csv'
response.headers.add('Content-Disposition', cont_disp)
return response
elif output_format == 'rss':
response_rss = render(
'opensearch_response_rss.xml',
results=[],
q=request.form['q'] if 'q' in request.form else '',
number_of_results=0,
base_url=get_base_url(),
error_message=error_message
)
return Response(response_rss, mimetype='text/xml')
else:
# html
request.errors.append(gettext('search error'))
return render(
'index.html',
)
@app.route('/search', methods=['GET', 'POST']) @app.route('/search', methods=['GET', 'POST'])
@app.route('/', methods=['GET', 'POST']) @app.route('/', methods=['GET', 'POST'])
def index(): def index():
@ -408,10 +436,19 @@ def index():
Supported outputs: html, json, csv, rss. Supported outputs: html, json, csv, rss.
""" """
# output_format
output_format = request.form.get('format', 'html')
if output_format not in ['html', 'csv', 'json', 'rss']:
output_format = 'html'
# check if there is query
if request.form.get('q') is None: if request.form.get('q') is None:
return render( if output_format == 'html':
'index.html', return render(
) 'index.html',
)
else:
return index_error(output_format, 'No query'), 400
# search # search
search_query = None search_query = None
@ -421,20 +458,24 @@ def index():
# search = Search(search_query) # without plugins # search = Search(search_query) # without plugins
search = SearchWithPlugins(search_query, request.user_plugins, request) search = SearchWithPlugins(search_query, request.user_plugins, request)
result_container = search.search() result_container = search.search()
except: except Exception as e:
request.errors.append(gettext('search error')) # log exception
logger.exception('search error') logger.exception('search error')
return render(
'index.html',
)
# is it an invalid input parameter or something else ?
if (issubclass(e.__class__, SearxParameterException)):
return index_error(output_format, e.message), 400
else:
return index_error(output_format, gettext('search error')), 500
# results
results = result_container.get_ordered_results() results = result_container.get_ordered_results()
number_of_results = result_container.results_number()
if number_of_results < result_container.results_length():
number_of_results = 0
# UI # UI
advanced_search = request.form.get('advanced_search', None) advanced_search = request.form.get('advanced_search', None)
output_format = request.form.get('format', 'html')
if output_format not in ['html', 'csv', 'json', 'rss']:
output_format = 'html'
# output # output
for result in results: for result in results:
@ -470,15 +511,12 @@ def index():
else: else:
result['publishedDate'] = format_date(result['publishedDate']) result['publishedDate'] = format_date(result['publishedDate'])
number_of_results = result_container.results_number()
if number_of_results < result_container.results_length():
number_of_results = 0
if output_format == 'json': if output_format == 'json':
return Response(json.dumps({'query': search_query.query, return Response(json.dumps({'query': search_query.query,
'number_of_results': number_of_results, 'number_of_results': number_of_results,
'results': results, 'results': results,
'answers': list(result_container.answers), 'answers': list(result_container.answers),
'corrections': list(result_container.corrections),
'infoboxes': result_container.infoboxes, 'infoboxes': result_container.infoboxes,
'suggestions': list(result_container.suggestions)}), 'suggestions': list(result_container.suggestions)}),
mimetype='application/json') mimetype='application/json')
@ -500,7 +538,8 @@ def index():
results=results, results=results,
q=request.form['q'], q=request.form['q'],
number_of_results=number_of_results, number_of_results=number_of_results,
base_url=get_base_url() base_url=get_base_url(),
override_theme='__common__',
) )
return Response(response_rss, mimetype='text/xml') return Response(response_rss, mimetype='text/xml')
@ -515,6 +554,7 @@ def index():
advanced_search=advanced_search, advanced_search=advanced_search,
suggestions=result_container.suggestions, suggestions=result_container.suggestions,
answers=result_container.answers, answers=result_container.answers,
corrections=result_container.corrections,
infoboxes=result_container.infoboxes, infoboxes=result_container.infoboxes,
paging=result_container.paging, paging=result_container.paging,
current_language=search_query.lang, current_language=search_query.lang,
@ -720,7 +760,8 @@ def opensearch():
ret = render('opensearch.xml', ret = render('opensearch.xml',
opensearch_method=method, opensearch_method=method,
host=get_base_url(), host=get_base_url(),
urljoin=urljoin) urljoin=urljoin,
override_theme='__common__')
resp = Response(response=ret, resp = Response(response=ret,
status=200, status=200,

View File

@ -36,6 +36,7 @@ class ViewsTestCase(SearxTestCase):
def search_mock(search_self, *args): def search_mock(search_self, *args):
search_self.result_container = Mock(get_ordered_results=lambda: self.test_results, search_self.result_container = Mock(get_ordered_results=lambda: self.test_results,
answers=set(), answers=set(),
corrections=set(),
suggestions=set(), suggestions=set(),
infoboxes=[], infoboxes=[],
results=self.test_results, results=self.test_results,
@ -45,6 +46,8 @@ class ViewsTestCase(SearxTestCase):
Search.search = search_mock Search.search = search_mock
def get_current_theme_name_mock(override=None): def get_current_theme_name_mock(override=None):
if override:
return override
return 'legacy' return 'legacy'
webapp.get_current_theme_name = get_current_theme_name_mock webapp.get_current_theme_name = get_current_theme_name_mock