Browse Source

add multi theming support

Matej Cotman 11 years ago
parent
commit
08eaffe245
41 changed files with 103 additions and 28 deletions
  1. 2 2
      Makefile
  2. 2 0
      searx/settings.yml
  3. 0 0
      searx/static/default/css/style.css
  4. 0 0
      searx/static/default/img/favicon.png
  5. 0 0
      searx/static/default/img/github_ribbon.png
  6. 0 0
      searx/static/default/img/icon_github.ico
  7. 0 0
      searx/static/default/img/icon_soundcloud.ico
  8. 0 0
      searx/static/default/img/icon_stackoverflow.ico
  9. 0 0
      searx/static/default/img/icon_twitter.ico
  10. 0 0
      searx/static/default/img/icon_vimeo.ico
  11. 0 0
      searx/static/default/img/icon_wikipedia.ico
  12. 0 0
      searx/static/default/img/icon_youtube.ico
  13. 0 0
      searx/static/default/img/preference-icon.png
  14. 0 0
      searx/static/default/img/search-icon.png
  15. 0 0
      searx/static/default/img/searx.png
  16. 0 0
      searx/static/default/img/searx_logo.svg
  17. 0 0
      searx/static/default/js/mootools-autocompleter-1.1.2-min.js
  18. 0 0
      searx/static/default/js/mootools-core-1.4.5-min.js
  19. 0 0
      searx/static/default/js/searx.js
  20. 0 0
      searx/static/default/less/autocompleter.less
  21. 0 0
      searx/static/default/less/definitions.less
  22. 0 0
      searx/static/default/less/mixins.less
  23. 0 0
      searx/static/default/less/search.less
  24. 0 0
      searx/static/default/less/style.less
  25. 2 2
      searx/templates/default/about.html
  26. 0 0
      searx/templates/default/base.html
  27. 0 0
      searx/templates/default/categories.html
  28. 0 0
      searx/templates/default/github_ribbon.html
  29. 2 2
      searx/templates/default/index.html
  30. 0 0
      searx/templates/default/opensearch.xml
  31. 0 0
      searx/templates/default/opensearch_response_rss.xml
  32. 12 2
      searx/templates/default/preferences.html
  33. 1 1
      searx/templates/default/result_templates/default.html
  34. 0 0
      searx/templates/default/result_templates/images.html
  35. 0 0
      searx/templates/default/result_templates/torrent.html
  36. 1 1
      searx/templates/default/result_templates/videos.html
  37. 4 4
      searx/templates/default/results.html
  38. 1 1
      searx/templates/default/search.html
  39. 1 1
      searx/templates/default/stats.html
  40. 19 3
      searx/utils.py
  41. 56 9
      searx/webapp.py

+ 2 - 2
Makefile

@@ -44,13 +44,13 @@ minimal: bin/buildout minimal.cfg setup.py
 	bin/buildout -c minimal.cfg $(options)
 
 styles:
-	@lessc -x searx/static/less/style.less > searx/static/css/style.css
+	@lessc -x searx/static/default/less/style.less > searx/static/default/css/style.css
 
 locales:
 	@pybabel compile -d searx/translations
 
 clean:
 	@rm -rf .installed.cfg .mr.developer.cfg bin parts develop-eggs \
