Browse Source

Merge branch 'master' of https://github.com/asciimoo/searx into csp-oscar-theme

Markus Heiser 4 years ago
parent
commit
f14a7add31
38 changed files with 829 additions and 288 deletions
  1. 2 1
      Makefile
  2. 53 43
      README.rst
  3. 6 5
      docs/admin/installation-uwsgi.rst
  4. 1 0
      docs/blog/index.rst
  5. 413 0
      docs/blog/lxcdev-202006.rst
  6. 1 1
      docs/blog/private-engines.rst
  7. 2 2
      docs/dev/search_api.rst
  8. 2 2
      docs/user/own-instance.rst
  9. 4 3
      docs/utils/lxc.sh.rst
  10. 6 0
      searx/engines/duckduckgo.py
  11. 82 85
      searx/engines/gigablast.py
  12. 1 1
      searx/engines/yacy.py
  13. 1 1
      searx/engines/yahoo.py
  14. 32 4
      searx/static/themes/oscar/css/logicodev-dark.css
  15. 0 0
      searx/static/themes/oscar/css/logicodev-dark.min.css
  16. 32 4
      searx/static/themes/oscar/css/logicodev.css
  17. 0 0
      searx/static/themes/oscar/css/logicodev.min.css
  18. 2 2
      searx/static/themes/oscar/less/logicodev/advanced.less
  19. 19 2
      searx/static/themes/oscar/less/logicodev/onoff.less
  20. 11 1
      searx/static/themes/oscar/less/logicodev/search.less
  21. 9 5
      searx/templates/__common__/about.html
  22. 1 1
      searx/templates/oscar/advanced.html
  23. 3 3
      searx/templates/oscar/base.html
  24. 2 2
      searx/templates/oscar/index.html
  25. 1 1
      searx/templates/oscar/infobox.html
  26. 1 0
      searx/templates/oscar/languages.html
  27. 17 16
      searx/templates/oscar/macros.html
  28. 47 47
      searx/templates/oscar/preferences.html
  29. 2 2
      searx/templates/oscar/result_templates/code.html
  30. 6 6
      searx/templates/oscar/result_templates/default.html
  31. 2 2
      searx/templates/oscar/result_templates/images.html
  32. 4 4
      searx/templates/oscar/result_templates/map.html
  33. 4 4
      searx/templates/oscar/result_templates/torrent.html
  34. 6 6
      searx/templates/oscar/result_templates/videos.html
  35. 1 0
      searx/templates/oscar/time-range.html
  36. 9 2
      searx/webapp.py
  37. 2 2
      utils/fetch_languages.py
  38. 42 28
      utils/searx.sh

+ 2 - 1
Makefile

@@ -223,6 +223,7 @@ test.pylint: pyenvinstall
 	$(call cmd,pylint,\
 	$(call cmd,pylint,\
 		searx/preferences.py \
 		searx/preferences.py \
 		searx/testing.py \
 		searx/testing.py \
+		searx/engines/gigablast.py \
 	)
 	)
 endif
 endif
 
 
@@ -243,7 +244,7 @@ test.sh:
 
 
 test.pep8: pyenvinstall
 test.pep8: pyenvinstall
 	@echo "TEST      pep8"
 	@echo "TEST      pep8"
-	$(Q)$(PY_ENV_ACT); pep8 --exclude=searx/static --max-line-length=120 --ignore "E402,W503" searx tests
+	$(Q)$(PY_ENV_ACT); pep8 --exclude='searx/static, searx/engines/gigablast.py' --max-line-length=120 --ignore "E402,W503" searx tests
 
 
 test.unit: pyenvinstall
 test.unit: pyenvinstall
 	@echo "TEST      tests/unit"
 	@echo "TEST      tests/unit"

+ 53 - 43
README.rst

@@ -1,63 +1,73 @@
-searx
+.. SPDX-License-Identifier: AGPL-3.0-or-later
-=====
+
+.. figure:: https://raw.githubusercontent.com/asciimoo/searx/master/searx/static/themes/oscar/img/logo_searx_a.png
+   :target: https://asciimoo.github.io/searx/
+   :alt: searX
+   :width: 100%
+   :align: center
+
+-------
+
+|searx install|
+|searx homepage|
+|searx wiki|
+|AGPL License|
+|Issues|
+|commits|
+|OpenCollective searx backers|
+|OpenCollective searx sponsors|
 
 
-A privacy-respecting, hackable `metasearch
+Privacy-respecting, hackable `metasearch engine`_ / *pronunciation* **səːks**.
-engine <https://en.wikipedia.org/wiki/Metasearch_engine>`__.
 
 
-Pronunciation: səːks
+.. _metasearch engine: https://en.wikipedia.org/wiki/Metasearch_engine
 
 
-List of `running
+.. |searx install| image:: https://img.shields.io/badge/-install-blue
-instances <https://searx.space/>`__.
+   :target: https://asciimoo.github.io/searx/admin/installation.html
 
 
-See the `documentation <https://asciimoo.github.io/searx>`__ and the `wiki <https://github.com/asciimoo/searx/wiki>`__ for more information.
+.. |searx homepage| image:: https://img.shields.io/badge/-homepage-blue
+   :target: https://asciimoo.github.io/searx
 
 
-|OpenCollective searx backers|
+.. |searx wiki| image:: https://img.shields.io/badge/-wiki-blue
-|OpenCollective searx sponsors|
+   :target: https://github.com/asciimoo/searx/wiki
 
 
-Installation
+.. |AGPL License|  image:: https://img.shields.io/badge/license-AGPL-blue.svg
-~~~~~~~~~~~~
+   :target: https://github.com/asciimoo/searx/blob/master/LICENSE
 
 
-With Docker
+.. |Issues| image:: https://img.shields.io/github/issues/asciimoo/searx?color=yellow&label=issues
------------
+   :target: https://github.com/asciimoo/searx/issues
-Go to the `searx-docker <https://github.com/searx/searx-docker>`__ project.
 
 
-Without Docker
+.. |PR| image:: https://img.shields.io/github/issues-pr-raw/asciimoo/searx?color=yellow&label=PR
---------------
+   :target: https://github.com/asciimoo/searx/pulls
-For all of the details, follow this `step by step installation <https://asciimoo.github.io/searx/admin/installation.html>`__.
 
 
-Note: the documentation needs to be updated.
+.. |commits| image:: https://img.shields.io/github/commit-activity/y/asciimoo/searx?color=yellow&label=commits
+   :target: https://github.com/asciimoo/searx/commits/master
 
 
-If you are in a hurry
+.. |OpenCollective searx backers| image:: https://opencollective.com/searx/backers/badge.svg
----------------------
+   :target: https://opencollective.com/searx#backer
--  clone the source:
-   ``git clone https://github.com/asciimoo/searx.git && cd searx``
--  install dependencies: ``./manage.sh update_packages``
--  edit your
-   `settings.yml <https://github.com/asciimoo/searx/blob/master/searx/settings.yml>`__
-   (set your ``secret_key``!)
--  run ``python searx/webapp.py`` to start the application
 
 
+.. |OpenCollective searx sponsors| image:: https://opencollective.com/searx/sponsors/badge.svg
+   :target: https://opencollective.com/searx#sponsor
 
 
-Bugs
-~~~~
 
 
-Bugs or suggestions? Visit the `issue
+If you are looking for running instances, ready to use, then visit searx.space_.
-tracker <https://github.com/asciimoo/searx/issues>`__.
 
 
-`License <https://github.com/asciimoo/searx/blob/master/LICENSE>`__
+Otherwise jump to the user_, admin_ and developer_ handbooks you will find on
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+our homepage_.
 
 
-More about searx
+.. _searx.space: https://searx.space
-~~~~~~~~~~~~~~~~
+.. _user: https://asciimoo.github.io/searx/user
+.. _admin: https://asciimoo.github.io/searx/admin
+.. _developer: https://asciimoo.github.io/searx/dev
+.. _homepage: https://asciimoo.github.io/searx
 
 
--  `openhub <https://www.openhub.net/p/searx/>`__
+contact:
--  `twitter <https://twitter.com/Searx_engine>`__
+  openhub_ // twitter_ // IRC: #searx @ freenode
--  IRC: #searx @ freenode
 
 
+.. _openhub: https://www.openhub.net/p/searx
+.. _twitter: https://twitter.com/Searx_engine
 
 
-.. |OpenCollective searx backers| image:: https://opencollective.com/searx/backers/badge.svg
+-------
-   :target: https://opencollective.com/searx#backer
 
 
+|gluten free|
 
 
-.. |OpenCollective searx sponsors| image:: https://opencollective.com/searx/sponsors/badge.svg
+.. |gluten free| image:: https://forthebadge.com/images/featured/featured-gluten-free.svg
-   :target: https://opencollective.com/searx#sponsor

+ 6 - 5
docs/admin/installation-uwsgi.rst

@@ -108,6 +108,7 @@ restart the uwsgi application.
          :start-after: START searx uwsgi-description ubuntu-20.04
          :start-after: START searx uwsgi-description ubuntu-20.04
          :end-before: END searx uwsgi-description ubuntu-20.04
          :end-before: END searx uwsgi-description ubuntu-20.04
 
 
+   .. hotfix: a bug group-tab need this comment
 
 
    .. group-tab:: Arch Linux
    .. group-tab:: Arch Linux
 
 
@@ -115,6 +116,7 @@ restart the uwsgi application.
          :start-after: START searx uwsgi-description arch
          :start-after: START searx uwsgi-description arch
          :end-before: END searx uwsgi-description arch
          :end-before: END searx uwsgi-description arch
 
 
+   .. hotfix: a bug group-tab need this comment
 
 
    .. group-tab::  Fedora / RHEL
    .. group-tab::  Fedora / RHEL
 
 
@@ -128,22 +130,21 @@ restart the uwsgi application.
    .. group-tab:: Ubuntu / debian
    .. group-tab:: Ubuntu / debian
 
 
       .. kernel-include:: $DOCS_BUILD/includes/searx.rst
       .. kernel-include:: $DOCS_BUILD/includes/searx.rst
-         :code: ini
          :start-after: START searx uwsgi-appini ubuntu-20.04
          :start-after: START searx uwsgi-appini ubuntu-20.04
          :end-before: END searx uwsgi-appini ubuntu-20.04
          :end-before: END searx uwsgi-appini ubuntu-20.04
 
 
+   .. hotfix: a bug group-tab need this comment
+
    .. group-tab:: Arch Linux
    .. group-tab:: Arch Linux
 
 
       .. kernel-include:: $DOCS_BUILD/includes/searx.rst
       .. kernel-include:: $DOCS_BUILD/includes/searx.rst
-         :code: ini
          :start-after: START searx uwsgi-appini arch
          :start-after: START searx uwsgi-appini arch
          :end-before: END searx uwsgi-appini arch
          :end-before: END searx uwsgi-appini arch
 
 
+   .. hotfix: a bug group-tab need this comment
+
    .. group-tab::  Fedora / RHEL
    .. group-tab::  Fedora / RHEL
 
 
       .. kernel-include:: $DOCS_BUILD/includes/searx.rst
       .. kernel-include:: $DOCS_BUILD/includes/searx.rst
-         :code: ini
          :start-after: START searx uwsgi-appini fedora
          :start-after: START searx uwsgi-appini fedora
          :end-before: END searx uwsgi-appini fedora
          :end-before: END searx uwsgi-appini fedora
-
-

+ 1 - 0
docs/blog/index.rst

@@ -6,6 +6,7 @@ Blog
    :maxdepth: 2
    :maxdepth: 2
    :caption: Contents
    :caption: Contents
 
 
+   lxcdev-202006
    python3
    python3
    admin
    admin
    intro-offline
    intro-offline

+ 413 - 0
docs/blog/lxcdev-202006.rst

