+36
-27
@@ -26,6 +26,8 @@ import gluon.settings as settings
|
|||||||
from gluon.utils import web2py_uuid, secure_dumps, secure_loads
|
from gluon.utils import web2py_uuid, secure_dumps, secure_loads
|
||||||
from gluon.settings import global_settings
|
from gluon.settings import global_settings
|
||||||
from gluon import recfile
|
from gluon import recfile
|
||||||
|
from gluon.cache import CacheInRam
|
||||||
|
from gluon.fileutils import copystream
|
||||||
import hashlib
|
import hashlib
|
||||||
import portalocker
|
import portalocker
|
||||||
try:
|
try:
|
||||||
@@ -47,8 +49,7 @@ import cgi
|
|||||||
import urlparse
|
import urlparse
|
||||||
import copy
|
import copy
|
||||||
import tempfile
|
import tempfile
|
||||||
from gluon.cache import CacheInRam
|
|
||||||
from gluon.fileutils import copystream
|
|
||||||
|
|
||||||
FMT = '%a, %d-%b-%Y %H:%M:%S PST'
|
FMT = '%a, %d-%b-%Y %H:%M:%S PST'
|
||||||
PAST = 'Sat, 1-Jan-1971 00:00:00'
|
PAST = 'Sat, 1-Jan-1971 00:00:00'
|
||||||
@@ -82,13 +83,22 @@ less_template = '<link href="%s" rel="stylesheet/less" type="text/css" />'
|
|||||||
css_inline = '<style type="text/css">\n%s\n</style>'
|
css_inline = '<style type="text/css">\n%s\n</style>'
|
||||||
js_inline = '<script type="text/javascript">\n%s\n</script>'
|
js_inline = '<script type="text/javascript">\n%s\n</script>'
|
||||||
|
|
||||||
|
template_mapping = {
|
||||||
|
'css': css_template,
|
||||||
|
'js': js_template,
|
||||||
|
'coffee': coffee_template,
|
||||||
|
'ts': typescript_template,
|
||||||
|
'less': less_template,
|
||||||
|
'css:inline': css_inline,
|
||||||
|
'js:inline': js_inline
|
||||||
|
}
|
||||||
|
|
||||||
# IMPORTANT:
|
# IMPORTANT:
|
||||||
# this is required so that pickled dict(s) and class.__dict__
|
# this is required so that pickled dict(s) and class.__dict__
|
||||||
# are sorted and web2py can detect without ambiguity when a session changes
|
# are sorted and web2py can detect without ambiguity when a session changes
|
||||||
class SortingPickler(Pickler):
|
class SortingPickler(Pickler):
|
||||||
def save_dict(self, obj):
|
def save_dict(self, obj):
|
||||||
self.write(EMPTY_DICT if self.bin else MARK+DICT)
|
self.write(EMPTY_DICT if self.bin else MARK + DICT)
|
||||||
self.memoize(obj)
|
self.memoize(obj)
|
||||||
self._batch_setitems([(key, obj[key]) for key in sorted(obj)])
|
self._batch_setitems([(key, obj[key]) for key in sorted(obj)])
|
||||||
|
|
||||||
@@ -275,7 +285,7 @@ class Request(Storage):
|
|||||||
"""
|
"""
|
||||||
self._vars = copy.copy(self.get_vars)
|
self._vars = copy.copy(self.get_vars)
|
||||||
for key, value in self.post_vars.iteritems():
|
for key, value in self.post_vars.iteritems():
|
||||||
if not key in self._vars:
|
if key not in self._vars:
|
||||||
self._vars[key] = value
|
self._vars[key] = value
|
||||||
else:
|
else:
|
||||||
if not isinstance(self._vars[key], list):
|
if not isinstance(self._vars[key], list):
|
||||||
@@ -436,19 +446,20 @@ class Response(Storage):
|
|||||||
return page
|
return page
|
||||||
|
|
||||||
def include_meta(self):
|
def include_meta(self):
|
||||||
s = "\n";
|
s = "\n"
|
||||||
for meta in (self.meta or {}).iteritems():
|
for meta in (self.meta or {}).iteritems():
|
||||||
k, v = meta
|
k, v = meta
|
||||||
if isinstance(v,dict):
|
if isinstance(v, dict):
|
||||||
s = s+'<meta'+''.join(' %s="%s"' % (xmlescape(key), xmlescape(v[key])) for key in v) +' />\n'
|
s += '<meta' + ''.join(' %s="%s"' % (xmlescape(key), xmlescape(v[key])) for key in v) +' />\n'
|
||||||
else:
|
else:
|
||||||
s = s+'<meta name="%s" content="%s" />\n' % (k, xmlescape(v))
|
s += '<meta name="%s" content="%s" />\n' % (k, xmlescape(v))
|
||||||
self.write(s, escape=False)
|
self.write(s, escape=False)
|
||||||
|
|
||||||
def include_files(self, extensions=None):
|
def include_files(self, extensions=None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Caching method for writing out files.
|
Includes files (usually in the head).
|
||||||
|
Can minify and cache local files
|
||||||
By default, caches in ram for 5 minutes. To change,
|
By default, caches in ram for 5 minutes. To change,
|
||||||
response.cache_includes = (cache_method, time_expire).
|
response.cache_includes = (cache_method, time_expire).
|
||||||
Example: (cache.disk, 60) # caches to disk for 1 minute.
|
Example: (cache.disk, 60) # caches to disk for 1 minute.
|
||||||
@@ -456,9 +467,13 @@ class Response(Storage):
|
|||||||
from gluon import URL
|
from gluon import URL
|
||||||
|
|
||||||
files = []
|
files = []
|
||||||
|
ext_files = []
|
||||||
has_js = has_css = False
|
has_js = has_css = False
|
||||||
for item in self.files:
|
for item in self.files:
|
||||||
if extensions and not item.split('.')[-1] in extensions:
|
if isinstance(item, (list, tuple)):
|
||||||
|
ext_files.append(item)
|
||||||
|
continue
|
||||||
|
if extensions and not item.rpartition('.')[2] in extensions:
|
||||||
continue
|
continue
|
||||||
if item in files:
|
if item in files:
|
||||||
continue
|
continue
|
||||||
@@ -487,10 +502,13 @@ class Response(Storage):
|
|||||||
time_expire)
|
time_expire)
|
||||||
else:
|
else:
|
||||||
files = call_minify()
|
files = call_minify()
|
||||||
s = ''
|
|
||||||
|
files.extend(ext_files)
|
||||||
|
s = []
|
||||||
for item in files:
|
for item in files:
|
||||||
if isinstance(item, str):
|
if isinstance(item, str):
|
||||||
f = item.lower().split('?')[0]
|
f = item.lower().split('?')[0]
|
||||||
|
ext = f.rpartition('.')[2]
|
||||||
# if static_version we need also to check for
|
# if static_version we need also to check for
|
||||||
# static_version_urls. In that case, the _.x.x.x
|
# static_version_urls. In that case, the _.x.x.x
|
||||||
# bit would have already been added by the URL()
|
# bit would have already been added by the URL()
|
||||||
@@ -498,24 +516,15 @@ class Response(Storage):
|
|||||||
if self.static_version and not self.static_version_urls:
|
if self.static_version and not self.static_version_urls:
|
||||||
item = item.replace(
|
item = item.replace(
|
||||||
'/static/', '/static/_%s/' % self.static_version, 1)
|
'/static/', '/static/_%s/' % self.static_version, 1)
|
||||||
if f.endswith('.css'):
|
tmpl = template_mapping.get(ext)
|
||||||
s += css_template % item
|
if tmpl:
|
||||||
elif f.endswith('.js'):
|
s.append(tmpl % item)
|
||||||
s += js_template % item
|
|
||||||
elif f.endswith('.coffee'):
|
|
||||||
s += coffee_template % item
|
|
||||||
elif f.endswith('.ts'):
|
|
||||||
# http://www.typescriptlang.org/
|
|
||||||
s += typescript_template % item
|
|
||||||
elif f.endswith('.less'):
|
|
||||||
s += less_template % item
|
|
||||||
elif isinstance(item, (list, tuple)):
|
elif isinstance(item, (list, tuple)):
|
||||||
f = item[0]
|
f = item[0]
|
||||||
if f == 'css:inline':
|
tmpl = template_mapping.get(f)
|
||||||
s += css_inline % item[1]
|
if tmpl:
|
||||||
elif f == 'js:inline':
|
s.append(tmpl % item[1])
|
||||||
s += js_inline % item[1]
|
self.write(''.join(s), escape=False)
|
||||||
self.write(s, escape=False)
|
|
||||||
|
|
||||||
def stream(self,
|
def stream(self,
|
||||||
stream,
|
stream,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from test_http import *
|
|||||||
from test_cache import *
|
from test_cache import *
|
||||||
from test_contenttype import *
|
from test_contenttype import *
|
||||||
from test_fileutils import *
|
from test_fileutils import *
|
||||||
|
from test_globals import *
|
||||||
from test_html import *
|
from test_html import *
|
||||||
from test_is_url import *
|
from test_is_url import *
|
||||||
from test_languages import *
|
from test_languages import *
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
Unit tests for gluon.globals
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from fix_path import fix_sys_path
|
||||||
|
|
||||||
|
fix_sys_path(__file__)
|
||||||
|
|
||||||
|
from gluon.globals import Response
|
||||||
|
from gluon import URL
|
||||||
|
|
||||||
|
|
||||||
|
class testResponse(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_include_files(self):
|
||||||
|
|
||||||
|
def return_includes(response, extensions=None):
|
||||||
|
response.include_files(extensions)
|
||||||
|
return response.body.getvalue()
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
response.files.append(URL('a', 'static', 'css/file.css'))
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content, '<link href="/a/static/css/file.css" rel="stylesheet" type="text/css" />')
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
response.files.append(URL('a', 'static', 'css/file.js'))
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content, '<script src="/a/static/css/file.js" type="text/javascript"></script>')
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
response.files.append(URL('a', 'static', 'css/file.coffee'))
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content, '<script src="/a/static/css/file.coffee" type="text/coffee"></script>')
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
response.files.append(URL('a', 'static', 'css/file.ts'))
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content, '<script src="/a/static/css/file.ts" type="text/typescript"></script>')
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
response.files.append(URL('a', 'static', 'css/file.less'))
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content, '<link href="/a/static/css/file.less" rel="stylesheet/less" type="text/css" />')
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
response.files.append(('css:inline', 'background-color; white;'))
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content, '<style type="text/css">\nbackground-color; white;\n</style>')
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
response.files.append(('js:inline', 'alert("hello")'))
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content, '<script type="text/javascript">\nalert("hello")\n</script>')
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
response.files.append('https://code.jquery.com/jquery-1.11.3.min.js')
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content, '<script src="https://code.jquery.com/jquery-1.11.3.min.js" type="text/javascript"></script>')
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
response.files.append('https://code.jquery.com/jquery-1.11.3.min.js?var=0')
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content, '<script src="https://code.jquery.com/jquery-1.11.3.min.js?var=0" type="text/javascript"></script>')
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
response.files.append('https://code.jquery.com/jquery-1.11.3.min.js?var=0')
|
||||||
|
response.files.append('https://code.jquery.com/jquery-1.11.3.min.js?var=0')
|
||||||
|
response.files.append(URL('a', 'static', 'css/file.css'))
|
||||||
|
response.files.append(URL('a', 'static', 'css/file.css'))
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content,
|
||||||
|
'<script src="https://code.jquery.com/jquery-1.11.3.min.js?var=0" type="text/javascript"></script>' +
|
||||||
|
'<link href="/a/static/css/file.css" rel="stylesheet" type="text/css" />')
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
response.files.append(('js', 'http://maps.google.com/maps/api/js?sensor=false'))
|
||||||
|
response.files.append('https://code.jquery.com/jquery-1.11.3.min.js?var=0')
|
||||||
|
response.files.append(URL('a', 'static', 'css/file.css'))
|
||||||
|
response.files.append(URL('a', 'static', 'css/file.ts'))
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content,
|
||||||
|
'<script src="https://code.jquery.com/jquery-1.11.3.min.js?var=0" type="text/javascript"></script>' +
|
||||||
|
'<link href="/a/static/css/file.css" rel="stylesheet" type="text/css" />' +
|
||||||
|
'<script src="/a/static/css/file.ts" type="text/typescript"></script>' +
|
||||||
|
'<script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
response.files.append(URL('a', 'static', 'css/file.js'))
|
||||||
|
response.files.append(URL('a', 'static', 'css/file.css'))
|
||||||
|
content = return_includes(response, extensions=['css'])
|
||||||
|
self.assertEqual(content, '<link href="/a/static/css/file.css" rel="stylesheet" type="text/css" />')
|
||||||
|
|
||||||
|
#regr test for #628
|
||||||
|
response = Response()
|
||||||
|
response.files.append('http://maps.google.com/maps/api/js?sensor=false')
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content, '')
|
||||||
|
|
||||||
|
#regr test for #628
|
||||||
|
response = Response()
|
||||||
|
response.files.append(('js', 'http://maps.google.com/maps/api/js?sensor=false'))
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content, '<script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>')
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
response.files.append(['js', 'http://maps.google.com/maps/api/js?sensor=false'])
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content, '<script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>')
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
response.files.append(('js1', 'http://maps.google.com/maps/api/js?sensor=false'))
|
||||||
|
content = return_includes(response)
|
||||||
|
self.assertEqual(content, '')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user