-		searx.egg-info lib include .coverage coverage searx/static/css/*.css
+		searx.egg-info lib include .coverage coverage searx/static/default/css/*.css
 
 .PHONY: all tests robot flake8 coverage production minimal styles locales clean

+ 2 - 0
searx/settings.yml

@@ -4,6 +4,8 @@ server:
     debug : True
     request_timeout : 2.0 # seconds
     base_url : False
+    themes_path : ""
+    default_theme : default
 
 engines:
   - name : wikipedia

+ 0 - 0
searx/static/css/style.css → searx/static/default/css/style.css


+ 0 - 0
searx/static/img/favicon.png → searx/static/default/img/favicon.png


+ 0 - 0
searx/static/img/github_ribbon.png → searx/static/default/img/github_ribbon.png


+ 0 - 0
searx/static/img/icon_github.ico → searx/static/default/img/icon_github.ico


+ 0 - 0
searx/static/img/icon_soundcloud.ico → searx/static/default/img/icon_soundcloud.ico


+ 0 - 0
searx/static/img/icon_stackoverflow.ico → searx/static/default/img/icon_stackoverflow.ico


+ 0 - 0
searx/static/img/icon_twitter.ico → searx/static/default/img/icon_twitter.ico


+ 0 - 0
searx/static/img/icon_vimeo.ico → searx/static/default/img/icon_vimeo.ico


+ 0 - 0
searx/static/img/icon_wikipedia.ico → searx/static/default/img/icon_wikipedia.ico


+ 0 - 0
searx/static/img/icon_youtube.ico → searx/static/default/img/icon_youtube.ico


+ 0 - 0
searx/static/img/preference-icon.png → searx/static/default/img/preference-icon.png


+ 0 - 0
searx/static/img/search-icon.png → searx/static/default/img/search-icon.png


+ 0 - 0
searx/static/img/searx.png → searx/static/default/img/searx.png


+ 0 - 0
searx/static/img/searx_logo.svg → searx/static/default/img/searx_logo.svg


+ 0 - 0
searx/static/js/mootools-autocompleter-1.1.2-min.js → searx/static/default/js/mootools-autocompleter-1.1.2-min.js


+ 0 - 0
searx/static/js/mootools-core-1.4.5-min.js → searx/static/default/js/mootools-core-1.4.5-min.js


+ 0 - 0
searx/static/js/searx.js → searx/static/default/js/searx.js


+ 0 - 0
searx/static/less/autocompleter.less → searx/static/default/less/autocompleter.less


+ 0 - 0
searx/static/less/definitions.less → searx/static/default/less/definitions.less


+ 0 - 0
searx/static/less/mixins.less → searx/static/default/less/mixins.less


+ 0 - 0
searx/static/less/search.less → searx/static/default/less/search.less


+ 0 - 0
searx/static/less/style.less → searx/static/default/less/style.less


+ 2 - 2
searx/templates/about.html → searx/templates/default/about.html

@@ -1,6 +1,6 @@
-{% extends 'base.html' %}
+{% extends 'default/base.html' %}
 {% block content %}
-{% include 'github_ribbon.html' %}
+{% include 'default/github_ribbon.html' %}
 <div class="row">
     <h1>About <a href="{{ url_for('index') }}">searx</a></h1>
 

+ 0 - 0
searx/templates/base.html → searx/templates/default/base.html


+ 0 - 0
searx/templates/categories.html → searx/templates/default/categories.html


+ 0 - 0
searx/templates/github_ribbon.html → searx/templates/default/github_ribbon.html


+ 2 - 2
searx/templates/index.html → searx/templates/default/index.html

@@ -1,8 +1,8 @@
-{% extends "base.html" %}
+{% extends "default/base.html" %}
 {% block content %}
 <div class="center">
     <div class="title"><h1>searx</h1></div>
-    {% include 'search.html' %}
+    {% include 'default/search.html' %}
     <p class="top_margin">
         <a href="{{ url_for('about') }}" class="hmarg">{{ _('about') }}</a>
         <a href="{{ url_for('preferences') }}" class="hmarg">{{ _('preferences') }}</a>

+ 0 - 0
searx/templates/opensearch.xml → searx/templates/default/opensearch.xml


+ 0 - 0
searx/templates/opensearch_response_rss.xml → searx/templates/default/opensearch_response_rss.xml


+ 12 - 2
searx/templates/preferences.html → searx/templates/default/preferences.html

@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "default/base.html" %}
 {% block head %} {% endblock %}
 {% block content %}
 <div class="row">
@@ -8,7 +8,7 @@
     <fieldset>
         <legend>{{ _('Default categories') }}</legend>
         <p>
-        {% include 'categories.html' %}
+        {% include 'default/categories.html' %}
         </p>
     </fieldset>
     <fieldset>
@@ -52,6 +52,16 @@
         </select>
         </p>
     </fieldset>
+    <fieldset>
+        <legend>{{ _('Themes') }}</legend>
+        <p>
+        <select name="theme">
+            {% for name in themes %}
+            <option value="{{ name }}" {% if name == theme %}selected="selected"{% endif %}>{{ name }}</option>
+            {% endfor %}
+        </select>
+        </p>
+    </fieldset>
     <fieldset>
     <legend>{{ _('Currently used search engines') }}</legend>
 

+ 1 - 1
searx/templates/result_templates/default.html → searx/templates/default/result_templates/default.html

@@ -1,7 +1,7 @@
 <div class="result {{ result.class }}">
 
   {% if result['favicon'] %}
-    <img width="14" height="14" class="favicon" src="static/img/icon_{{result['favicon']}}.ico" />
+    <img width="14" height="14" class="favicon" src="static/{{theme}}/img/icon_{{result['favicon']}}.ico" />
   {% endif %}
 
   <div>

+ 0 - 0
searx/templates/result_templates/images.html → searx/templates/default/result_templates/images.html


+ 0 - 0
searx/templates/result_templates/torrent.html → searx/templates/default/result_templates/torrent.html


+ 1 - 1
searx/templates/result_templates/videos.html → searx/templates/default/result_templates/videos.html

@@ -1,6 +1,6 @@
 <div class="result">
   {% if result['favicon'] %}
-    <img width="14" height="14" class="favicon" src="static/img/icon_{{result['favicon']}}.ico" />
+    <img width="14" height="14" class="favicon" src="static/{{theme}}/img/icon_{{result['favicon']}}.ico" />
   {% endif %}
 
     <p>

+ 4 - 4
searx/templates/results.html → searx/templates/default/results.html

@@ -1,9 +1,9 @@
-{% extends "base.html" %}
+{% extends "default/base.html" %}
 {% block title %}{{ q }} - {% endblock %}
 {% block content %}
 <div class="right"><a href="{{ url_for('preferences') }}" id="preferences"><span>preferences</span></a></div>
 <div class="small search center">
-    {% include 'search.html' %}
+    {% include 'default/search.html' %}
 </div>
 <div id="results">
     <div id="sidebar">
@@ -43,9 +43,9 @@
 
     {% for result in results %}
         {% if result['template'] %}
-            {% include 'result_templates/'+result['template'] %}
+            {% include 'default/result_templates/'+result['template'] %}
         {% else %}
-            {% include 'result_templates/default.html' %}
+            {% include 'default/result_templates/default.html' %}
         {% endif %}
     {% endfor %}
 

+ 1 - 1
searx/templates/search.html → searx/templates/default/search.html

@@ -3,5 +3,5 @@
     <input type="text" placeholder="{{ _('Search for...') }}" id="q" class="q" name="q" tabindex="1" autocomplete="off" {% if q %}value="{{ q }}"{% endif %}/>
     <input type="submit" value="search" id="search_submit" />
   </div>
-  {% include 'categories.html' %}
+  {% include 'default/categories.html' %}
 </form>

+ 1 - 1
searx/templates/stats.html → searx/templates/default/stats.html

@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "default/base.html" %}
 {% block head %} {% endblock %}
 {% block content %}
 <h2>{{ _('Engine stats') }}</h2>

+ 19 - 3
searx/utils.py

@@ -1,10 +1,12 @@
-from HTMLParser import HTMLParser
 #import htmlentitydefs
-import csv
 from codecs import getincrementalencoder
+from HTMLParser import HTMLParser
+from random import choice
+
 import cStringIO
+import csv
+import os
 import re
-from random import choice
 
 ua_versions = ('26.0', '27.0', '28.0')
 ua_os = ('Windows NT 6.3; WOW64',
@@ -110,3 +112,17 @@ class UnicodeWriter:
     def writerows(self, rows):
         for row in rows:
             self.writerow(row)
+
+
+def get_themes(root):
+    """Returns available themes list."""
+
+    static_path = os.path.join(root, 'static')
+    static_names = set(os.listdir(static_path))
+    templates_path = os.path.join(root, 'templates')
+    templates_names = set(os.listdir(templates_path))
+
+    themes = []
+    for name in static_names.intersection(templates_names):
+        themes += [name]
+    return static_path, templates_path, themes

+ 56 - 9
searx/webapp.py

@@ -38,16 +38,23 @@ from searx.engines import (
     search as do_search, categories, engines, get_engines_stats,
     engine_shortcuts
 )
-from searx.utils import UnicodeWriter, highlight_content, html_to_text
+from searx.utils import (
+    UnicodeWriter, highlight_content, html_to_text, get_themes
+)
 from searx.languages import language_codes
 from searx.search import Search
 from searx.autocomplete import backends as autocomplete_backends
 
 
+static_path, templates_path, themes = get_themes(settings['themes_path'] if \
+    settings.get('themes_path', None) else searx_dir)
+default_theme = settings['default_theme'] if \
+    settings.get('default_theme', None) else 'default'
+
 app = Flask(
     __name__,
-    static_folder=os.path.join(searx_dir, 'static'),
-    template_folder=os.path.join(searx_dir, 'templates')
+    static_folder=static_path,
+    template_folder=templates_path
 )
 
 app.secret_key = settings['server']['secret_key']
@@ -90,7 +97,30 @@ def get_base_url():
     return hostname
 
 
-def render(template_name, **kwargs):
+def get_current_theme_name(override=None):
+    """Returns theme name.
+
+    Checks in this order:
+    1. override
+    2. cookies
+    3. settings"""
+
+    if override and override in themes:
+        return override
+    theme_name = request.cookies.get('theme', default_theme)
+    if theme_name not in themes:
+        theme_name = default_theme
+    return theme_name
+
+
+def url_for_theme(endpoint, override_theme=None, **values):
+    if endpoint == 'static' and values.get('filename', None):
+        theme_name = get_current_theme_name(override=override_theme)
+        values['filename'] = "{}/{}".format(theme_name, values['filename'])
+    return url_for(endpoint, **values)
+
+
+def render(template_name, override_theme=None, **kwargs):
     blocked_engines = request.cookies.get('blocked_engines', '').split(',')
 
     autocomplete = request.cookies.get('autocomplete')
@@ -125,7 +155,13 @@ def render(template_name, **kwargs):
 
     kwargs['method'] = request.cookies.get('method', 'POST')
 
-    return render_template(template_name, **kwargs)
+    # override url_for function in templates
+    kwargs['url_for'] = url_for_theme
+
+    kwargs['theme'] = get_current_theme_name(override=override_theme)
+
+    return render_template(
+        '{}/{}'.format(kwargs['theme'], template_name), **kwargs)
 
 
 @app.route('/search', methods=['GET', 'POST'])
@@ -232,7 +268,8 @@ def index():
         paging=search.paging,
         pageno=search.pageno,
         base_url=get_base_url(),
-        suggestions=search.suggestions
+        suggestions=search.suggestions,
+        theme=get_current_theme_name()
     )
 
 
@@ -290,7 +327,7 @@ def preferences():
 
     if request.method == 'GET':
         blocked_engines = request.cookies.get('blocked_engines', '').split(',')
-    else:
+    else:  # on save
         selected_categories = []
         locale = None
         autocomplete = ''
@@ -315,6 +352,8 @@ def preferences():
                 engine_name = pd_name.replace('engine_', '', 1)
                 if engine_name in engines:
                     blocked_engines.append(engine_name)
+            elif pd_name == 'theme':
+                theme = pd if pd in themes else default_theme
 
         resp = make_response(redirect(url_for('index')))
 
@@ -352,6 +391,9 @@ def preferences():
 
         resp.set_cookie('method', method, max_age=cookie_max_age)
 
+        resp.set_cookie(
+            'theme', theme, max_age=cookie_max_age)
+
         return resp
     return render('preferences.html',
                   locales=settings['locales'],
@@ -361,7 +403,9 @@ def preferences():
                   categs=categories.items(),
                   blocked_engines=blocked_engines,
                   autocomplete_backends=autocomplete_backends,
-                  shortcuts={y: x for x, y in engine_shortcuts.items()})
+                  shortcuts={y: x for x, y in engine_shortcuts.items()},
+                  themes=themes,
+                  theme=get_current_theme_name())
 
 
 @app.route('/stats', methods=['GET'])
@@ -404,7 +448,10 @@ def opensearch():
 
 @app.route('/favicon.ico')
 def favicon():
-    return send_from_directory(os.path.join(app.root_path, 'static/img'),
+    return send_from_directory(os.path.join(app.root_path,
+                                            'static',
+                                            get_current_theme_name(),
+                                            'img'),
                                'favicon.png',
                                mimetype='image/vnd.microsoft.icon')