@@ -0,0 +1,413 @@
+.. _blog-lxcdev-202006:
+
+=======================================
+Developing in Linux containers [202006]
+=======================================
+
+.. _LXC: https://linuxcontainers.org/lxc/introduction/
+
+.. sidebar:: Audience
+
+   This blog post is written for experienced admins and developers / readers
+   should have a serious meaning about: *distributed*, *merge* and *linux
+   container*.
+
+.. contents:: Contents
+   :depth: 2
+   :local:
+   :backlinks: entry
+
+In PR :PR:`1803` we added a lot of scripts to Searx's boilerplate.  In this blog
+post I will show you, how you can make use of them in *distributed and
+heterogeneous development cycles* (TL;DR; jump to the :ref:`blog-lxcdev-202006
+abstract`).
+
+Motivation
+==========
+
+Normally in our development cycle, we edit the sources and run some test and/or
+builds by using ``make`` before we commit.  This cycle is simple and perfect but
+might fail in some aspects we should not overlook.
+
+    The environment in which we run all our development processes matters!
+
+The :ref:`makefile` and the :ref:`make pyenv` encapsulate a lot for us, but they
+do not have access to all prerequisites.  For example, there may have
+dependencies on packages that are installed on the developer's desktop, but
+usually are not preinstalled on a server or client system.  Another examples
+are; settings have been made to the software on the developer's host that would
+never be set on a *production* system.
+
+*Linux Containers* (LXC_) are isolate environments and not to mix up on
+developer's all the prerequisites of all the projects he contribute to, is
+always a good choice.
+
+The scripts from PR :PR:`1803` can divide in those to install and maintain
+software:
+
+- :ref:`searx.sh`
+- :ref:`filtron.sh`
+- :ref:`morty.sh`
+
+and the script :ref:`lxc.sh`, with we can scale our installation, maintenance or
+even development tasks over a stack of containers, what we call: *Searx's lxc
+suite*.
+
+Gentlemen, start your engines!
+==============================
+
+.. _LXD: https://linuxcontainers.org/lxd/introduction/
+.. _archlinux: https://www.archlinux.org/
+
+Before you can start with containers, you need to install and initiate LXD_
+once:
+
+.. tabs::
+
+  .. group-tab:: desktop
+
+     .. code:: sh
+
+        $ snap install lxd
+        $ lxd init --auto
+
+And you need to clone from origin or if you have your own fork, clone from your
+fork:
+
+.. tabs::
+
+  .. group-tab:: desktop
+
+     .. code:: sh
+
+        $ cd ~/Downloads
+        $ git clone https://github.com/asciimoo/searx.git
+        $ cd searx
+
+The :ref:`lxc-searx.env` consists of several images, see ``export
+LXC_SUITE=(...`` near by :origin:`utils/lxc-searx.env#L19`.  For this blog post
+we exercise on a archlinux_ image.  The container of this image is named
+``searx-archlinux``.  Lets build the container, but be sure that this container
+does not already exists, so first lets remove possible old one:
+
+.. tabs::
+
+  .. group-tab:: desktop
+
+     .. code:: sh
+
+        $ sudo -H ./utils/lxc.sh remove searx-archlinux
+        $ sudo -H ./utils/lxc.sh build searx-archlinux
+
+.. sidebar:: The ``searx-archlinux`` container
+
+   is the base of all our exercises here.
+
+In this container we install all services :ref:`including searx, morty & filtron
+<lxc.sh install suite>` in once:
+
+.. tabs::
+
+  .. group-tab:: desktop
+
+     .. code:: sh
+
+        $ sudo -H ./utils/lxc.sh install suite searx-archlinux
+
+To proxy HTTP from filtron and morty in the container to the outside of the
+container, install nginx into the container.  Once for the bot blocker filtron:
+
+.. tabs::
+
+  .. group-tab:: desktop
+
+     .. code:: sh
+
+        $ sudo -H ./utils/lxc.sh cmd searx-archlinux \
+          ./utils/filtron.sh nginx install
+        ...
+        INFO:  got 429 from http://10.174.184.156/searx
+
+and once for the content sanitizer (content proxy morty):
+
+.. tabs::
+
+  .. group-tab:: desktop
+
+     .. code:: sh
+
+        $ sudo -H ./utils/lxc.sh cmd searx-archlinux \
+          ./utils/morty.sh nginx install
+        ...
+        INFO:  got 200 from http://10.174.184.156/morty/
+
+.. sidebar:: Fully functional searx suite
+
+   From here on you have a fully functional searx suite running with bot blocker
+   (filtron) and Web content sanitizer (content proxy morty) needed for a
+   *privacy protecting* search engine.
+
+On your system, the IP of your ``searx-archlinux`` container differs from
+http://10.174.184.156/searx, just open the URL reported in your installation
+protocol in your WEB browser from the desktop to test the instance from outside
+of the container.
+
+In such a searx suite admins can maintain and access the debug log of the
+different services quite easy.
+
+.. _working in containers:
+
+In containers, work as usual
+============================
+
+Usually you open a root-bash using ``sudo -H bash``.  In case of LXC containers
+open the root-bash in the container using ``./utils/lxc.sh cmd
+searx-archlinux``:
+
+.. tabs::
+
+  .. group-tab:: desktop
+
+     .. code:: sh
+
+        $ sudo -H ./utils/lxc.sh cmd searx-archlinux bash
+        INFO:  [searx-archlinux] bash
+        [root@searx-archlinux searx]# pwd
+        /share/searx
+
+The prompt ``[root@searx-archlinux ...]`` signals, that you are the root user in
+the searx-container.  To debug the running searx instance use:
+
+.. tabs::
+
+  .. group-tab:: root@searx-archlinux
+
+     .. code:: sh
+
+        $ ./utils/searx.sh inspect service
+        ...
+        use [CTRL-C] to stop monitoring the log
+        ...
+
+Back in the browser on your desktop open the service http://10.174.184.156/searx
+and run your application tests while the debug log is shown in the terminal from
+above.  You can stop monitoring using ``CTRL-C``, this also disables the *"debug
+option"* in searx's settings file and restarts the searx uwsgi application.  To
+debug services from filtron and morty analogous use:
+
+.. tabs::
+
+  .. group-tab:: root@searx-archlinux
+
+     .. code:: sh
+
+        $ ./utils/filtron.sh inspect service
+        $ ./utils/morty.sh inspect service
+
+Another point we have to notice is that each service (:ref:`searx <searx.sh>`,
+:ref:`filtron <filtron.sh>` and :ref:`morty <morty.sh>`) runs under dedicated
+system user account with the same name (compare :ref:`create searx user`).  To
+get a shell from theses accounts, simply call one of the scripts:
+
+.. tabs::
+
+  .. group-tab:: root@searx-archlinux
+
+     .. code:: sh
+
+        $ ./utils/searx.sh shell
+        $ ./utils/filtron.sh shell
+        $ ./utils/morty.sh shell
+
+To get in touch, open a shell from the service user (searx@searx-archlinux):
+
+.. tabs::
+
+  .. group-tab:: desktop
+
+     .. code:: sh
+
+        $ sudo -H ./utils/lxc.sh cmd searx-archlinux \
+        ./utils/searx.sh shell
+        // exit with [CTRL-D]
+        (searx-pyenv) [searx@searx-archlinux ~]$ ...
+
+The prompt ``[searx@searx-archlinux]`` signals that you are logged in as system
+user ``searx`` in the ``searx-archlinux`` container and the python *virtualenv*
+``(searx-pyenv)`` environment is activated.
+
+.. tabs::
+
+  .. group-tab:: searx@searx-archlinux
+
+     .. code:: sh
+
+        (searx-pyenv) [searx@searx-archlinux ~]$ pwd
+        /usr/local/searx
+
+
+
+Wrap production into developer suite
+====================================
+
+In this section we will see how to change the *"Fully functional searx suite"*
+from a LXC container (which is quite ready for production) into a developer
+suite.  For this, we have to keep an eye on the :ref:`installation basic`:
+
+- searx setup in: ``/etc/searx/settings.yml``
+- searx user's home: ``/usr/local/searx``
+- virtualenv in: ``/usr/local/searx/searx-pyenv``
+- searx software in: ``/usr/local/searx/searx-src``
+
+The searx software is a clone of the ``GIT_URL`` (see :ref:`makefile setup`) and
+the working tree is checked out from the ``GIT_BRANCH``.  With the use of the
+:ref:`searx.sh` the searx service was installed as :ref:`uWSGI application
+<searx uwsgi>`.  To maintain this service, we can use ``systemctl`` (compare
+:ref:`service architectures on distributions <uwsgi configuration>`).
+
+.. tabs::
+
+  .. group-tab:: desktop
+
+     .. code:: sh
+
+        $ sudo -H ./utils/lxc.sh cmd searx-archlinux \
+          systemctl stop uwsgi@searx
+
+With the command above, we stopped the searx uWSGI-App in the archlinux
+container.
+
+The uWSGI-App for the archlinux dsitros is configured in
+:origin:`utils/templates/etc/uwsgi/apps-archlinux/searx.ini`, from where at
+least you should attend the settings of ``uid``, ``chdir``, ``env`` and
+``http``::
+
+  env = SEARX_SETTINGS_PATH=/etc/searx/settings.yml
+  http = 127.0.0.1:8888
+
+  chdir = /usr/local/searx/searx-src/searx
+  virtualenv = /usr/local/searx/searx-pyenv
+  pythonpath = /usr/local/searx/searx-src
+
+If you have read the :ref:`"Good to know section" <lxc.sh>` you remember, that
+each container shares the root folder of the repository and the command
+``utils/lxc.sh cmd`` handles relative path names **transparent**.  To wrap the
+searx installation into a developer one, we simple have to create a smylink to
+the **transparent** reposetory from the desktop.  Now lets replace the
+repository at ``searx-src`` in the container with the working tree from outside
+of the container:
+
+.. tabs::
+
+  .. group-tab:: container becomes a developer suite
+
+     .. code:: sh
+
+        $ sudo -H ./utils/lxc.sh cmd searx-archlinux \
+          mv /usr/local/searx/searx-src /usr/local/searx/searx-src.old
+
+        $ sudo -H ./utils/lxc.sh cmd searx-archlinux \
+          ln -s /share/searx/ /usr/local/searx/searx-src
+
+Now we can develop as usual in the working tree of our desktop system.  Every
+time the software was changed, you have to restart the searx service (in the
+conatiner):
+
+.. tabs::
+
+  .. group-tab:: desktop
+
+     .. code:: sh
+
+        $ sudo -H ./utils/lxc.sh cmd searx-archlinux \
+          systemctl restart uwsgi@searx
+
+
+Remember: :ref:`working in containers` .. here are just some examples from my
+daily usage:
+
+.. tabs::
+
+  .. group-tab:: desktop
+
+     To *inspect* the searx instance (already described above):
+
+     .. code:: sh
+
+        $ sudo -H ./utils/lxc.sh cmd searx-archlinux \
+          ./utils/searx.sh inspect service
+
+     Run :ref:`makefile`, e.g. to test inside the container:
+
+     .. code:: sh
+
+        $ sudo -H ./utils/lxc.sh cmd searx-archlinux \
+          make test
+
+     To install all prerequisites needed for a :ref:`buildhosts`:
+
+     .. code:: sh
+
+        $ sudo -H ./utils/lxc.sh cmd searx-archlinux \
+          ./utils/searx.sh install buildhost
+
+     To build the docs on a buildhost :ref:`buildhosts`:
+
+     .. code:: sh
+
+        $ sudo -H ./utils/lxc.sh cmd searx-archlinux \
+          make docs
+
+.. _blog-lxcdev-202006 abstract:
+
+Abstract
+========
+
+We build up a fully functional searx suite in a archlinux container:
+
+.. code:: sh
+
+   $ sudo -H ./utils/lxc.sh install suite searx-archlinux
+
+To access HTTP from the desktop we installed nginx for the services inside the
+conatiner:
+
+.. tabs::
+
+  .. group-tab:: [root@searx-archlinux]
+
+     .. code:: sh
+
+        $ ./utils/filtron.sh nginx install
+        $ ./utils/morty.sh nginx install
+
+To wrap the suite into a developer one, we created a symbolic link to the
+repository which is shared **transparent** from the desktop's file system into
+the container :
+
+.. tabs::
+
+  .. group-tab:: [root@searx-archlinux]
+
+     .. code:: sh
+
+	$ mv /usr/local/searx/searx-src /usr/local/searx/searx-src.old
+	$ ln -s /share/searx/ /usr/local/searx/searx-src
+	$ systemctl restart uwsgi@searx
+
+To get remarks from the suite of the archlinux container we can use:
+
+.. tabs::
+
+  .. group-tab:: desktop
+
+     .. code:: sh
+
+        $ sudo -H ./utils/lxc.sh show suite searx-archlinux
+        ...
+        [searx-archlinux]  INFO:  (eth0) filtron:    http://10.174.184.156:4004/ http://10.174.184.156/searx
+        [searx-archlinux]  INFO:  (eth0) morty:      http://10.174.184.156:3000/
+        [searx-archlinux]  INFO:  (eth0) docs-live:  http://10.174.184.156:8080/
+        [searx-archlinux]  INFO:  (eth0) IPv6:       http://[fd42:573b:e0b3:e97e:216:3eff:fea5:9b65]
+        ...
+

+ 1 - 1
docs/blog/private-engines.rst

@@ -13,7 +13,7 @@ Private engines
 To solve this issue private engines were introduced in :pull:`1823`.
 To solve this issue private engines were introduced in :pull:`1823`.
 A new option was added to engines named `tokens`. It expects a list
 A new option was added to engines named `tokens`. It expects a list
 of strings. If the user making a request presents one of the tokens
 of strings. If the user making a request presents one of the tokens
-of an engine, he/she is able to access information about the engine
+of an engine, they can access information about the engine
 and make search requests.
 and make search requests.
 
 
 Example configuration to restrict access to the Arch Linux Wiki engine:
 Example configuration to restrict access to the Arch Linux Wiki engine:

+ 2 - 2
docs/dev/search_api.rst

@@ -81,7 +81,7 @@ Parameters
   Theme of instance.
   Theme of instance.
 
 
   Please note, available themes depend on an instance.  It is possible that an
   Please note, available themes depend on an instance.  It is possible that an
-  instance administrator deleted, created or renamed themes on his/her instance.
+  instance administrator deleted, created or renamed themes on their instance.
   See the available options in the preferences page of the instance.
   See the available options in the preferences page of the instance.
 
 
 ``oscar-style`` : default ``logicodev``
 ``oscar-style`` : default ``logicodev``
@@ -91,7 +91,7 @@ Parameters
   ``oscar``.
   ``oscar``.
 
 
   Please note, available styles depend on an instance.  It is possible that an
   Please note, available styles depend on an instance.  It is possible that an
-  instance administrator deleted, created or renamed styles on his/her
+  instance administrator deleted, created or renamed styles on their
   instance. See the available options in the preferences page of the instance.
   instance. See the available options in the preferences page of the instance.
 
 
 ``enabled_plugins`` : optional
 ``enabled_plugins`` : optional

+ 2 - 2
docs/user/own-instance.rst

@@ -44,9 +44,9 @@ hidden from visited result pages.
 What are the consequences of using public instances?
 What are the consequences of using public instances?
 ----------------------------------------------------
 ----------------------------------------------------
 
 
-If someone uses a public instance, he/she has to trust the administrator of that
+If someone uses a public instance, they have to trust the administrator of that
 instance.  This means that the user of the public instance does not know whether
 instance.  This means that the user of the public instance does not know whether
-his/her requests are logged, aggregated and sent or sold to a third party.
+their requests are logged, aggregated and sent or sold to a third party.
 
 
 Also, public instances without proper protection are more vulnerable to abusing
 Also, public instances without proper protection are more vulnerable to abusing
 the search service, In this case the external service in exchange returns
 the search service, In this case the external service in exchange returns

+ 4 - 3
docs/utils/lxc.sh.rst

@@ -47,9 +47,9 @@ one**::
 
 
 *Good to know ...*
 *Good to know ...*
 
 
-Eeach container shares the root folder of the repository and the
+Each container shares the root folder of the repository and the command
-command ``utils/lxc.sh cmd`` **handles relative path names transparent**,
+``utils/lxc.sh cmd`` **handles relative path names transparent**, compare output
-compare output of::
+of::
 
 
   $ sudo -H ./utils/lxc.sh cmd -- ls -la Makefile
   $ sudo -H ./utils/lxc.sh cmd -- ls -la Makefile
   ...
   ...
@@ -66,6 +66,7 @@ If there comes the time you want to **get rid off all** the containers and
   $ sudo -H ./utils/lxc.sh remove
   $ sudo -H ./utils/lxc.sh remove
   $ sudo -H ./utils/lxc.sh remove images
   $ sudo -H ./utils/lxc.sh remove images
 
 
+.. _lxc.sh install suite:
 
 
 Install suite
 Install suite
 =============
 =============

+ 6 - 0
searx/engines/duckduckgo.py

@@ -50,6 +50,7 @@ result_xpath = '//div[@class="result results_links results_links_deep web-result
 url_xpath = './/a[@class="result__a"]/@href'
 url_xpath = './/a[@class="result__a"]/@href'
 title_xpath = './/a[@class="result__a"]'
 title_xpath = './/a[@class="result__a"]'
 content_xpath = './/a[@class="result__snippet"]'
 content_xpath = './/a[@class="result__snippet"]'
+correction_xpath = '//div[@id="did_you_mean"]//a'
 
 
 
 
 # match query's language to a region code that duckduckgo will accept
 # match query's language to a region code that duckduckgo will accept
@@ -125,6 +126,11 @@ def response(resp):
                         'content': content,
                         'content': content,
                         'url': res_url})
                         'url': res_url})
 
 
