Browse Source

Routing and Templating have been added

Roland Haroutiounian 9 years ago
parent
commit
e16bd85072

+ 32
- 0
lodel/interface/web/controllers.py View File

1
+# -*- coding: utf-8 -*-
2
+from werkzeug.wrappers import Response
3
+from lodel.template.loader import TemplateLoader
4
+
5
+# This module contains the web UI controllers that will be called from the web ui class
6
+
7
+def admin(request):
8
+    loader = TemplateLoader()
9
+    response = Response(loader.render_to_response('templates/admin/admin.html'), mimetype='text/html')
10
+    response.status_code = 200
11
+    return response
12
+
13
+
14
+def index(request):
15
+    loader = TemplateLoader()
16
+    response = Response(loader.render_to_response('templates/index/index.html'), mimetype='text/html')
17
+    response.status_code = 200
18
+    return response
19
+
20
+
21
+def not_found(request):
22
+    loader = TemplateLoader()
23
+    response = Response(loader.render_to_response('templates/errors/404.html'), mimetype='text/html')
24
+    response.status_code = 404
25
+    return response
26
+
27
+
28
+def test(request):
29
+    loader = TemplateLoader()
30
+    response = Response(loader.render_to_response('templates/test.html'), mimetype='text/html')
31
+    response.status_code = 200
32
+    return response

+ 12
- 0
lodel/interface/web/lodelrequest.py View File

1
+from werkzeug.wrappers import Request
2
+from werkzeug.urls import url_decode
3
+
4
+
5
+class LodelRequest(Request):
6
+
7
+    def __init__(self, environ):
8
+        super().__init__(environ)
9
+        self.PATH = self.path.lstrip('/')
10
+        self.FILES = self.files.to_dict(flat=False)
11
+        self.GET = url_decode(self.query_string).to_dict(flat=False)
12
+        self.POST = self.form.to_dict(flat=False)

+ 20
- 0
lodel/interface/web/router.py View File

1
+# -*- coding: utf-8 -*-
2
+import re
3
+
4
+from lodel.interface.web.controllers import *
5
+import lodel.interface.web.urls as main_urls
6
+
7
+
8
+def get_controller(request):
9
+    url_rules = []
10
+    for url in main_urls.urls:
11
+        url_rules.append((url[0], url[1]))
12
+
13
+    # Returning the right controller to call
14
+    for regex, callback in url_rules:
15
+        match = re.search(regex, request.PATH)
16
+        if match is not None:
17
+            request.url_args = match.groups()
18
+            return callback
19
+
20
+    return not_found

+ 65
- 6
lodel/interface/web/run.py View File

1
-from werkzeug.wrappers import Response
1
+# -*- coding: utf-8 -*-
2
+import os
3
+import datetime
4
+from werkzeug.contrib.sessions import FilesystemSessionStore
2
 
5
 
