瀏覽代碼

Merge pull request #70 from matejc/theming_support

add multi theming support
Adam Tauber 10 年之前
父節點
當前提交
7369fbd54c
共有 41 個檔案被更改,包括 103 行新增28 行删除
  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
44 44
 	bin/buildout -c minimal.cfg $(options)
45 45
 
46 46
 styles:
47
-	@lessc -x searx/static/less/style.less > searx/static/css/style.css
47
+	@lessc -x searx/static/default/less/style.less > searx/static/default/css/style.css
48 48
 
49 49
 locales:
50 50
 	@pybabel compile -d searx/translations
51 51
 
52 52
 clean:
53 53
 	@rm -rf .installed.cfg .mr.developer.cfg bin parts develop-eggs \
54
-		searx.egg-info lib include .coverage coverage searx/static/css/*.css
54
+		searx.egg-info lib include .coverage coverage searx/static/default/css/*.css
55 55
 
56 56
 .PHONY: all tests robot flake8 coverage production minimal styles locales clean

+ 2
- 0
searx/settings.yml 查看文件

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

searx/static/css/style.css → searx/static/default/css/style.css 查看文件


searx/static/img/favicon.png → searx/static/default/img/favicon.png 查看文件


searx/static/img/github_ribbon.png → searx/static/default/img/github_ribbon.png 查看文件


searx/static/img/icon_github.ico → searx/static/default/img/icon_github.ico 查看文件


searx/static/img/icon_soundcloud.ico → searx/static/default/img/icon_soundcloud.ico 查看文件


searx/static/img/icon_stackoverflow.ico → searx/static/default/img/icon_stackoverflow.ico 查看文件


searx/static/img/icon_twitter.ico → searx/static/default/img/icon_twitter.ico 查看文件


searx/static/img/icon_vimeo.ico → searx/static/default/img/icon_vimeo.ico 查看文件


searx/static/img/icon_wikipedia.ico → searx/static/default/img/icon_wikipedia.ico 查看文件


searx/static/img/icon_youtube.ico → searx/static/default/img/icon_youtube.ico 查看文件


searx/static/img/preference-icon.png → searx/static/default/img/preference-icon.png 查看文件


searx/static/img/search-icon.png → searx/static/default/img/search-icon.png 查看文件


searx/static/img/searx.png → searx/static/default/img/searx.png 查看文件


searx/static/img/searx_logo.svg → searx/static/default/img/searx_logo.svg 查看文件


searx/static/js/mootools-autocompleter-1.1.2-min.js → searx/static/default/js/mootools-autocompleter-1.1.2-min.js 查看文件


searx/static/js/mootools-core-1.4.5-min.js → searx/static/default/js/mootools-core-1.4.5-min.js 查看文件


searx/static/js/searx.js → searx/static/default/js/searx.js 查看文件


searx/static/less/autocompleter.less → searx/static/default/less/autocompleter.less 查看文件


searx/static/less/definitions.less → searx/static/default/less/definitions.less 查看文件


searx/static/less/mixins.less → searx/static/default/less/mixins.less 查看文件


searx/static/less/search.less → searx/static/default/less/search.less 查看文件


searx/static/less/style.less → searx/static/default/less/style.less 查看文件


searx/templates/about.html → searx/templates/default/about.html 查看文件

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

searx/templates/base.html → searx/templates/default/base.html 查看文件


searx/templates/categories.html → searx/templates/default/categories.html 查看文件


searx/templates/github_ribbon.html → searx/templates/default/github_ribbon.html 查看文件


searx/templates/index.html → searx/templates/default/index.html 查看文件

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

searx/templates/opensearch.xml → searx/templates/default/opensearch.xml 查看文件


searx/templates/opensearch_response_rss.xml → searx/templates/default/opensearch_response_rss.xml 查看文件


searx/templates/preferences.html → searx/templates/default/preferences.html 查看文件

@@ -1,4 +1,4 @@
1
-{% extends "base.html" %}
1
+{% extends "default/base.html" %}
2 2
 {% block head %} {% endblock %}
3 3
 {% block content %}
4 4
 <div class="row">
@@ -8,7 +8,7 @@
8 8
     <fieldset>
9 9
         <legend>{{ _('Default categories') }}</legend>
10 10
         <p>
11
-        {% include 'categories.html' %}
11
+        {% include 'default/categories.html' %}
12 12
         </p>
13 13
     </fieldset>
14 14
     <fieldset>
@@ -53,6 +53,16 @@
53 53
         </p>
54 54
     </fieldset>
55 55
     <fieldset>
56
+        <legend>{{ _('Themes') }}</legend>
57
+        <p>
58
+        <select name="theme">
59
+            {% for name in themes %}
60
+            <option value="{{ name }}" {% if name == theme %}selected="selected"{% endif %}>{{ name }}</option>
61
+            {% endfor %}
62
+        </select>
63
+        </p>
64
+    </fieldset>
65
+    <fieldset>
56 66
     <legend>{{ _('Currently used search engines') }}</legend>
57 67
 
58 68
     <table>

searx/templates/result_templates/default.html → searx/templates/default/result_templates/default.html 查看文件

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

searx/templates/result_templates/images.html → searx/templates/default/result_templates/images.html 查看文件


searx/templates/result_templates/torrent.html → searx/templates/default/result_templates/torrent.html 查看文件


searx/templates/result_templates/videos.html → searx/templates/default/result_templates/videos.html 查看文件

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

searx/templates/results.html → searx/templates/default/results.html 查看文件

@@ -1,9 +1,9 @@
1
-{% extends "base.html" %}
1
+{% extends "default/base.html" %}
2 2
 {% block title %}{{ q }} - {% endblock %}
3 3
 {% block content %}
4 4
 <div class="right"><a href="{{ url_for('preferences') }}" id="preferences"><span>preferences</span></a></div>
5 5
 <div class="small search center">
6
-    {% include 'search.html' %}
6
+    {% include 'default/search.html' %}
7 7
 </div>
8 8
 <div id="results">
9 9
     <div id="sidebar">
@@ -43,9 +43,9 @@
43 43
 
44 44
     {% for result in results %}
45 45
         {% if result['template'] %}
46
-            {% include 'result_templates/'+result['template'] %}
46
+            {% include 'default/result_templates/'+result['template'] %}
47 47
         {% else %}
48
-            {% include 'result_templates/default.html' %}
48
+            {% include 'default/result_templates/default.html' %}
49 49
         {% endif %}
50 50
     {% endfor %}
51 51
 

searx/templates/search.html → searx/templates/default/search.html 查看文件

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

searx/templates/stats.html → searx/templates/default/stats.html 查看文件

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

+ 19
- 3
searx/utils.py 查看文件

@@ -1,10 +1,12 @@
1
-from HTMLParser import HTMLParser
2 1
 #import htmlentitydefs
3
-import csv
4 2
 from codecs import getincrementalencoder
3
+from HTMLParser import HTMLParser
4
+from random import choice
5
+
5 6
 import cStringIO
7
+import csv
8
+import os
6 9
 import re
7
-from random import choice
8 10
 
9 11
 ua_versions = ('26.0', '27.0', '28.0')
10 12
 ua_os = ('Windows NT 6.3; WOW64',
@@ -110,3 +112,17 @@ class UnicodeWriter:
110 112
     def writerows(self, rows):
111 113
         for row in rows:
112 114
             self.writerow(row)
115
+
116
+
117
+def get_themes(root):
118
+    """Returns available themes list."""
119
+
120
+    static_path = os.path.join(root, 'static')
121
+    static_names = set(os.listdir(static_path))
122
+    templates_path = os.path.join(root, 'templates')
123
+    templates_names = set(os.listdir(templates_path))
124
+
125
+    themes = []
126
+    for name in static_names.intersection(templates_names):
127
+        themes += [name]
128
+    return static_path, templates_path, themes

+ 56
- 9
searx/webapp.py 查看文件

@@ -38,16 +38,23 @@ from searx.engines import (
38 38
     search as do_search, categories, engines, get_engines_stats,
39 39
     engine_shortcuts
40 40
 )
41
-from searx.utils import UnicodeWriter, highlight_content, html_to_text
41
+from searx.utils import (
42
+    UnicodeWriter, highlight_content, html_to_text, get_themes
43
+)
42 44
 from searx.languages import language_codes
43 45
 from searx.search import Search
44 46
 from searx.autocomplete import backends as autocomplete_backends
45 47
 
46 48
 
49
+static_path, templates_path, themes = get_themes(settings['themes_path'] if \
50
+    settings.get('themes_path', None) else searx_dir)
51
+default_theme = settings['default_theme'] if \
52
+    settings.get('default_theme', None) else 'default'
53
+
47 54
 app = Flask(
48 55
     __name__,
49
-    static_folder=os.path.join(searx_dir, 'static'),
50
-    template_folder=os.path.join(searx_dir, 'templates')
56
+    static_folder=static_path,
57
+    template_folder=templates_path
51 58
 )
52 59
 
53 60
 app.secret_key = settings['server']['secret_key']
@@ -90,7 +97,30 @@ def get_base_url():
90 97
     return hostname
91 98
 
92 99
 
93
-def render(template_name, **kwargs):
100
+def get_current_theme_name(override=None):
101
+    """Returns theme name.
102
+
103
+    Checks in this order:
104
+    1. override
105
+    2. cookies
106
+    3. settings"""
107
+
108
+    if override and override in themes:
109
+        return override
110
+    theme_name = request.cookies.get('theme', default_theme)
111
+    if theme_name not in themes:
112
+        theme_name = default_theme
113
+    return theme_name
114
+
115
+
116
+def url_for_theme(endpoint, override_theme=None, **values):
117
+    if endpoint == 'static' and values.get('filename', None):
118
+        theme_name = get_current_theme_name(override=override_theme)
119
+        values['filename'] = "{}/{}".format(theme_name, values['filename'])
120
+    return url_for(endpoint, **values)
121
+
122
+
123
+def render(template_name, override_theme=None, **kwargs):
94 124
     blocked_engines = request.cookies.get('blocked_engines', '').split(',')
95 125
 
96 126
     autocomplete = request.cookies.get('autocomplete')
@@ -125,7 +155,13 @@ def render(template_name, **kwargs):
125 155
 
126 156
     kwargs['method'] = request.cookies.get('method', 'POST')
127 157
 
128
-    return render_template(template_name, **kwargs)
158
+    # override url_for function in templates
159
+    kwargs['url_for'] = url_for_theme
160
+
161
+    kwargs['theme'] = get_current_theme_name(override=override_theme)
162
+
163
+    return render_template(
164
+        '{}/{}'.format(kwargs['theme'], template_name), **kwargs)
129 165
 
130 166
 
131 167
 @app.route('/search', methods=['GET', 'POST'])
@@ -232,7 +268,8 @@ def index():
232 268
         paging=search.paging,
233 269
         pageno=search.pageno,
234 270
         base_url=get_base_url(),
235
-        suggestions=search.suggestions
271
+        suggestions=search.suggestions,
272
+        theme=get_current_theme_name()
236 273
     )
237 274
 
238 275
 
@@ -290,7 +327,7 @@ def preferences():
290 327
 
291 328
     if request.method == 'GET':
292 329
         blocked_engines = request.cookies.get('blocked_engines', '').split(',')
293
-    else:
330
+    else:  # on save
294 331
         selected_categories = []
295 332
         locale = None
296 333
         autocomplete = ''
@@ -315,6 +352,8 @@ def preferences():
315 352
                 engine_name = pd_name.replace('engine_', '', 1)
316 353
                 if engine_name in engines:
317 354
                     blocked_engines.append(engine_name)
355
+            elif pd_name == 'theme':
356
+                theme = pd if pd in themes else default_theme
318 357
 
319 358
         resp = make_response(redirect(url_for('index')))
320 359
 
@@ -352,6 +391,9 @@ def preferences():
352 391
 
353 392
         resp.set_cookie('method', method, max_age=cookie_max_age)
354 393
 
394
+        resp.set_cookie(
395
+            'theme', theme, max_age=cookie_max_age)
396
+
355 397
         return resp
356 398
     return render('preferences.html',
357 399
                   locales=settings['locales'],
@@ -361,7 +403,9 @@ def preferences():
361 403
                   categs=categories.items(),
362 404
                   blocked_engines=blocked_engines,
363 405
                   autocomplete_backends=autocomplete_backends,
364
-                  shortcuts={y: x for x, y in engine_shortcuts.items()})
406
+                  shortcuts={y: x for x, y in engine_shortcuts.items()},
407
+                  themes=themes,
408
+                  theme=get_current_theme_name())
365 409
 
366 410
 
367 411
 @app.route('/stats', methods=['GET'])
@@ -404,7 +448,10 @@ def opensearch():
404 448
 
405 449
 @app.route('/favicon.ico')
406 450
 def favicon():
407
-    return send_from_directory(os.path.join(app.root_path, 'static/img'),
451
+    return send_from_directory(os.path.join(app.root_path,
452
+                                            'static',
453
+                                            get_current_theme_name(),
454
+                                            'img'),
408 455
                                'favicon.png',
409 456
                                mimetype='image/vnd.microsoft.icon')
410 457