+    # parse correction
+    for correction in eval_xpath(doc, correction_xpath):
+        # append correction
+        results.append({'correction': extract_text(correction)})
+
     # return results
     # return results
     return results
     return results
 
 

+ 82 - 85
searx/engines/gigablast.py

@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: AGPL-3.0-or-later
 """
 """
  Gigablast (Web)
  Gigablast (Web)
 
 
@@ -9,121 +10,117 @@
  @stable      yes
  @stable      yes
  @parse       url, title, content
  @parse       url, title, content
 """
 """
+# pylint: disable=missing-function-docstring, invalid-name
 
 
-import random
+import re
 from json import loads
 from json import loads
-from time import time
+# from searx import logger
-from lxml.html import fromstring
-from searx.poolrequests import get
 from searx.url_utils import urlencode
 from searx.url_utils import urlencode
-from searx.utils import eval_xpath
+from searx.poolrequests import get
 
 
 # engine dependent config
 # engine dependent config
 categories = ['general']
 categories = ['general']
-paging = True
+# gigablast's pagination is totally damaged, don't use it
-number_of_results = 10
+paging = False
 language_support = True
 language_support = True
 safesearch = True
 safesearch = True
 
 
 # search-url
 # search-url
-base_url = 'https://gigablast.com/'
+base_url = 'https://gigablast.com'
-search_string = 'search?{query}'\
-    '&n={number_of_results}'\
-    '&c=main'\
-    '&s={offset}'\
-    '&format=json'\
-    '&langcountry={lang}'\
-    '&ff={safesearch}'\
-    '&rand={rxikd}'
-# specific xpath variables
-results_xpath = '//response//result'
-url_xpath = './/url'
-title_xpath = './/title'
-content_xpath = './/sum'
-
-supported_languages_url = 'https://gigablast.com/search?&rxikd=1'
-
-extra_param = ''  # gigablast requires a random extra parameter
-# which can be extracted from the source code of the search page
 
 
+# ugly hack: gigablast requires a random extra parameter which can be extracted
+# from the source code of the gigablast HTTP client
+extra_param = ''
+extra_param_path='/search?c=main&qlangcountry=en-us&q=south&s=10'
 
 
 def parse_extra_param(text):
 def parse_extra_param(text):
-    global extra_param
-    param_lines = [x for x in text.splitlines() if x.startswith('var url=') or x.startswith('url=url+')]
-    extra_param = ''
-    for l in param_lines:
-        extra_param += l.split("'")[1]
-    extra_param = extra_param.split('&')[-1]
 
 
-
+    # example:
-def init(engine_settings=None):
+    #
-    parse_extra_param(get('http://gigablast.com/search?c=main&qlangcountry=en-us&q=south&s=10').text)
+    # var uxrl='/search?c=main&qlangcountry=en-us&q=south&s=10&rand=1590740241635&n';
+    # uxrl=uxrl+'sab=730863287';
+    #
+    # extra_param --> "rand=1590740241635&nsab=730863287"
+
+    global extra_param  # pylint: disable=global-statement
+    re_var= None
+    for line in text.splitlines():
+        if re_var is None and extra_param_path in line:
+            var = line.split("=")[0].split()[1]  # e.g. var --> 'uxrl'
+            re_var = re.compile(var + "\\s*=\\s*" + var + "\\s*\\+\\s*'" + "(.*)" + "'(.*)")
+            extra_param = line.split("'")[1][len(extra_param_path):]
+            continue
+        if re_var is not None and re_var.search(line):
+            extra_param += re_var.search(line).group(1)
+            break
+    # logger.debug('gigablast extra_param="%s"', extra_param)
+
+def init(engine_settings=None):  # pylint: disable=unused-argument
+    parse_extra_param(get(base_url + extra_param_path).text)
 
 
 
 
 # do search-request
 # do search-request
-def request(query, params):
+def request(query, params):  # pylint: disable=unused-argument
-    print("EXTRAPARAM:", extra_param)
-    offset = (params['pageno'] - 1) * number_of_results
 
 
-    if params['language'] == 'all':
+    # see API http://www.gigablast.com/api.html#/search
-        language = 'xx'
+    # Take into account, that the API has some quirks ..
-    else:
-        language = params['language'].replace('-', '_').lower()
-        if language.split('-')[0] != 'zh':
-            language = language.split('-')[0]
 
 
-    if params['safesearch'] >= 1:
+    query_args = dict(
-        safesearch = 1
+        c = 'main'
-    else:
+        , format = 'json'
-        safesearch = 0
+        , q = query
+        , dr = 1
+        , showgoodimages = 0
+    )
 
 
-    # rxieu is some kind of hash from the search query, but accepts random atm
+    if params['language'] and params['language'] != 'all':
-    search_path = search_string.format(query=urlencode({'q': query}),
+        query_args['qlangcountry'] = params['language']
-                                       offset=offset,
+        query_args['qlang'] = params['language'].split('-')[0]
-                                       number_of_results=number_of_results,
-                                       lang=language,
-                                       rxikd=int(time() * 1000),
-                                       safesearch=safesearch)
 
 
-    params['url'] = base_url + search_path + '&' + extra_param
+    if params['safesearch'] >= 1:
+        query_args['ff'] = 1
 
 
-    return params
+    search_url = '/search?' + urlencode(query_args)
+    params['url'] = base_url + search_url + extra_param
 
 
+    return params
 
 
 # get response from search-request
 # get response from search-request
 def response(resp):
 def response(resp):
     results = []
     results = []
 
 
-    # parse results
+    response_json = loads(resp.text)
-    try:
+
-        response_json = loads(resp.text)
+    # logger.debug('gigablast returns %s results', len(response_json['results']))
-    except:
-        parse_extra_param(resp.text)
-        raise Exception('extra param expired, please reload')
 
 
     for result in response_json['results']:
     for result in response_json['results']:
-        # append result
+        # see "Example JSON Output (&format=json)"
-        results.append({'url': result['url'],
+        # at http://www.gigablast.com/api.html#/search
-                        'title': result['title'],
-                        'content': result['sum']})
 
 
-    # return results
+        # sort out meaningless result
-    return results
+
+        title = result.get('title')
+        if len(title) < 2:
+            continue
+
+        url = result.get('url')
+        if len(url) < 9:
+            continue
+
+        content = result.get('sum')
+        if len(content) < 5:
+            continue
 
 
+        # extend fields
 
 
-# get supported languages from their site
+        subtitle = result.get('title')
-def _fetch_supported_languages(resp):
+        if len(subtitle) > 3 and subtitle != title:
-    supported_languages = []
+            title += " - " + subtitle
-    dom = fromstring(resp.text)
+
-    links = eval_xpath(dom, '//span[@id="menu2"]/a')
+        results.append(dict(
-    for link in links:
+            url = url
-        href = eval_xpath(link, './@href')[0].split('lang%3A')
+            , title = title
-        if len(href) == 2:
+            , content = content
-            code = href[1].split('_')
+        ))
-            if len(code) == 2:
+
-                code = code[0] + '-' + code[1].upper()
+    return results
-            else:
-                code = code[0]
-            supported_languages.append(code)
-
-    return supported_languages