3
-def application(environ, start_response):
4
-    request = Request(environ)
5
-    text = 'Hello %s!' % request.args.get('name', 'World')
6
-    response = Response(text, mimetype='text/plain')
7
-    return response(environ, start_response)
6
+from lodel.interface.web.router import get_controller
7
+from lodel.interface.web.lodelrequest import LodelRequest
8
+
9
+# TODO Déplacer ces trois paramètres dans les settings
10
+SESSION_FILES_TEMPLATE = 'lodel_%s.sess'
11
+SESSION_FILES_BASE_DIR = 'tmp/sessions'
12
+SESSION_EXPIRATION_LIMIT = 900 # 15 min
13
+
14
+session_store = FilesystemSessionStore(path=SESSION_FILES_BASE_DIR, filename_template=SESSION_FILES_TEMPLATE)
15
+
16
+# TODO Déplacer cette méthode dans un module Lodel/utils/datetime.py
17
+def get_utc_timestamp():
18
+    d = datetime.datetime.utcnow()
19
+    epoch = datetime.datetime(1970, 1, 1)
20
+    t = (d - epoch).total_seconds()
21
+    return t
22
+
23
+# TODO déplacer dans un module "sessions.py"
24
+def delete_old_session_files(timestamp_now):
25
+    session_files_path = os.path.abspath(session_store.path)
26
+    session_files = [file_object for file_object in os.listdir(session_files_path)
27
+                     if os.path.isfile(os.path.join(session_files_path, file_object))]
28
+    for session_file in session_files:
29
+        expiration_timestamp = os.statos.path.join(session_files_path, session_file)).st_mtime + \
30
+                                                                                      SESSION_EXPIRATION_LIMIT
31
+        if timestamp_now > expiration_timestamp:
32
+            os.unlink(os.path.join(session_files_path, session_file))
33
+
34
+
35
+# TODO Déplacer dans une module "sessions.py"
36
+def is_session_file_expired(timestamp_now, sid):
37
+    session_file = session_store.get_session_filename(sid)
38
+    expiration_timestamp = os.stat(session_file).st_mtime + SESSION_EXPIRATION_LIMIT
39
+    if timestamp_now < expiration_timestamp:
40
+        return False
41
+    return True
42
+
43
+# WSGI Application
44
+def application(env, start_response):
45
+    current_timestamp = get_utc_timestamp()
46
+    delete_old_session_files(current_timestamp)
47
+    request = LodelRequest(env)
48
+    sid = request.cookies.get('sid')
49
+    if sid is None or sid not in session_store.list():
50
+        request.session = session_store.new()
51
+        request.session['last_accessed'] = current_timestamp
52
+    else:
53
+        request.session = session_store.get(sid)
54
+        if is_session_file_expired(current_timestamp, sid):
55
+            session_store.delete(request.session)
56
+            request.session = session_store.new()
57
+            request.session['user_context'] = None
58
+        request.session['last_accessed'] = current_timestamp
59
+
60
+    controller = get_controller(request)
61
+    response = controller(request)
62
+    if request.session.should_save:
63
+        session_store.save(request.session)
64
+        response.set_cookie('sid', request.session.sid)
65
+
66
+    return response(env, start_response)

+ 9
- 0
lodel/interface/web/urls.py View File

1
+from lodel.interface.web.controllers import *
2
+
3
+urls = (
4
+    (r'^$', index),
5
+    (r'admin/?$', admin),
6
+    (r'admin/(.+)$', admin),
7
+    (r'test/(.+)$', test),
8
+    (r'test/?$', test)
9
+)

+ 0
- 0
lodel/template/__init__.py View File


+ 0
- 0
lodel/template/api/__init__.py View File


+ 3
- 0
lodel/template/api/api_lodel_templates.py View File

1
+# -*- coding: utf-8 -*-
2
+
3
+# Lodel 2 templates API : loaded by default

+ 0
- 0
lodel/template/exceptions/__init__.py View File


+ 7
- 0
lodel/template/exceptions/not_allowed_custom_api_key_error.py View File

1
+#-*- coding: utf-8 -*-
2
+
3
+
4
+class NotAllowedCustomAPIKeyError(Exception):
5
+
6
+    def __init__(self, message):
7
+        self.message = message

+ 71
- 0
lodel/template/loader.py View File

