123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- import bleach
- import bottle
- import commonmark
- import dateutil.parser
- import functools
- import importlib
- import json
- import os
- import re
- import urllib.parse
- from string import Template
- from urllib.parse import urlparse
- # This is used to export the bottle object for the WSGI server
- application = bottle.app()
- # Load settings for this app
- application.config.load_config('settings.ini')
- # Set a "settings" variable to be used from other modules without having to
- # import the "application" object
- settings = application.config
- # "bleach" module is used to sanitize the HTML output during CommmonMark->HTML
- # conversion. The library has only a very restrictive list of white-listed
- # tags, so we add some more here.
- bleach.sanitizer.ALLOWED_TAGS += [
- 'br', 'colgroup', 'col', 'div', 'dd', 'dl', 'dt', 'figure', 'figcaption',
- 'h1', 'h2', 'h3', 'hr', 'img', 'link', 'object', 'p', 'pre', 'source', 'span',
- 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'var', 'video' ]
- bleach.sanitizer.ALLOWED_ATTRIBUTES.update ({
- 'a': [ 'class', 'href' ],
- 'div': [ 'class' ],
- 'img': [ 'alt', 'class', 'src', 'title' ],
- 'link': [ 'href', 'rel' ],
- 'object': [ 'data', 'type', 'typemustmatch' ],
- 'source': [ 'src' ],
- 'video': [ 'controls', 'height', 'width' ]
- })
- from dokk import graph, user
- import dokk.sparql as sparql
- def url(name, *anons, **parameters):
- """
- Return a URL string corresponding to a Bottle route, given its arguments.
- We use this function because it's shorter than calling application.router.build
- everywhere, especially in templates.
- """
-
- # Escape all hashes symbols (#) because they are not passed on by the browser
- for parameter in parameters.keys():
- if isinstance(parameters[parameter], str):
- parameters[parameter] = parameters[parameter].replace('#', '%23')
-
- return application.router.build(name, *anons, **parameters)
- def clear_url(url):
- """
- Return only "host:port/path" of the given URL.
- This is mostly useful to display user-friendly URL names
- to the users.
- """
-
- url = urlparse(url)
-
- if url.hostname == None:
- return ''
-
- clean_url = url.hostname
-
- if url.port != None:
- clean_url += ':' + url.port
-
- # Do not write if it's a single /
- if url.path != '/':
- clean_url += url.path
-
- return clean_url
- def dump(variable, indent=None):
- """
- Dump a variable as JSON.
- Mostly useful for debugging.
- """
-
- return json.dumps(variable, indent=indent)
- def query(id, **parameters):
- """
- A function for templates for returning a query results.
-
- :param id: ID of the Query to execute.
- :param parameters: List of parameters for the query.
- """
-
- # Retrieve the Query
- query_string = graph.get_query(id)['content']
-
- # Substitute parameters
- query_string = Template(query_string).substitute(**parameters)
-
- try:
- return sparql.query_public(query_string)
- except Exception as e:
- return None
- def read_archive(filename):
- """
- Read a file from the archive and return its content.
- """
-
- filepath = settings['dokk.archive'] + '/' + filename
-
- if not os.path.isfile(filepath):
- return None
-
- with open(filepath, mode='r') as f:
- return f.read()
- # template() functions for rendering routes
- template = functools.partial(
- bottle.jinja2_template,
- template_lookup = [ '/srv/dokk/dokk/templates' ],
- template_settings = {
- 'filters': {
- 'clear_url': clear_url,
- 'commonmark2html': lambda text: bleach.clean(bleach.linkify(commonmark.commonmark(text or ''))),
- 'simple_date': lambda date: dateutil.parser.parse(date).strftime('%H:%M, %d %b %Y')
- },
- 'globals': {
- # Variables
- # ...
-
- # Functions
- 'dump': dump,
- 'query': query,
- 'url': url,
- 'user': lambda: user.get_from_session(),
- 'signedin': lambda: user.is_signedin()
- },
- 'autoescape': True
- })
- class NodeTemplate(bottle.Jinja2Template):
- """
- This is an extension of Bottle's Jinja2Template class, that is used to
- load templates. What we're doing here is override the loader() function
- to load templates from the database instead of the filesystem.
- This class is used only for loading nodes' templates.
-
- See: https://github.com/bottlepy/bottle/blob/a454029f6e8a087e5cb570eb6ee36c2087d26e4d/bottle.py#L3914
- https://jinja.pocoo.org/docs/2.10/api/#jinja2.FunctionLoader
- """
-
- def loader(self, template_id):
- tpl = graph.get_template(template_id)
-
- return tpl['content'] if 'content' in tpl else ''
- # This is used for rendering articles templates. We keep this as a separate
- # function in order to avoid mixing articles templates with app templates.
- article_template = functools.partial(
- bottle.template,
- template_adapter = NodeTemplate,
- template_lookup = [],
- template_settings = {
- 'filters': {
- 'clear_url': clear_url,
- 'simple_date': lambda date: dateutil.parser.parse(date).strftime('%H:%M, %d %b %Y'),
- 'template': lambda string, **parameters: article_template(string+'\n', **parameters)
- },
- 'globals': {
- # deprecated
- 'archive': read_archive,
- 'blob': read_archive,
- 'dump': dump,
- 'query': query,
- },
- 'autoescape': False
- })
- def page_type_filter(config):
- """
- A type filter used for routes.
- Matches valid page types for the editor.
-
- Example. @get('/dokk/<type:page>')
- """
-
- # article Free-text input for a node. Used for describing a node.
- # file Structured data for describing a file.
- # query A query template. Queries can be used inside articles.
- # template A text template. Templates can be used inside articles.
- # topic Structured data for a node. Complements "article".
- regexp = '(article|file|query|template|topic)'
-
- def to_python(match):
- return match
- def to_url(page):
- return page
- return regexp, to_python, to_url
- # Bind the page-type filter
- application.router.add_filter('page', page_type_filter)
- # Load application routes
- import dokk.routes
- # Load custom error pages
- import dokk.error
|