+ 1 - 1
searx/engines/yacy.py

@@ -75,7 +75,7 @@ def response(resp):
 
 
     for result in search_results[0].get('items', []):
     for result in search_results[0].get('items', []):
         # parse image results
         # parse image results
-        if result.get('image'):
+        if result.get('image') and result.get('width') and result.get('height'):
 
 
             result_url = ''
             result_url = ''
             if 'url' in result:
             if 'url' in result:

+ 1 - 1
searx/engines/yahoo.py

@@ -33,7 +33,7 @@ supported_languages_url = 'https://search.yahoo.com/web/advanced'
 results_xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' Sr ')]"
 results_xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' Sr ')]"
 url_xpath = './/h3/a/@href'
 url_xpath = './/h3/a/@href'
 title_xpath = './/h3/a'
 title_xpath = './/h3/a'
-content_xpath = './/div[@class="compText aAbs"]'
+content_xpath = './/div[contains(@class, "compText")]'
 suggestion_xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' AlsoTry ')]//a"
 suggestion_xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' AlsoTry ')]//a"
 
 
 time_range_dict = {'day': ['1d', 'd'],
 time_range_dict = {'day': ['1d', 'd'],

+ 32 - 4
searx/static/themes/oscar/css/logicodev-dark.css

@@ -70,7 +70,21 @@ input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not
   -ms-user-select: none;
   -ms-user-select: none;
 }
 }
 .onoffswitch-checkbox {
 .onoffswitch-checkbox {
-  display: none;
+  opacity: 0;
+  position: absolute;
+}
+.onoffswitch-checkbox:before {
+  content: "";
+  display: inline-block;
+  width: 16px;
+  height: 16px;
+  margin-right: 10px;
+  position: absolute;
+  left: 0;
+  bottom: 1px;
+  background-color: #fff;
+  border: 1px solid #ccc;
+  border-radius: 0px;
 }
 }
 .onoffswitch-label {
 .onoffswitch-label {
   display: block;
   display: block;
@@ -104,7 +118,7 @@ input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not
   top: 0;
   top: 0;
   bottom: 0;
   bottom: 0;
   right: 0px;
   right: 0px;
-  border: 2px solid #FFFFFF !important;
+  border: 2px solid #FFFFFF;
   border-radius: 50px !important;
   border-radius: 50px !important;
   transition: all 0.3s ease-in 0s;
   transition: all 0.3s ease-in 0s;
 }
 }
@@ -115,6 +129,9 @@ input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not
   right: 71px;
   right: 71px;
   background-color: #A1A1A1;
   background-color: #A1A1A1;
 }
 }
+.onoffswitch-checkbox:focus + .onoffswitch-label .onoffswitch-switch {
+  border: 3px solid #444444;
+}
 .result_header {
 .result_header {
   margin-top: 0px;
   margin-top: 0px;
   margin-bottom: 2px;
   margin-bottom: 2px;
@@ -377,6 +394,17 @@ Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
 .search-margin {
 .search-margin {
   margin-bottom: 0.6em;
   margin-bottom: 0.6em;
 }
 }
+.visually-hidden {
+  position: absolute !important;
+  height: 1px;
+  width: 1px;
+  overflow: hidden;
+  clip: rect(1px 1px 1px 1px);
+  /* IE6, IE7 */
+  clip: rect(1px, 1px, 1px, 1px);
+  white-space: nowrap;
+  /* added line */
+}
 #advanced-search-container {
 #advanced-search-container {
   display: none;
   display: none;
   text-align: left;
   text-align: left;
@@ -407,8 +435,8 @@ Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
   font-weight: bold;
   font-weight: bold;
   border-bottom: #01d7d4 5px solid;
   border-bottom: #01d7d4 5px solid;
 }
 }
-#check-advanced {
+#check-advanced:focus + label {
-  display: none;
+  text-decoration: underline;
 }
 }
 #check-advanced:checked ~ #advanced-search-container {
 #check-advanced:checked ~ #advanced-search-container {
   display: block;
   display: block;

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


+ 32 - 4
searx/static/themes/oscar/css/logicodev.css

@@ -43,7 +43,21 @@ input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not
   -ms-user-select: none;
   -ms-user-select: none;
 }
 }
 .onoffswitch-checkbox {
 .onoffswitch-checkbox {
-  display: none;
+  opacity: 0;
+  position: absolute;
+}
+.onoffswitch-checkbox:before {
+  content: "";
+  display: inline-block;
+  width: 16px;
+  height: 16px;
+  margin-right: 10px;
+  position: absolute;
+  left: 0;
+  bottom: 1px;
+  background-color: #fff;
+  border: 1px solid #ccc;
+  border-radius: 0px;
 }
 }
 .onoffswitch-label {
 .onoffswitch-label {
   display: block;
   display: block;
@@ -77,7 +91,7 @@ input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not
   top: 0;
   top: 0;
   bottom: 0;
   bottom: 0;
   right: 0px;
   right: 0px;
-  border: 2px solid #FFFFFF !important;
+  border: 2px solid #FFFFFF;
   border-radius: 50px !important;
   border-radius: 50px !important;
   transition: all 0.3s ease-in 0s;
   transition: all 0.3s ease-in 0s;
 }
 }
@@ -88,6 +102,9 @@ input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not
   right: 71px;
   right: 71px;
   background-color: #A1A1A1;
   background-color: #A1A1A1;
 }
 }
+.onoffswitch-checkbox:focus + .onoffswitch-label .onoffswitch-switch {
+  border: 3px solid #444444;
+}
 .result_header {
 .result_header {
   margin-top: 0px;
   margin-top: 0px;
   margin-bottom: 2px;
   margin-bottom: 2px;
@@ -350,6 +367,17 @@ Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
 .search-margin {
 .search-margin {
   margin-bottom: 0.6em;
   margin-bottom: 0.6em;
 }
 }
+.visually-hidden {
+  position: absolute !important;
+  height: 1px;
+  width: 1px;
+  overflow: hidden;
+  clip: rect(1px 1px 1px 1px);
+  /* IE6, IE7 */
+  clip: rect(1px, 1px, 1px, 1px);
+  white-space: nowrap;
+  /* added line */
+}
 #advanced-search-container {
 #advanced-search-container {
   display: none;
   display: none;
   text-align: left;
   text-align: left;
@@ -380,8 +408,8 @@ Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
   font-weight: bold;
   font-weight: bold;
   border-bottom: #01d7d4 5px solid;
   border-bottom: #01d7d4 5px solid;
 }
 }
-#check-advanced {
+#check-advanced:focus + label {
-  display: none;
+  text-decoration: underline;
 }
 }
 #check-advanced:checked ~ #advanced-search-container {
 #check-advanced:checked ~ #advanced-search-container {
   display: block;
   display: block;

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


+ 2 - 2
searx/static/themes/oscar/less/logicodev/advanced.less

@@ -31,8 +31,8 @@
     }
     }
 }
 }
 
 
-#check-advanced {
+#check-advanced:focus + label {
-    display: none;
+    text-decoration: underline;
 }
 }
 
 
 #check-advanced:checked ~ #advanced-search-container {
 #check-advanced:checked ~ #advanced-search-container {

+ 19 - 2
searx/static/themes/oscar/less/logicodev/onoff.less

@@ -9,7 +9,21 @@
     -ms-user-select: none;
     -ms-user-select: none;
 }
 }
 .onoffswitch-checkbox {
 .onoffswitch-checkbox {
-    display: none;
+    opacity: 0;
+    position: absolute;
+}
+.onoffswitch-checkbox:before {
+    content: "";
+    display: inline-block;
+    width: 16px;
+    height: 16px;
+    margin-right: 10px;
+    position: absolute;
+    left: 0;
+    bottom: 1px;
+    background-color: #fff;
+    border: 1px solid #ccc;
+    border-radius: 0px;
 }
 }
 .onoffswitch-label {
 .onoffswitch-label {
     display: block;
     display: block;
@@ -44,7 +58,7 @@
     top: 0;
     top: 0;
     bottom: 0;
     bottom: 0;
     right: 0px;
     right: 0px;
-    border: 2px solid #FFFFFF !important;
+    border: 2px solid #FFFFFF;
     border-radius: 50px !important;
     border-radius: 50px !important;
     transition: all 0.3s ease-in 0s;
     transition: all 0.3s ease-in 0s;
 }
 }
@@ -55,3 +69,6 @@
     right: 71px;
     right: 71px;
     background-color: #A1A1A1;
     background-color: #A1A1A1;
 }
 }
+.onoffswitch-checkbox:focus + .onoffswitch-label .onoffswitch-switch {
+    border: 3px solid #444444;
+}

+ 11 - 1
searx/static/themes/oscar/less/logicodev/search.less

@@ -77,4 +77,14 @@ Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
 
 
 .search-margin {
 .search-margin {
     margin-bottom: 0.6em;
     margin-bottom: 0.6em;
-}
+}
+
+.visually-hidden {
+    position: absolute !important;
+    height: 1px;
+    width: 1px;
+    overflow: hidden;
+    clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
+    clip: rect(1px, 1px, 1px, 1px);
+    white-space: nowrap; /* added line */
+}

+ 9 - 5
searx/templates/__common__/about.html

@@ -59,12 +59,16 @@
 
 
   <h2 id='add to browser'>How to set as the default search engine?</h2>
   <h2 id='add to browser'>How to set as the default search engine?</h2>
 
 
-  <dt>Firefox</dt>
+  <p>
+    Searx supports <a href="https://github.com/dewitt/opensearch/blob/master/opensearch-1-1-draft-6.md">OpenSearch</a>.
+    For more information on changing your default search engine, see your browser's documentation:
+  </p>
 
 
-  <dd>
+  <ul>
-    <a href="#" onclick="window.external.AddSearchProvider(window.location.protocol + '//' + window.location.host + '{{ url_for('opensearch') }}');">Install</a>
+    <li><a href="https://support.mozilla.org/en-US/kb/add-or-remove-search-engine-firefox">Firefox</a></li>
-    searx as a search engine on any version of Firefox! (javascript required)
+    <li><a href="https://support.microsoft.com/en-us/help/4028574/microsoft-edge-change-the-default-search-engine" >Microsoft Egde</a></li>
-  </dd>
+    <li>Chrome based browsers <a href="https://www.chromium.org/tab-to-search">only add websites that the user navigates to without a path.</a>
+  </ul>
 
 
   <h2>Where to find anonymous usage statistics of this instance ?</h2>
   <h2>Where to find anonymous usage statistics of this instance ?</h2>
 
 

+ 1 - 1
searx/templates/oscar/advanced.html

@@ -1,4 +1,4 @@
-<input type="checkbox" name="advanced_search" id="check-advanced" {% if advanced_search %} checked="checked"{% endif %}>
+<input type="checkbox" name="advanced_search" class="visually-hidden" id="check-advanced" {% if advanced_search %} checked="checked"{% endif %}>
 <label for="check-advanced">{{- "" -}}
 <label for="check-advanced">{{- "" -}}
     <span class="glyphicon glyphicon-cog"></span>
     <span class="glyphicon glyphicon-cog"></span>
     {{- _('Advanced settings') -}}
     {{- _('Advanced settings') -}}

+ 3 - 3
searx/templates/oscar/base.html