1
+# -*- coding: utf-8 -*-
2
+import jinja2
3
+import os
4
+
5
+import settings
6
+from lodel.template.api import api_lodel_templates
7
+from lodel.template.exceptions.not_allowed_custom_api_key_error import NotAllowedCustomAPIKeyError
8
+
9
+
10
+class TemplateLoader(object):
11
+
12
+    _reserved_template_keys = ['lodel']
13
+
14
+    ## @brief Initializes a template loader
15
+    #
16
+    # @param search_path str : the base path from which the templates are searched. To use absolute paths, you can set
17
+    # it to the root "/". By default, it will be the root of the project, defined in the settings of the application.
18
+    # @param follow_links bool : indicates whether or not to follow the symbolic links (default: True)
19
+    # @param is_cache_active bool : indicates whether or not the cache should be activated or not (default: True)
20
+    # @todo connect this to the new settings system
21
+    def __init__(self, search_path=settings.base_path, follow_links=True, is_cache_active=True):
22
+        self.search_path = search_path
23
+        self.follow_links = follow_links
24
+        self.is_cache_active = is_cache_active
25
+
26
+    ## @brief Renders a HTML content of a template
27
+    #
28
+    # @see template.loader.TemplateLoader.render_to_response
29
+    #
30
+    # @return str. String containing the HTML output of the processed templated
31
+    def render_to_html(self, template_file, template_vars={}, template_extra=None):
32
+        loader = jinja2.FileSystemLoader(searchpath=self.search_path, followlinks=self.follow_links)
33
+        environment = jinja2.Environment(loader=loader) if self.is_cache_active else jinja2.Environment(loader=loader,
34
+                                                                                                        cache_size=0)
35
+        template = environment.get_template(template_file)
36
+
37
+        # lodel2 default api is loaded
38
+        # TODO change this if needed
39
+        template.globals['lodel'] = api_lodel_templates
40
+
41
+        # Extra modules are loaded
42
+        if template_extra is not None:
43
+            for extra in template_extra:
44
+                if not self._is_allowed_template_key(extra[0]):
45
+                    raise NotAllowedCustomAPIKeyError("The name '%s' is a reserved one for the loaded APIs in "
46
+                                                      "templates" % extra[0])
47
+                template.globals[extra[0]] = extra[1]
48
+
49
+        return template.render(template_vars)
50
+
51
+    ## @brief Renders a template into an encoded form ready to be sent to a wsgi response
52
+    #
53
+    # @param template_file str : path to the template file (starting from the base path used to instanciate the
54
+    # TemplateLoader)
55
+    # @param template_vars dict : parameters to be used in the template
56
+    # @param template_extra list : list of tuples indicating the custom modules to import in the template
57
+    # (default: None).
58
+    #
59
+    # The modules are given as tuples with the format : ('name_to_use_in_the_template', module)
60
+    #
61
+    # @return str
62
+    def render_to_response(self, template_file, template_vars={}, template_extra=None):
63
+        return self.render_to_html(template_file=template_file, template_vars=template_vars,
64
+                                   template_extra=template_extra).encode()
65
+
66
+    ## @brief Checks if the key used for the template is allowed
67
+    #
68
+    # @param key str
69
+    # @return bool
70
+    def _is_allowed_template_key(self, key):
71
+        return False if key in self.__class__.__reserved_template_keys else True

+ 3
- 0
templates/admin/admin.html View File

1
+{% extends "templates/base_backend.html" %}
2
+{% block title %}Lodel 2 - ADMIN{% endblock %}
3
+{% block content %}ADMIN{% endblock %}

+ 15
- 0
templates/base.html View File

1
+<!doctype html>
2
+<html lang="en">
3
+<head>
4
+    <meta charset="UTF-8" />
5
+    <title>{% block title %}{% endblock %}</title>
6
+    {% block style %}{% endblock %}
7
+    {% block scripts %}{% endblock %}
8
+</head>
9
+<body>
10
+    <div id="content">
11
+        {% block content %}{% endblock %}
12
+    </div>
13
+    <script type="text/javascript">{% block javascript %}{% endblock %}</script>
14
+</body>
15
+</html>

+ 15
- 0
templates/base_backend.html View File

1
+<!doctype html>
2
+<html lang="en">
3
+<head>
4
+    <meta charset="UTF-8" />
5
+    <title>{% block title %}{% endblock %}</title>
6
+    {% block style %}{% endblock %}
7
+    {% block scripts %}{% endblock %}
8
+</head>
9
+<body>
10
+    <div id="content">
11
+        {% block content %}{% endblock %}
12
+    </div>
13
+    <script type="text/javascript">{% block javascript %}{% endblock %}</script>
14
+</body>
15
+</html>

+ 5
- 0
templates/errors/404.html View File

1
+{% extends "Lodel/templates/base.html" %}
2
+{% block title %}Error 404{% endblock %}
3
+{% block content %}
4
+    <h1>404 - File Not Found</h1>
5
+{% endblock %}

+ 3
- 0
templates/index/index.html View File

1
+{% extends "Lodel/templates/base.html" %}
2
+{% block title %}Lodel 2 - DASHBOARD{% endblock %}
3
+{% block content %}DASHBOARD{% endblock %}

+ 15
- 0
templates/test.html View File

1
+<html>
2
+<head></head>
3
+<body>
4
+    <form action="http://haroutiounian-devel.in.revues.org:9090/admin?r=1&rand[]=7&rand[]=5" method="POST" enctype="multipart/form-data">
5
+        <input type="text" name="re[]" value="3"><br />
6
+        <input type="text" name="re[]" value="1"><br />
7
+        <input type="text" name="val" value="8"><br />
8
+        <input type="file" name="myfile1"><br />
9
+        <input type="file" name="myfile2"><br />
10
+        <input type="file" name="myfiles[]"><br />
11
+        <input type="file" name="myfiles[]"><br />
12
+        <input type="submit" value="tester"><br />
13
+    </form>
14
+</body>
15
+</html>

Loading…
Cancel
Save