@@ -1,6 +1,6 @@
 {% from 'oscar/macros.html' import icon %}
 {% 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="{{ preferences.get_value('locale') }}" xml:lang="{{ preferences.get_value('locale') }}"{% if rtl %} dir="rtl"{% endif %}>
 <head>
 <head>
     <meta charset="UTF-8" />
     <meta charset="UTF-8" />
     <meta name="description" content="searx - a privacy-respecting, hackable metasearch engine" />
     <meta name="description" content="searx - a privacy-respecting, hackable metasearch engine" />
@@ -8,7 +8,7 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="generator" content="searx/{{ searx_version }}">
     <meta name="generator" content="searx/{{ searx_version }}">
     <meta name="referrer" content="no-referrer">
     <meta name="referrer" content="no-referrer">
-    <meta name="viewport" content="width=device-width, initial-scale=1 , maximum-scale=1.0, user-scalable=1" />
+    <meta name="viewport" content="width=device-width, initial-scale=1 , maximum-scale=2.0, user-scalable=1" />
     {% block meta %}{% endblock %}
     {% block meta %}{% endblock %}
     <script src="{{ url_for('js_translations') }}"></script>
     <script src="{{ url_for('js_translations') }}"></script>
     <title>{% block title %}{% endblock %}{{ instance_name }}</title>
     <title>{% block title %}{% endblock %}{{ instance_name }}</title>
@@ -42,7 +42,7 @@
         <style type="text/css">
         <style type="text/css">
             .tab-content > .active_if_nojs, .active_if_nojs {display: block !important; visibility: visible !important;}
             .tab-content > .active_if_nojs, .active_if_nojs {display: block !important; visibility: visible !important;}
             .margin_top_if_nojs {margin-top: 20px;}
             .margin_top_if_nojs {margin-top: 20px;}
-            .hide_if_nojs {display: none !important;overflow:none !important;}
+            .hide_if_nojs {display: none !important;overflow: hidden !important;}
             .disabled_if_nojs {pointer-events: none; cursor: default; text-decoration: line-through;}
             .disabled_if_nojs {pointer-events: none; cursor: default; text-decoration: line-through;}
         </style>
         </style>
     </noscript>
     </noscript>

+ 2 - 2
searx/templates/oscar/index.html

@@ -6,10 +6,10 @@
             {% if cookies['oscar-style'] == 'pointhi' %}
             {% if cookies['oscar-style'] == 'pointhi' %}
                 <h1 class="text-hide center-block"><img class="center-block img-responsive" src="{{ url_for('static', filename='img/searx_logo.png') }}" alt="searx logo"/>searx</h1>
                 <h1 class="text-hide center-block"><img class="center-block img-responsive" src="{{ url_for('static', filename='img/searx_logo.png') }}" alt="searx logo"/>searx</h1>
             {% else %}
             {% else %}
-                <h1 class="text-hide center-block" id="main-logo">
+                <div class="text-hide center-block" id="main-logo">
                     <img class="center-block img-responsive" src="{{ url_for('static', filename='img/logo_searx_a.png') }}" alt="searx logo" />
                     <img class="center-block img-responsive" src="{{ url_for('static', filename='img/logo_searx_a.png') }}" alt="searx logo" />
                     searx
                     searx
-                </h1>
+                </div>
             {% endif %}
             {% endif %}
         </div>
         </div>
     </div>
     </div>

+ 1 - 1
searx/templates/oscar/infobox.html

@@ -5,7 +5,7 @@
         {% for u in infobox.urls %}{% if u.official %} <a href="{{ u.url }}">{{ u.domain }}</a>{% endif %}{% endfor %}
         {% for u in infobox.urls %}{% if u.official %} <a href="{{ u.url }}">{{ u.domain }}</a>{% endif %}{% endfor %}
     </div>
     </div>
     <div class="panel-body">
     <div class="panel-body">
-        {% if infobox.img_src %}<img class="img-responsive center-block infobox_part" src="{{ image_proxify(infobox.img_src) }}" alt="{{ infobox.infobox }}" />{% endif %}
+        {% if infobox.img_src %}<img class="img-responsive center-block infobox_part" src="{{ image_proxify(infobox.img_src) }}" />{% endif %}
 
 
         {% if infobox.content %}<bdi><p class="infobox_part">{{ infobox.content | safe }}</p></bdi>{% endif %}
         {% if infobox.content %}<bdi><p class="infobox_part">{{ infobox.content | safe }}</p></bdi>{% endif %}
 
 

+ 1 - 0
searx/templates/oscar/languages.html

@@ -1,3 +1,4 @@
+<label class="visually-hidden" for="language">{{ _('Language') }}</label>
 <select class="language custom-select form-control" id="language" name="language" accesskey="l">
 <select class="language custom-select form-control" id="language" name="language" accesskey="l">
   <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) -%}

+ 17 - 16
searx/templates/oscar/macros.html

@@ -8,34 +8,34 @@
     <img width="32" height="32" class="favicon" src="{{ url_for('static', filename='themes/oscar/img/icons/' + favicon + '.png') }}" alt="{{ favicon }}" />
     <img width="32" height="32" class="favicon" src="{{ url_for('static', filename='themes/oscar/img/icons/' + favicon + '.png') }}" alt="{{ favicon }}" />
 {%- endmacro %}
 {%- endmacro %}
 
 
-{%- macro result_link(url, title, classes='') -%}
+{%- macro result_link(url, title, classes='', id='') -%}
-<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 %}{% if id %} aria-labelledby="result-{{id}}"{%endif%}>{{ title }}</a>
 {%- endmacro -%}
 {%- endmacro -%}
 
 
 <!-- Draw result header -->
 <!-- Draw result header -->
-{% macro result_header(result, favicons) -%}
+{% macro result_header(result, favicons, id) -%}
-<h4 class="result_header">{% if result.engine~".png" in favicons %}{{ draw_favicon(result.engine) }} {% endif %}{% if result.url %}{{ result_link(result.url, result.title|safe) }}{% else %}{{ result.title|safe}}{% endif %}</h4>
+<h4 class="result_header" id="result-{{id}}">{% if result.engine~".png" in favicons %}{{ draw_favicon(result.engine) }} {% endif %}{% if result.url %}{{ result_link(result.url, result.title|safe, id=id) }}{% else %}{{ result.title|safe}}{% endif %}</h4>
 {%- endmacro %}
 {%- endmacro %}
 
 
 <!-- Draw result sub header -->
 <!-- Draw result sub header -->
-{% macro result_sub_header(result) -%}
+{% macro result_sub_header(result, id) -%}
     {% if result.publishedDate %}<time class="text-muted" datetime="{{ result.pubdate }}" >{{ result.publishedDate }}</time>{% endif %}
     {% if result.publishedDate %}<time class="text-muted" datetime="{{ result.pubdate }}" >{{ result.publishedDate }}</time>{% endif %}
-    {% if result.magnetlink %}<small> &bull; {{ result_link(result.magnetlink, icon('magnet') + _('magnet link'), "magnetlink") }}</small>{% endif %}
+    {% if result.magnetlink %}<small> &bull; {{ result_link(result.magnetlink, icon('magnet') + _('magnet link'), "magnetlink", id) }}</small>{% endif %}
-    {% if result.torrentfile %}<small> &bull; {{ result_link(result.torrentfile, icon('download-alt') + _('torrent file'), "torrentfile") }}</small>{% endif %}
+    {% if result.torrentfile %}<small> &bull; {{ result_link(result.torrentfile, icon('download-alt') + _('torrent file'), "torrentfile", id) }}</small>{% endif %}
 {%- endmacro %}
 {%- endmacro %}
 
 
 <!-- Draw result footer -->
 <!-- Draw result footer -->
-{% macro result_footer(result) -%}
+{% macro result_footer(result, id) -%}
     <div class="clearfix"></div>{{- "" -}}
     <div class="clearfix"></div>{{- "" -}}
     <div class="pull-right">
     <div class="pull-right">
         {%- for engine in result.engines -%}
         {%- for engine in result.engines -%}
             <span class="label label-default">{{ engine }}</span>
             <span class="label label-default">{{ engine }}</span>
         {%- endfor -%}
         {%- endfor -%}
         {%- if result.url -%}
         {%- if result.url -%}
-        <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info") }}</small>
+        <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info", id) }}</small>
         {%- endif -%}
         {%- endif -%}
         {%- 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", id) }}</small>
         {%- endif -%}
         {%- endif -%}
     </div>
     </div>
     {%- if result.pretty_url -%}
     {%- if result.pretty_url -%}
@@ -44,31 +44,31 @@
 {%- endmacro %}
 {%- endmacro %}
 
 
 <!-- Draw result footer -->
 <!-- Draw result footer -->
-{% macro result_footer_rtl(result) -%}
+{% macro result_footer_rtl(result, id) -%}
     <div class="clearfix"></div>{{- "" -}}
     <div class="clearfix"></div>{{- "" -}}
     {% for engine in result.engines -%}
     {% for engine in result.engines -%}
         <span class="label label-default">{{ engine }}</span>
         <span class="label label-default">{{ engine }}</span>
     {%- endfor %}
     {%- endfor %}
     {%- if result.url -%}
     {%- if result.url -%}
-    <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info") }}</small>
+    <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info", id) }}</small>
     {%- endif -%}
     {%- endif -%}
     {% 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", id) }}</small>
     {%- endif %}
     {%- endif %}
     {%- if result.pretty_url -%}
     {%- if result.pretty_url -%}
     <div class="external-link">{{ result.pretty_url }}</div>
     <div class="external-link">{{ result.pretty_url }}</div>
     {%- endif %}
     {%- endif %}
 {%- endmacro %}
 {%- endmacro %}
 
 
-{% macro preferences_item_header(info, label, rtl) -%}
+{% macro preferences_item_header(info, label, rtl, id) -%}
     {% if rtl %}
     {% if rtl %}
     <div class="row form-group">
     <div class="row form-group">
-        <label class="col-sm-3 col-md-2 pull-right">{{ label }}</label>
+        <label class="col-sm-3 col-md-2 pull-right"{% if id %} for="{{id}}"{% endif %}>{{ label }}</label>
         <span class="col-sm-5 col-md-6 help-block pull-left">{{ info }}</span>
         <span class="col-sm-5 col-md-6 help-block pull-left">{{ info }}</span>
         <div class="col-sm-4 col-md-4">
         <div class="col-sm-4 col-md-4">
     {% else %}
     {% else %}
     <div class="row form-group">
     <div class="row form-group">
-        <label class="col-sm-3 col-md-2">{{ label }}</label>
+        <label class="col-sm-3 col-md-2"{% if id %} for="{{id}}"{% endif %}>{{ label }}</label>
         <div class="col-sm-4 col-md-4">
         <div class="col-sm-4 col-md-4">
     {% endif %}
     {% endif %}
 {%- endmacro %}
 {%- endmacro %}
@@ -91,6 +91,7 @@
             <span class="onoffswitch-inner"></span>
             <span class="onoffswitch-inner"></span>
             <span class="onoffswitch-switch"></span>
             <span class="onoffswitch-switch"></span>
         </label>
         </label>
+        <label class="visually-hidden" for="{{ id }}">{{ _('Allow') }}</label>
     </div>
     </div>
 {%- endmacro %}
 {%- endmacro %}
 
 

+ 47 - 47
searx/templates/oscar/preferences.html

@@ -30,9 +30,9 @@
                         <div class="col-sm-11 col-md-10">
                         <div class="col-sm-11 col-md-10">
                             {% include 'oscar/categories.html' %}
                             {% include 'oscar/categories.html' %}
                         </div>
                         </div>
-                        <label class="col-sm-3 col-md-2">{{ _('Default categories') }}</label>
+                        <label class="col-sm-3 col-md-2" for="categories">{{ _('Default categories') }}</label>
                         {% else %}
                         {% else %}
-                        <label class="col-sm-3 col-md-2">{{ _('Default categories') }}</label>
+                        <label class="col-sm-3 col-md-2" for="categories">{{ _('Default categories') }}</label>
                         <div class="col-sm-11 col-md-10 search-categories">
                         <div class="col-sm-11 col-md-10 search-categories">
                             {% include 'oscar/categories.html' %}
                             {% include 'oscar/categories.html' %}
                         </div>
                         </div>
@@ -40,14 +40,14 @@
                     </div>
                     </div>
                     {% set language_label = _('Search language') %}
                     {% set language_label = _('Search language') %}
                     {% set language_info = _('What language do you prefer for search?') %}
                     {% set language_info = _('What language do you prefer for search?') %}
-                    {{ preferences_item_header(language_info, language_label, rtl) }}
+                    {{ preferences_item_header(language_info, language_label, rtl, 'language') }}
                                                 {% include 'oscar/languages.html' %}
                                                 {% include 'oscar/languages.html' %}
                     {{ preferences_item_footer(language_info, language_label, rtl) }}
                     {{ preferences_item_footer(language_info, language_label, rtl) }}
 
 
                     {% set locale_label = _('Interface language') %}
                     {% set locale_label = _('Interface language') %}
                     {% set locale_info = _('Change the language of the layout') %}
                     {% set locale_info = _('Change the language of the layout') %}
-                    {{ preferences_item_header(locale_info, locale_label, rtl) }}
+                    {{ preferences_item_header(locale_info, locale_label, rtl, 'locale') }}
-                        <select class="form-control" name='locale'>
+                        <select class="form-control" name="locale" id="locale">
                             {% for locale_id,locale_name in locales.items() | sort %}
                             {% for locale_id,locale_name in locales.items() | sort %}
                             <option value="{{ locale_id }}" {% if locale_id == current_locale %}selected="selected"{% endif %}>{{ locale_name }}</option>
                             <option value="{{ locale_id }}" {% if locale_id == current_locale %}selected="selected"{% endif %}>{{ locale_name }}</option>
                             {% endfor %}
                             {% endfor %}
@@ -56,8 +56,8 @@
 
 
                     {% set autocomplete_label = _('Autocomplete') %}
                     {% set autocomplete_label = _('Autocomplete') %}
                     {% set autocomplete_info = _('Find stuff as you type') %}
                     {% set autocomplete_info = _('Find stuff as you type') %}
-                    {{ preferences_item_header(autocomplete_info, autocomplete_label, rtl) }}
+                    {{ preferences_item_header(autocomplete_info, autocomplete_label, rtl, 'autocomplete') }}
-                        <select class="form-control" name="autocomplete">
+                        <select class="form-control" name="autocomplete" id="autocomplete">
                             <option value=""> - </option>
                             <option value=""> - </option>
                             {% for backend in autocomplete_backends %}
                             {% for backend in autocomplete_backends %}
                             <option value="{{ backend }}" {% if backend == autocomplete %}selected="selected"{% endif %}>{{ backend }}</option>
                             <option value="{{ backend }}" {% if backend == autocomplete %}selected="selected"{% endif %}>{{ backend }}</option>
@@ -67,8 +67,8 @@
 
 
                     {% set image_proxy_label = _('Image proxy') %}
                     {% set image_proxy_label = _('Image proxy') %}
                     {% set image_proxy_info = _('Proxying image results through searx') %}
                     {% set image_proxy_info = _('Proxying image results through searx') %}
-                    {{ preferences_item_header(image_proxy_info, image_proxy_label, rtl) }}
+                    {{ preferences_item_header(image_proxy_info, image_proxy_label, rtl, 'image_proxy') }}
-                        <select class="form-control" name='image_proxy'>
+                        <select class="form-control" name="image_proxy" id="image_proxy">
                             <option value="1" {% if image_proxy  %}selected="selected"{% endif %}>{{ _('Enabled') }}</option>
                             <option value="1" {% if image_proxy  %}selected="selected"{% endif %}>{{ _('Enabled') }}</option>
                             <option value="" {% if not image_proxy %}selected="selected"{% endif %}>{{ _('Disabled')}}</option>
                             <option value="" {% if not image_proxy %}selected="selected"{% endif %}>{{ _('Disabled')}}</option>
                         </select>
                         </select>
@@ -76,8 +76,8 @@
 
 
                     {% set method_label = _('Method') %}
                     {% set method_label = _('Method') %}
                     {% set method_info = _('Change how forms are submited, <a href="http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods" rel="external">learn more about request methods</a>') %}
                     {% set method_info = _('Change how forms are submited, <a href="http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods" rel="external">learn more about request methods</a>') %}
-                    {{ preferences_item_header(method_info, method_label, rtl) }}
+                    {{ preferences_item_header(method_info, method_label, rtl, 'method') }}
-                        <select class="form-control" name='method'>
+                        <select class="form-control" name="method" id="method">
                             <option value="POST" {% if method == 'POST' %}selected="selected"{% endif %}>POST</option>
                             <option value="POST" {% if method == 'POST' %}selected="selected"{% endif %}>POST</option>
                             <option value="GET" {% if method == 'GET' %}selected="selected"{% endif %}>GET</option>
                             <option value="GET" {% if method == 'GET' %}selected="selected"{% endif %}>GET</option>
                         </select>
                         </select>
@@ -85,8 +85,8 @@
 
 
                     {% set safesearch_label = _('SafeSearch') %}
                     {% set safesearch_label = _('SafeSearch') %}
                     {% set safesearch_info = _('Filter content') %}
                     {% set safesearch_info = _('Filter content') %}
-                    {{ preferences_item_header(safesearch_info, safesearch_label, rtl) }}
+                    {{ preferences_item_header(safesearch_info, safesearch_label, rtl, 'safesearch') }}
-                        <select class="form-control" name='safesearch'>
+                        <select class="form-control" name="safesearch" id="safesearch">
                             <option value="2" {% if safesearch == '2' %}selected="selected"{% endif %}>{{ _('Strict') }}</option>
                             <option value="2" {% if safesearch == '2' %}selected="selected"{% endif %}>{{ _('Strict') }}</option>
                             <option value="1" {% if safesearch == '1' %}selected="selected"{% endif %}>{{ _('Moderate') }}</option>
                             <option value="1" {% if safesearch == '1' %}selected="selected"{% endif %}>{{ _('Moderate') }}</option>
                             <option value="0" {% if safesearch == '0' %}selected="selected"{% endif %}>{{ _('None') }}</option>
                             <option value="0" {% if safesearch == '0' %}selected="selected"{% endif %}>{{ _('None') }}</option>
@@ -95,16 +95,16 @@
 
 
                     {% set theme_label = _('Themes') %}
                     {% set theme_label = _('Themes') %}
                     {% set theme_info = _('Change searx layout') %}
                     {% set theme_info = _('Change searx layout') %}
-                    {{ preferences_item_header(theme_info, theme_label, rtl) }}
+                    {{ preferences_item_header(theme_info, theme_label, rtl, 'theme') }}
-                        <select class="form-control" name="theme">
+                        <select class="form-control" name="theme" id="theme">
                             {% for name in themes %}
                             {% for name in themes %}
                             <option value="{{ name }}" {% if name == theme %}selected="selected"{% endif %}>{{ name }}</option>
                             <option value="{{ name }}" {% if name == theme %}selected="selected"{% endif %}>{{ name }}</option>
                             {% endfor %}
                             {% endfor %}
                         </select>
                         </select>
                     {{ preferences_item_footer(theme_info, theme_label, rtl) }}
                     {{ preferences_item_footer(theme_info, theme_label, rtl) }}
 
 
-                    {{ preferences_item_header(_('Choose style for this theme'), _('Style'), rtl) }}
+                    {{ preferences_item_header(_('Choose style for this theme'), _('Style'), rtl, 'oscar_style') }}
-                        <select class="form-control" name='oscar-style'>
+                        <select class="form-control" name="oscar-style" id="oscar_style">
                             <option value="logicodev" >Logicodev</option>
                             <option value="logicodev" >Logicodev</option>
                             <option value="pointhi" {% if preferences.get_value('oscar-style') == 'pointhi' %}selected="selected"{% endif %}>Pointhi</option>
                             <option value="pointhi" {% if preferences.get_value('oscar-style') == 'pointhi' %}selected="selected"{% endif %}>Pointhi</option>
                             <option value="logicodev-dark" {% if preferences.get_value('oscar-style') == 'logicodev-dark' %}selected="selected"{% endif %}>Logicodev dark</option>
                             <option value="logicodev-dark" {% if preferences.get_value('oscar-style') == 'logicodev-dark' %}selected="selected"{% endif %}>Logicodev dark</option>
@@ -113,8 +113,8 @@
 
 
                     {% set label = _('Results on new tabs') %}
                     {% set label = _('Results on new tabs') %}
                     {% set info = _('Open result links on new browser tabs') %}
                     {% set info = _('Open result links on new browser tabs') %}
-                    {{ preferences_item_header(info, label, rtl) }}
+                    {{ preferences_item_header(info, label, rtl, 'results_on_new_tab') }}
-                        <select class="form-control" name='results_on_new_tab'>
+                        <select class="form-control" name="results_on_new_tab" id="results_on_new_tab">
                             <option value="1" {% if results_on_new_tab %}selected="selected"{% endif %}>{{ _('On') }}</option>
                             <option value="1" {% if results_on_new_tab %}selected="selected"{% endif %}>{{ _('On') }}</option>
                             <option value="0" {% if not results_on_new_tab %}selected="selected"{% endif %}>{{ _('Off')}}</option>
                             <option value="0" {% if not results_on_new_tab %}selected="selected"{% endif %}>{{ _('Off')}}</option>
                         </select>
                         </select>
@@ -122,8 +122,8 @@
 
 
                     {% set label = _('Open Access DOI resolver') %}
                     {% set label = _('Open Access DOI resolver') %}
                     {% set info = _('Redirect to open-access versions of publications when available (plugin required)') %}
                     {% set info = _('Redirect to open-access versions of publications when available (plugin required)') %}
-                    {{ preferences_item_header(info, label, rtl) }}
+                    {{ preferences_item_header(info, label, rtl, 'doi_resolver') }}
-                        <select class="form-control" id='doi_resolver' name='doi_resolver'>
+                        <select class="form-control" name="doi_resolver" id="doi_resolver">
                             {% for doi_resolver_name,doi_resolver_url in doi_resolvers.items() %}
                             {% for doi_resolver_name,doi_resolver_url in doi_resolvers.items() %}
                             <option value="{{ doi_resolver_name }}" {% if doi_resolver_name == current_doi_resolver %}selected="selected"{% endif %}>
                             <option value="{{ doi_resolver_name }}" {% if doi_resolver_name == current_doi_resolver %}selected="selected"{% endif %}>
                                     {{ doi_resolver_name }} - {{ doi_resolver_url }}
                                     {{ doi_resolver_name }} - {{ doi_resolver_url }}
@@ -134,8 +134,8 @@
 
 
                     {% set label = _('Engine tokens') %}
                     {% set label = _('Engine tokens') %}
                     {% set info = _('Access tokens for private engines') %}
                     {% set info = _('Access tokens for private engines') %}
-                    {{ preferences_item_header(info, label, rtl) }}
+                    {{ preferences_item_header(info, label, rtl, 'tokens') }}
-                        <input class="form-control" id='tokens' name='tokens' value='{{ preferences.tokens.get_value() }}'/>
+                        <input class="form-control" id="tokens" name="tokens" value='{{ preferences.tokens.get_value() }}'/>
                     {{ preferences_item_footer(info, label, rtl) }}
                     {{ preferences_item_footer(info, label, rtl) }}
                 </div>
                 </div>
                 </fieldset>
                 </fieldset>
@@ -173,23 +173,23 @@
                           <table class="table table-hover table-condensed table-striped">
                           <table class="table table-hover table-condensed table-striped">
                                 <tr>
                                 <tr>
                                     {% if not rtl %}
                                     {% if not rtl %}
-                                    <th>{{ _("Allow") }}</th>
+                                    <th scope="col">{{ _("Allow") }}</th>
-                                    <th>{{ _("Engine name") }}</th>
+                                    <th scope="col">{{ _("Engine name") }}</th>
-                                    <th>{{ _("Shortcut") }}</th>
+                                    <th scope="col">{{ _("Shortcut") }}</th>
-                                    <th>{{ _("Selected language") }}</th>
+                                    <th scope="col">{{ _("Selected language") }}</th>
-                                    <th>{{ _("SafeSearch") }}</th>
+                                    <th scope="col">{{ _("SafeSearch") }}</th>
-                                    <th>{{ _("Time range") }}</th>
+                                    <th scope="col">{{ _("Time range") }}</th>
-                                    <th>{{ _("Avg. time") }}</th>
+                                    <th scope="col">{{ _("Avg. time") }}</th>
-                                    <th>{{ _("Max time") }}</th>
+                                    <th scope="col">{{ _("Max time") }}</th>
                                     {% else %}
                                     {% else %}
-                                    <th class="text-right">{{ _("Max time") }}</th>
+                                    <th scope="col" class="text-right">{{ _("Max time") }}</th>
-                                    <th class="text-right">{{ _("Avg. time") }}</th>
+                                    <th scope="col" class="text-right">{{ _("Avg. time") }}</th>
-                                    <th class="text-right">{{ _("Time range") }}</th>
+                                    <th scope="col" class="text-right">{{ _("Time range") }}</th>
-                                    <th class="text-right">{{ _("SafeSearch") }}</th>
+                                    <th scope="col" class="text-right">{{ _("SafeSearch") }}</th>
-                                    <th class="text-right">{{ _("Selected language") }}</th>
+                                    <th scope="col" class="text-right">{{ _("Selected language") }}</th>
-                                    <th class="text-right">{{ _("Shortcut") }}</th>
+                                    <th scope="col" class="text-right">{{ _("Shortcut") }}</th>
-                                    <th class="text-right">{{ _("Engine name") }}</th>
+                                    <th scope="col" class="text-right">{{ _("Engine name") }}</th>
-                                    <th class="text-right">{{ _("Allow") }}</th>
+                                    <th scope="col" class="text-right">{{ _("Allow") }}</th>
                                     {% endif %}
                                     {% endif %}
                                 </tr>
                                 </tr>
                         {% for search_engine in engines_by_category[categ] %}
                         {% for search_engine in engines_by_category[categ] %}
@@ -199,21 +199,21 @@
                                     <td class="onoff-checkbox">
                                     <td class="onoff-checkbox">
                                         {{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}
                                         {{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}
                                     </td>
                                     </td>
-                                    <th>{{ search_engine.name }}</th>
+                                    <th scope="row">{{ search_engine.name }}</th>
                                     <td class="name">{{ shortcuts[search_engine.name] }}</td>
                                     <td class="name">{{ shortcuts[search_engine.name] }}</td>
                                         <td>{{ support_toggle(stats[search_engine.name].supports_selected_language) }}</td>
                                         <td>{{ support_toggle(stats[search_engine.name].supports_selected_language) }}</td>
                                         <td>{{ support_toggle(search_engine.safesearch==True) }}</td>
                                         <td>{{ support_toggle(search_engine.safesearch==True) }}</td>
                                         <td>{{ support_toggle(search_engine.time_range_support==True) }}</td>
                                         <td>{{ support_toggle(search_engine.time_range_support==True) }}</td>
-                                        <td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td>
+                                        <td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{% if stats[search_engine.name]['warn_time'] %}{{ icon('exclamation-sign')}} {% endif %}{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td>
-                                        <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td>
+                                        <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{% if stats[search_engine.name]['warn_timeout'] %}{{ icon('exclamation-sign') }} {% endif %}{{ search_engine.timeout }}</td>
-                                                                        {% else %}
+                                    {% else %}
-                                        <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td>
+                                        <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}{% if stats[search_engine.name]['warn_time'] %} {{ icon('exclamation-sign')}}{% endif %}</td>
-                                        <td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td>
+                                        <td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}{% if stats[search_engine.name]['warn_time'] %} {{ icon('exclamation-sign')}}{% endif %}</td>
                                         <td>{{ support_toggle(search_engine.time_range_support==True) }}</td>
                                         <td>{{ support_toggle(search_engine.time_range_support==True) }}</td>
                                         <td>{{ support_toggle(search_engine.safesearch==True) }}</td>
                                         <td>{{ support_toggle(search_engine.safesearch==True) }}</td>
                                         <td>{{ support_toggle(stats[search_engine.name].supports_selected_language) }}</td>
                                         <td>{{ support_toggle(stats[search_engine.name].supports_selected_language) }}</td>
                                         <td>{{ shortcuts[search_engine.name] }}</td>
                                         <td>{{ shortcuts[search_engine.name] }}</td>
-                                    <th>{{ search_engine.name }}</th>
+                                    <th scope="row">{{ search_engine.name }}</th>
                                     <td class="onoff-checkbox">
                                     <td class="onoff-checkbox">
                                         {{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}
                                         {{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}
                                     </td>
                                     </td>
@@ -241,7 +241,7 @@
                                 <h3 class="panel-title">{{ _(plugin.name) }}</h3>
                                 <h3 class="panel-title">{{ _(plugin.name) }}</h3>
                             </div>
                             </div>
                             <div class="panel-body">
                             <div class="panel-body">
-                                <div class="col-xs-6 col-sm-4 col-md-6">{{ _(plugin.description) }}</div>
+                                <div class="col-xs-6 col-sm-4 col-md-6"><label for="{{'plugin_' + plugin.id}}">{{ _(plugin.description) }}</label></div>
                                 <div class="col-xs-6 col-sm-4 col-md-6">
                                 <div class="col-xs-6 col-sm-4 col-md-6">
                                     <div class="onoff-checkbox">
                                     <div class="onoff-checkbox">
                                     {{ checkbox_toggle('plugin_' + plugin.id, plugin.id not in allowed_plugins) }}
                                     {{ checkbox_toggle('plugin_' + plugin.id, plugin.id not in allowed_plugins) }}

+ 2 - 2
searx/templates/oscar/result_templates/code.html

@@ -1,11 +1,11 @@
-{% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon %}
+{% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon with context%}
 
 
 {{ result_header(result, favicons) }}
 {{ result_header(result, favicons) }}
 {{ result_sub_header(result) }}
 {{ result_sub_header(result) }}
 
 
 {% if result.content %}<p class="result-content">{{ result.content|safe }}</p>{% endif %}
 {% if result.content %}<p class="result-content">{{ result.content|safe }}</p>{% endif %}
 
 
-{% if result.repository %}<p class="result-content">{{ icon('file') }} <a href="{{ result.repository }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.repository }}</a></p>{% endif %}
+{% if result.repository %}<p class="result-content">{{ icon('file') }} <a href="{{ result.repository }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %} aria-labelledby="result-{{loop.index}}">{{ result.repository }}</a></p>{% endif %}
 
 
 <div dir="ltr">
 <div dir="ltr">
 {{ result.codelines|code_highlighter(result.code_language)|safe }}
 {{ result.codelines|code_highlighter(result.code_language)|safe }}

+ 6 - 6
searx/templates/oscar/result_templates/default.html

@@ -1,10 +1,10 @@
 {% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon with context %}
 {% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon with context %}
 
 
-{{- result_header(result, favicons) -}}
+{{- result_header(result, favicons, loop.index) -}}
-{{- result_sub_header(result) -}}
+{{- result_sub_header(result, loop.index) -}}
 
 
 {%- if result.embedded -%}
 {%- if result.embedded -%}
-    <small> &bull; <a class="text-info btn-collapse collapsed cursor-pointer media-loader disabled_if_nojs" data-toggle="collapse" data-target="#result-media-{{ index }}" data-btn-text-collapsed="{{ _('show media') }}" data-btn-text-not-collapsed="{{ _('hide media') }}">{{ icon('music') }} {{ _('show media') }}</a></small>
+    <small> &bull; <a class="text-info btn-collapse collapsed cursor-pointer media-loader disabled_if_nojs" data-toggle="collapse" data-target="#result-media-{{ index }}" data-btn-text-collapsed="{{ _('show media') }}" data-btn-text-not-collapsed="{{ _('hide media') }}" aria-labelledby="result-{{loop.index}}">{{ icon('music') }} {{ _('show media') }}</a></small>
 {%- endif -%}
 {%- endif -%}
 
 
 {%- if result.embedded -%}
 {%- if result.embedded -%}
@@ -16,7 +16,7 @@
 {%- if result.img_src -%}
 {%- if result.img_src -%}
 <div class="container-fluid">
 <div class="container-fluid">
     <div class="row">
     <div class="row">
-<img src="{{ image_proxify(result.img_src) }}" alt="{{ result.title|striptags }}" title="{{ result.title|striptags }}" style="width: auto; max-height: 60px; min-height: 60px;" class="col-xs-2 col-sm-4 col-md-4 result-content">
+<img src="{{ image_proxify(result.img_src) }}" title="{{ result.title|striptags }}" style="width: auto; max-height: 60px; min-height: 60px;" class="col-xs-2 col-sm-4 col-md-4 result-content">
 {% if result.content %}<p class="result-content col-xs-8 col-sm-8 col-md-8">{{ result.content|safe }}</p>{% endif -%}
 {% if result.content %}<p class="result-content col-xs-8 col-sm-8 col-md-8">{{ result.content|safe }}</p>{% endif -%}
     </div>
     </div>
 </div>
 </div>
@@ -25,7 +25,7 @@
 {%- endif -%}
 {%- endif -%}
 
 
 {%- if rtl -%}
 {%- if rtl -%}
-{{ result_footer_rtl(result) }}
+{{ result_footer_rtl(result, loop.index) }}
 {%- else -%}
 {%- else -%}
-{{ result_footer(result) }}
+{{ result_footer(result, loop.index) }}
 {%- endif -%}
 {%- endif -%}

+ 2 - 2
searx/templates/oscar/result_templates/images.html

@@ -1,6 +1,6 @@
-{%- from 'oscar/macros.html' import draw_favicon -%}
+{%- from 'oscar/macros.html' import draw_favicon with context -%}
 
 
-<a href="{{ result.img_src }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %} data-toggle="modal" data-target="#modal-{{ index }}-{{pageno}}">{{- "" -}}
+<a href="{{ result.img_src }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %} data-toggle="modal" data-target="#modal-{{ index }}-{{pageno}}" id="result-{{loop.index}}">{{- "" -}}
     <img src="{% if result.thumbnail_src %}{{ image_proxify(result.thumbnail_src) }}{% else %}{{ image_proxify(result.img_src) }}{% endif %}" alt="{{ result.title|striptags }}" title="{{ result.title|striptags }}" class="img-thumbnail">{{- "" -}}
     <img src="{% if result.thumbnail_src %}{{ image_proxify(result.thumbnail_src) }}{% else %}{{ image_proxify(result.img_src) }}{% endif %}" alt="{{ result.title|striptags }}" title="{{ result.title|striptags }}" class="img-thumbnail">{{- "" -}}
 </a>
 </a>
 <div class="modal fade" id="modal-{{ index }}-{{ pageno }}" tabindex="-1" role="dialog" aria-hidden="true">{{- "" -}}
 <div class="modal fade" id="modal-{{ index }}-{{ pageno }}" tabindex="-1" role="dialog" aria-hidden="true">{{- "" -}}

+ 4 - 4
searx/templates/oscar/result_templates/map.html

@@ -1,7 +1,7 @@
 {% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon %}
 {% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon %}
 
 
-{{ result_header(result, favicons) }}
+{{ result_header(result, favicons, loop.index) }}
-{{ result_sub_header(result) }}
+{{ result_sub_header(result, loop.index) }}
 
 
 {% if (result.latitude and result.longitude) or result.boundingbox %}
 {% if (result.latitude and result.longitude) or result.boundingbox %}
     <small> &bull; <a class="text-info btn-collapse collapsed searx_init_map cursor-pointer disabled_if_nojs" data-toggle="collapse" data-target="#result-map-{{ index }}" data-leaflet-target="osm-map-{{ index }}" data-map-lon="{{ result.longitude }}" data-map-lat="{{ result.latitude }}" {% if result.boundingbox %}data-map-boundingbox='{{ result.boundingbox|tojson|safe }}'{% endif %} {% if result.geojson %}data-map-geojson='{{ result.geojson|tojson|safe }}'{% endif %} data-btn-text-collapsed="{{ _('show map') }}" data-btn-text-not-collapsed="{{ _('hide map') }}">{{ icon('globe') }} {{ _('show map') }}</a></small>
     <small> &bull; <a class="text-info btn-collapse collapsed searx_init_map cursor-pointer disabled_if_nojs" data-toggle="collapse" data-target="#result-map-{{ index }}" data-leaflet-target="osm-map-{{ index }}" data-map-lon="{{ result.longitude }}" data-map-lat="{{ result.latitude }}" {% if result.boundingbox %}data-map-boundingbox='{{ result.boundingbox|tojson|safe }}'{% endif %} {% if result.geojson %}data-map-geojson='{{ result.geojson|tojson|safe }}'{% endif %} data-btn-text-collapsed="{{ _('show map') }}" data-btn-text-not-collapsed="{{ _('hide map') }}">{{ icon('globe') }} {{ _('show map') }}</a></small>
@@ -66,7 +66,7 @@
 {% endif %}
 {% endif %}
 
 
 {% if rtl %}
 {% if rtl %}
-{{ result_footer_rtl(result) }}
+{{ result_footer_rtl(result, loop.index) }}
 {% else %}
 {% else %}
-{{ result_footer(result) }}
+{{ result_footer(result, loop.index) }}
 {% endif %}
 {% endif %}

+ 4 - 4
searx/templates/oscar/result_templates/torrent.html

@@ -1,7 +1,7 @@
 {% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon %}
 {% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon %}
 
 
-{{ result_header(result, favicons) }}
+{{ result_header(result, favicons, loop.index) }}
-{{ result_sub_header(result) }}
+{{ result_sub_header(result, loop.index) }}
 
 
 {% if result.seed is defined %}<p class="result-content">{{ icon('transfer') }} {{ _('Seeder') }} <span class="badge">{{ result.seed }}</span> &bull; {{ _('Leecher') }} <span class="badge">{{ result.leech }}</span>{% endif %}
 {% if result.seed is defined %}<p class="result-content">{{ icon('transfer') }} {{ _('Seeder') }} <span class="badge">{{ result.seed }}</span> &bull; {{ _('Leecher') }} <span class="badge">{{ result.leech }}</span>{% endif %}
 {% if result.filesize %}<br />{{ icon('floppy-disk') }} {{ _('Filesize') }} 
 {% if result.filesize %}<br />{{ icon('floppy-disk') }} {{ _('Filesize') }} 
@@ -19,7 +19,7 @@
 </p>
 </p>
 
 
 {% if rtl %}
 {% if rtl %}
-{{ result_footer_rtl(result) }}
+{{ result_footer_rtl(result, loop.index) }}
 {% else %}
 {% else %}
-{{ result_footer(result) }}
+{{ result_footer(result, loop.index) }}
 {% endif %}
 {% endif %}

+ 6 - 6
searx/templates/oscar/result_templates/videos.html

@@ -1,10 +1,10 @@
 {% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon %}
 {% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon %}
 
 
-{{ result_header(result, favicons) }}
+{{ result_header(result, favicons, loop.index) }}
-{{ result_sub_header(result) }}
+{{ result_sub_header(result, loop.index) }}
 
 
 {% if result.embedded %}
 {% if result.embedded %}
-    <small> &bull; <a class="text-info btn-collapse collapsed cursor-pointer media-loader disabled_if_nojs" data-toggle="collapse" data-target="#result-video-{{ index }}" data-btn-text-collapsed="{{ _('show video') }}" data-btn-text-not-collapsed="{{ _('hide video') }}">{{ icon('film') }} {{ _('show video') }}</a></small>
+    <small> &bull; <a class="text-info btn-collapse collapsed cursor-pointer media-loader disabled_if_nojs" data-toggle="collapse" data-target="#result-video-{{ index }}" data-btn-text-collapsed="{{ _('show video') }}" data-btn-text-not-collapsed="{{ _('hide video') }}" aria-labelledby="result-{{loop.index}}">{{ icon('film') }} {{ _('show video') }}</a></small>
 {% endif %}
 {% endif %}
 
 
 {% if result.embedded %}
 {% if result.embedded %}
@@ -15,7 +15,7 @@
 
 
 <div class="container-fluid">
 <div class="container-fluid">
     <div class="row">
     <div class="row">
-        <a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}><img class="thumbnail col-xs-6 col-sm-4 col-md-4 result-content" src="{{ image_proxify(result.thumbnail) }}" alt="{{ result.title|striptags }} {{ result.engine }}" /></a>
+        <a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}><img class="thumbnail col-xs-6 col-sm-4 col-md-4 result-content" src="{{ image_proxify(result.thumbnail) }}" aria-labelledby="result-{{loop.index}}" /></a>
         {% if result.author %}<p class="col-xs-12 col-sm-8 col-md-8 result-content"><b>{{ _('Author') }}</b>: {{ result.author }}</p>{% endif %}
         {% if result.author %}<p class="col-xs-12 col-sm-8 col-md-8 result-content"><b>{{ _('Author') }}</b>: {{ result.author }}</p>{% endif %}
         {% if result.length %}<p class="col-xs-12 col-sm-8 col-md-8 result-content"><b>{{ _('Length') }}</b>: {{ result.length }}</p>{% endif %}
         {% if result.length %}<p class="col-xs-12 col-sm-8 col-md-8 result-content"><b>{{ _('Length') }}</b>: {{ result.length }}</p>{% endif %}
         {% if result.content %}<p class="col-xs-12 col-sm-8 col-md-8 result-content">{{ result.content|safe }}</p>{% endif %}
         {% if result.content %}<p class="col-xs-12 col-sm-8 col-md-8 result-content">{{ result.content|safe }}</p>{% endif %}
@@ -23,7 +23,7 @@
 </div>
 </div>
 
 
 {% if rtl %}
 {% if rtl %}
-{{ result_footer_rtl(result) }}
+{{ result_footer_rtl(result, loop.index) }}
 {% else %}
 {% else %}
-{{ result_footer(result) }}
+{{ result_footer(result, loop.index) }}
 {% endif %}
 {% endif %}

+ 1 - 0
searx/templates/oscar/time-range.html

@@ -1,3 +1,4 @@
+<label class="visually-hidden" for="time-range">{{ _('Time range') }}</label>
 <select name="time_range" id="time-range" class="custom-select form-control" accesskey="t">{{- "" -}}
 <select name="time_range" id="time-range" class="custom-select form-control" accesskey="t">{{- "" -}}
     <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') -}}

+ 9 - 2
searx/webapp.py

@@ -335,8 +335,15 @@ def image_proxify(url):
     if not request.preferences.get_value('image_proxy'):
     if not request.preferences.get_value('image_proxy'):
         return url
         return url
 
 
-    if url.startswith('data:image/jpeg;base64,'):
+    if url.startswith('data:image/'):
-        return url
+        # 50 is an arbitrary number to get only the beginning of the image.
+        partial_base64 = url[len('data:image/'):50].split(';')
+        if len(partial_base64) == 2 \
+           and partial_base64[0] in ['gif', 'png', 'jpeg', 'pjpeg', 'webp', 'tiff', 'bmp']\
+           and partial_base64[1].startswith('base64,'):
+            return url
+        else:
+            return None
 
 
     if settings.get('result_proxy'):
     if settings.get('result_proxy'):
         return proxify(url)
         return proxify(url)

+ 2 - 2
utils/fetch_languages.py

@@ -28,10 +28,10 @@ def fetch_supported_languages():
     names.sort()
     names.sort()
 
 
     for engine_name in names:
     for engine_name in names:
-        print("fetching languages of engine %s" % engine_name)
-
         if hasattr(engines[engine_name], 'fetch_supported_languages'):
         if hasattr(engines[engine_name], 'fetch_supported_languages'):
             engines_languages[engine_name] = engines[engine_name].fetch_supported_languages()
             engines_languages[engine_name] = engines[engine_name].fetch_supported_languages()
+            print("fetched %s languages from engine %s" % (
+                len(engines_languages[engine_name]), engine_name))
             if type(engines_languages[engine_name]) == list:
             if type(engines_languages[engine_name]) == list:
                 engines_languages[engine_name] = sorted(engines_languages[engine_name])
                 engines_languages[engine_name] = sorted(engines_languages[engine_name])
 
 

+ 42 - 28
utils/searx.sh

@@ -816,47 +816,61 @@ rst-doc() {
 
 
             case $DIST_ID-$DIST_VERS in
             case $DIST_ID-$DIST_VERS in
                 ubuntu-*|debian-*)  cat <<EOF
                 ubuntu-*|debian-*)  cat <<EOF
-# init.d --> /usr/share/doc/uwsgi/README.Debian.gz
+
-# For uWSGI debian uses the LSB init process, this might be changed
+.. code:: bash
-# one day, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833067
+
-
+   # init.d --> /usr/share/doc/uwsgi/README.Debian.gz
-create     ${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP}
+   # For uWSGI debian uses the LSB init process, this might be changed
-enable:    sudo -H ln -s ${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP} ${uWSGI_APPS_ENABLED}/
+   # one day, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833067
-start:     sudo -H service uwsgi start   ${SEARX_UWSGI_APP%.*}
+
-restart:   sudo -H service uwsgi restart ${SEARX_UWSGI_APP%.*}
+   create     ${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP}
-stop:      sudo -H service uwsgi stop    ${SEARX_UWSGI_APP%.*}
+   enable:    sudo -H ln -s ${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP} ${uWSGI_APPS_ENABLED}/
-disable:   sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+   start:     sudo -H service uwsgi start   ${SEARX_UWSGI_APP%.*}
+   restart:   sudo -H service uwsgi restart ${SEARX_UWSGI_APP%.*}
+   stop:      sudo -H service uwsgi stop    ${SEARX_UWSGI_APP%.*}
+   disable:   sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+
 EOF
 EOF
                 ;;
                 ;;
                 arch-*) cat <<EOF
                 arch-*) cat <<EOF
-# systemd --> /usr/lib/systemd/system/uwsgi@.service
+
-# For uWSGI archlinux uses systemd template units, see
+.. code:: bash
-# - http://0pointer.de/blog/projects/instances.html
+
-# - https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
+   # systemd --> /usr/lib/systemd/system/uwsgi@.service
-
+   # For uWSGI archlinux uses systemd template units, see
-create:    ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+   # - http://0pointer.de/blog/projects/instances.html
-enable:    sudo -H systemctl enable   uwsgi@${SEARX_UWSGI_APP%.*}
+   # - https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
-start:     sudo -H systemctl start    uwsgi@${SEARX_UWSGI_APP%.*}
+
-restart:   sudo -H systemctl restart  uwsgi@${SEARX_UWSGI_APP%.*}
+   create:    ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
-stop:      sudo -H systemctl stop     uwsgi@${SEARX_UWSGI_APP%.*}
+   enable:    sudo -H systemctl enable   uwsgi@${SEARX_UWSGI_APP%.*}
-disable:   sudo -H systemctl disable  uwsgi@${SEARX_UWSGI_APP%.*}
+   start:     sudo -H systemctl start    uwsgi@${SEARX_UWSGI_APP%.*}
+   restart:   sudo -H systemctl restart  uwsgi@${SEARX_UWSGI_APP%.*}
+   stop:      sudo -H systemctl stop     uwsgi@${SEARX_UWSGI_APP%.*}
+   disable:   sudo -H systemctl disable  uwsgi@${SEARX_UWSGI_APP%.*}
+
 EOF
 EOF
                 ;;
                 ;;
                 fedora-*) cat <<EOF
                 fedora-*) cat <<EOF
-# systemd --> /usr/lib/systemd/system/uwsgi.service
-# The unit file starts uWSGI in emperor mode (/etc/uwsgi.ini), see
-# - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
 
 
-create:    ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+.. code:: bash
-restart:   sudo -H touch ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+
-disable:   sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+   # systemd --> /usr/lib/systemd/system/uwsgi.service
+   # The unit file starts uWSGI in emperor mode (/etc/uwsgi.ini), see
+   # - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
+
+   create:    ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+   restart:   sudo -H touch ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+   disable:   sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+
 EOF
 EOF
                 ;;
                 ;;
             esac
             esac
             echo -e ".. END searx uwsgi-description $DIST_NAME"
             echo -e ".. END searx uwsgi-description $DIST_NAME"
 
 
             echo -e "\n.. START searx uwsgi-appini $DIST_NAME"
             echo -e "\n.. START searx uwsgi-appini $DIST_NAME"
-            eval "echo \"$(< "${TEMPLATES}/${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP}")\""
+            echo ".. code:: bash"
+            echo
+            eval "echo \"$(< "${TEMPLATES}/${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP}")\"" | prefix_stdout "  "
             echo -e "\n.. END searx uwsgi-appini $DIST_NAME"
             echo -e "\n.. END searx uwsgi-appini $DIST_NAME"
 
 
         )
         )

Some files were not shown because too many files changed in this diff