Merge ssh://github.com/web2py/web2py

This commit is contained in:
Michele Comitini
2013-09-05 11:17:43 +02:00
41 changed files with 548 additions and 302 deletions
+6 -6
View File
@@ -6,17 +6,17 @@ python:
- '2.7' - '2.7'
- 'pypy' - 'pypy'
install: install:
- pip install -e . --use-mirrors - pip install -e .
env: env:
- DB=sqlite:memory - DB=sqlite:memory
- DB=mysql://root:@localhost/test_w2p - DB=mysql://root:@localhost/test_w2p
- DB=postgres://postgres:@localhost/test_w2p - DB=postgres://postgres:@localhost/test_w2p
before_script: before_script:
- if [[ $TRAVIS_PYTHON_VERSION != '2.7' ]]; then pip install --use-mirrors unittest2; fi - if [[ $TRAVIS_PYTHON_VERSION != '2.7' ]]; then pip install unittest2; fi
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --use-mirrors coverage; fi; - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install coverage; fi;
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --use-mirrors python-coveralls; fi - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install python-coveralls; fi
- if [[ $DB == postgres* ]]; then pip install --use-mirrors psycopg2; fi; - if [[ $DB == postgres* ]]; then pip install psycopg2; fi;
- if [[ $TRAVIS_PYTHON_VERSION == '2.5' ]]; then pip install --use-mirrors pysqlite; fi - if [[ $TRAVIS_PYTHON_VERSION == '2.5' ]]; then pip install pysqlite; fi
- if [[ $DB == mysql* ]]; then mysql -e 'create database test_w2p;'; fi - if [[ $DB == mysql* ]]; then mysql -e 'create database test_w2p;'; fi
- if [[ $DB == postgres* ]]; then psql -c 'create database test_w2p;' -U postgres; fi - if [[ $DB == postgres* ]]; then psql -c 'create database test_w2p;' -U postgres; fi
#Temporal solution to travis issue #155 #Temporal solution to travis issue #155
+1 -1
View File
@@ -54,7 +54,7 @@ src:
### build web2py_src.zip ### build web2py_src.zip
echo '' > NEWINSTALL echo '' > NEWINSTALL
mv web2py_src.zip web2py_src_old.zip | echo 'no old' mv web2py_src.zip web2py_src_old.zip | echo 'no old'
cd ..; zip -r web2py/web2py_src.zip web2py/gluon/*.py web2py/gluon/contrib/* web2py/extras/* handlers/* web2py/examples/* web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/MANIFEST.in web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py cd ..; zip -r web2py/web2py_src.zip web2py/web2py.py web2py/anyserver.py web2py/gluon/*.py web2py/gluon/contrib/* web2py/extras/* web2py/handlers/* web2py/examples/* web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/MANIFEST.in web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py
mdp: mdp:
make src make src
+1 -1
View File
@@ -1 +1 @@
Version 2.6.0-development+timestamp.2013.08.29.21.31.47 Version 2.6.0-development+timestamp.2013.09.04.16.32.36
+4 -4
View File
@@ -189,7 +189,7 @@ def run(servername, ip, port, softcron=True, logging=False, profiler=None):
if logging: if logging:
application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase, application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase,
logfilename='httpserver.log', logfilename='httpserver.log',
profilerfilename=profiler) profiler_dir=profiler)
else: else:
application = gluon.main.wsgibase application = gluon.main.wsgibase
if softcron: if softcron:
@@ -319,8 +319,8 @@ def main():
parser.add_option('-P', parser.add_option('-P',
'--profiler', '--profiler',
default=False, default=False,
dest='profiler', dest='profiler_dir',
help='profiler filename') help='profiler dir')
servers = ', '.join(x for x in dir(Servers) if not x[0] == '_') servers = ', '.join(x for x in dir(Servers) if not x[0] == '_')
parser.add_option('-s', parser.add_option('-s',
'--server', '--server',
@@ -346,7 +346,7 @@ def main():
print 'starting %s on %s:%s...' % ( print 'starting %s on %s:%s...' % (
options.server, options.ip, options.port) options.server, options.ip, options.port)
run(options.server, options.ip, options.port, run(options.server, options.ip, options.port,
logging=options.logging, profiler=options.profiler) logging=options.logging, profiler=options.profiler_dir)
if __name__ == '__main__': if __name__ == '__main__':
main() main()
+2 -1
View File
@@ -1016,8 +1016,9 @@ def design():
privates.sort() privates.sort()
# Get all static files # Get all static files
MAXNFILES = 1000
statics = listdir(apath('%s/static/' % app, r=request), '[^\.#].*') statics = listdir(apath('%s/static/' % app, r=request), '[^\.#].*')
statics = [x.replace('\\', '/') for x in statics] statics = [x.replace('\\', '/') for x in statics[:MAXNFILES]]
statics.sort() statics.sort()
# Get all languages # Get all languages
+1
View File
@@ -248,6 +248,7 @@
'Next Edit Point': 'nächster Bearbeitungsschritt', 'Next Edit Point': 'nächster Bearbeitungsschritt',
'NO': 'NEIN', 'NO': 'NEIN',
'No databases in this application': 'Keine Datenbank in dieser Anwendung', 'No databases in this application': 'Keine Datenbank in dieser Anwendung',
'no package selected': 'no package selected',
'No ticket_storage.txt found under /private folder': 'No ticket_storage.txt found under /private folder', 'No ticket_storage.txt found under /private folder': 'No ticket_storage.txt found under /private folder',
'online designer': 'online designer', 'online designer': 'online designer',
'or alternatively': 'or alternatively', 'or alternatively': 'or alternatively',
+43 -29
View File
@@ -74,28 +74,6 @@
* Ideally all events should be bound to the document, so we can avoid calling * Ideally all events should be bound to the document, so we can avoid calling
* this over and over... all will be bound to the document * this over and over... all will be bound to the document
*/ */
var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d";
var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S";
$("input.date", target).each(function () {
$(this).attr('autocomplete', 'off');
Calendar.setup({
inputField: this,
ifFormat: date_format,
showsTime: false
});
});
$("input.datetime", target).each(function () {
$(this).attr('autocomplete', 'off');
Calendar.setup({
inputField: this,
ifFormat: datetime_format,
showsTime: true,
timeFormat: "24"
});
});
$("input.time", target).each(function () {
$(this).timeEntry().attr('autocomplete', 'off');
});
/*adds btn class to buttons*/ /*adds btn class to buttons*/
$('button', target).addClass('btn'); $('button', target).addClass('btn');
$('form input[type="submit"], form input[type="button"]', target).addClass('btn'); $('form input[type="submit"], form input[type="button"]', target).addClass('btn');
@@ -181,18 +159,13 @@
/* /*
* This is called once for page * This is called once for page
* Ideally it should bound all the things that are needed * Ideally it should bound all the things that are needed
* and require no dom manipulations
*/ */
var doc = $(document); var doc = $(document);
doc.on('click', '.flash', function (e) { doc.on('click', '.flash', function (e) {
var t = $(this); var t = $(this);
if(t.css('top') == '0px') t.slideUp('slow'); if(t.css('top') == '0px') t.slideUp('slow');
else t.fadeOut(); else t.fadeOut();
/* if I want to display a clickable something
* inside flash, I should not be prevented to follow it
*
* e.preventDefault();
*/
}); });
doc.on('keyup', 'input.integer', function () { doc.on('keyup', 'input.integer', function () {
this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse(); this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse();
@@ -205,7 +178,48 @@
if(this.checked) if(this.checked)
if(!web2py.confirm(confirm_message)) this.checked = false; if(!web2py.confirm(confirm_message)) this.checked = false;
}); });
var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S";
doc.on('click', "input.datetime", function () {
var tformat = $(this).data('w2p_datetime_format')
var active = $(this).data('w2p_datetime');
var format = (typeof tformat != 'undefined') ? tformat : datetime_format;
if(active === undefined) {
Calendar.setup({
inputField: this,
ifFormat: format,
showsTime: true,
timeFormat: "24"
});
$(this).attr('autocomplete', 'off');
$(this).data('w2p_datetime', 1);
$(this).trigger('click');
}
});
var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d";
doc.on('click', "input.date", function () {
var tformat = $(this).data('w2p_date_format')
var active = $(this).data('w2p_date');
var format = (typeof tformat != 'undefined') ? tformat : date_format;
if(active === undefined) {
Calendar.setup({
inputField: this,
ifFormat: format,
showsTime: false
});
$(this).data('w2p_date', 1);
$(this).attr('autocomplete', 'off');
$(this).trigger('click');
}
});
doc.on('focus', "input.time", function () {
var active = $(this).data('w2p_time');
if(active === undefined) {
$(this).timeEntry({
spinnerImage: ''
}).attr('autocomplete', 'off');
$(this).data('w2p_time', 1);
}
});
doc.ajaxSuccess(function (e, xhr) { doc.ajaxSuccess(function (e, xhr) {
var redirect = xhr.getResponseHeader('web2py-redirect-location'); var redirect = xhr.getResponseHeader('web2py-redirect-location');
if(redirect !== null) { if(redirect !== null) {
@@ -1,5 +1,6 @@
import time import time
response.view = 'cache_examples/generic.html'
def cache_in_ram(): def cache_in_ram():
"""cache the output of the lambda function in ram""" """cache the output of the lambda function in ram"""
+43 -29
View File
@@ -74,28 +74,6 @@
* Ideally all events should be bound to the document, so we can avoid calling * Ideally all events should be bound to the document, so we can avoid calling
* this over and over... all will be bound to the document * this over and over... all will be bound to the document
*/ */
var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d";
var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S";
$("input.date", target).each(function () {
$(this).attr('autocomplete', 'off');
Calendar.setup({
inputField: this,
ifFormat: date_format,
showsTime: false
});
});
$("input.datetime", target).each(function () {
$(this).attr('autocomplete', 'off');
Calendar.setup({
inputField: this,
ifFormat: datetime_format,
showsTime: true,
timeFormat: "24"
});
});
$("input.time", target).each(function () {
$(this).timeEntry().attr('autocomplete', 'off');
});
/*adds btn class to buttons*/ /*adds btn class to buttons*/
$('button', target).addClass('btn'); $('button', target).addClass('btn');
$('form input[type="submit"], form input[type="button"]', target).addClass('btn'); $('form input[type="submit"], form input[type="button"]', target).addClass('btn');
@@ -181,18 +159,13 @@
/* /*
* This is called once for page * This is called once for page
* Ideally it should bound all the things that are needed * Ideally it should bound all the things that are needed
* and require no dom manipulations
*/ */
var doc = $(document); var doc = $(document);
doc.on('click', '.flash', function (e) { doc.on('click', '.flash', function (e) {
var t = $(this); var t = $(this);
if(t.css('top') == '0px') t.slideUp('slow'); if(t.css('top') == '0px') t.slideUp('slow');
else t.fadeOut(); else t.fadeOut();
/* if I want to display a clickable something
* inside flash, I should not be prevented to follow it
*
* e.preventDefault();
*/
}); });
doc.on('keyup', 'input.integer', function () { doc.on('keyup', 'input.integer', function () {
this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse(); this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse();
@@ -205,7 +178,48 @@
if(this.checked) if(this.checked)
if(!web2py.confirm(confirm_message)) this.checked = false; if(!web2py.confirm(confirm_message)) this.checked = false;
}); });
var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S";
doc.on('click', "input.datetime", function () {
var tformat = $(this).data('w2p_datetime_format')
var active = $(this).data('w2p_datetime');
var format = (typeof tformat != 'undefined') ? tformat : datetime_format;
if(active === undefined) {
Calendar.setup({
inputField: this,
ifFormat: format,
showsTime: true,
timeFormat: "24"
});
$(this).attr('autocomplete', 'off');
$(this).data('w2p_datetime', 1);
$(this).trigger('click');
}
});
var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d";
doc.on('click', "input.date", function () {
var tformat = $(this).data('w2p_date_format')
var active = $(this).data('w2p_date');
var format = (typeof tformat != 'undefined') ? tformat : date_format;
if(active === undefined) {
Calendar.setup({
inputField: this,
ifFormat: format,
showsTime: false
});
$(this).data('w2p_date', 1);
$(this).attr('autocomplete', 'off');
$(this).trigger('click');
}
});
doc.on('focus', "input.time", function () {
var active = $(this).data('w2p_time');
if(active === undefined) {
$(this).timeEntry({
spinnerImage: ''
}).attr('autocomplete', 'off');
$(this).data('w2p_time', 1);
}
});
doc.ajaxSuccess(function (e, xhr) { doc.ajaxSuccess(function (e, xhr) {
var redirect = xhr.getResponseHeader('web2py-redirect-location'); var redirect = xhr.getResponseHeader('web2py-redirect-location');
if(redirect !== null) { if(redirect !== null) {
@@ -0,0 +1,3 @@
{{extend 'layout.html'}}
<h1>Cache Examples</h1>
{{=BEAUTIFY(response._vars)}}
@@ -0,0 +1,4 @@
{{extend 'layout.html'}}
<h1>Ajax Wiki</h1>
{{=form}}
{{=html}}
+43 -29
View File
@@ -74,28 +74,6 @@
* Ideally all events should be bound to the document, so we can avoid calling * Ideally all events should be bound to the document, so we can avoid calling
* this over and over... all will be bound to the document * this over and over... all will be bound to the document
*/ */
var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d";
var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S";
$("input.date", target).each(function () {
$(this).attr('autocomplete', 'off');
Calendar.setup({
inputField: this,
ifFormat: date_format,
showsTime: false
});
});
$("input.datetime", target).each(function () {
$(this).attr('autocomplete', 'off');
Calendar.setup({
inputField: this,
ifFormat: datetime_format,
showsTime: true,
timeFormat: "24"
});
});
$("input.time", target).each(function () {
$(this).timeEntry().attr('autocomplete', 'off');
});
/*adds btn class to buttons*/ /*adds btn class to buttons*/
$('button', target).addClass('btn'); $('button', target).addClass('btn');
$('form input[type="submit"], form input[type="button"]', target).addClass('btn'); $('form input[type="submit"], form input[type="button"]', target).addClass('btn');
@@ -181,18 +159,13 @@
/* /*
* This is called once for page * This is called once for page
* Ideally it should bound all the things that are needed * Ideally it should bound all the things that are needed
* and require no dom manipulations
*/ */
var doc = $(document); var doc = $(document);
doc.on('click', '.flash', function (e) { doc.on('click', '.flash', function (e) {
var t = $(this); var t = $(this);
if(t.css('top') == '0px') t.slideUp('slow'); if(t.css('top') == '0px') t.slideUp('slow');
else t.fadeOut(); else t.fadeOut();
/* if I want to display a clickable something
* inside flash, I should not be prevented to follow it
*
* e.preventDefault();
*/
}); });
doc.on('keyup', 'input.integer', function () { doc.on('keyup', 'input.integer', function () {
this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse(); this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse();
@@ -205,7 +178,48 @@
if(this.checked) if(this.checked)
if(!web2py.confirm(confirm_message)) this.checked = false; if(!web2py.confirm(confirm_message)) this.checked = false;
}); });
var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S";
doc.on('click', "input.datetime", function () {
var tformat = $(this).data('w2p_datetime_format')
var active = $(this).data('w2p_datetime');
var format = (typeof tformat != 'undefined') ? tformat : datetime_format;
if(active === undefined) {
Calendar.setup({
inputField: this,
ifFormat: format,
showsTime: true,
timeFormat: "24"
});
$(this).attr('autocomplete', 'off');
$(this).data('w2p_datetime', 1);
$(this).trigger('click');
}
});
var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d";
doc.on('click', "input.date", function () {
var tformat = $(this).data('w2p_date_format')
var active = $(this).data('w2p_date');
var format = (typeof tformat != 'undefined') ? tformat : date_format;
if(active === undefined) {
Calendar.setup({
inputField: this,
ifFormat: format,
showsTime: false
});
$(this).data('w2p_date', 1);
$(this).attr('autocomplete', 'off');
$(this).trigger('click');
}
});
doc.on('focus', "input.time", function () {
var active = $(this).data('w2p_time');
if(active === undefined) {
$(this).timeEntry({
spinnerImage: ''
}).attr('autocomplete', 'off');
$(this).data('w2p_time', 1);
}
});
doc.ajaxSuccess(function (e, xhr) { doc.ajaxSuccess(function (e, xhr) {
var redirect = xhr.getResponseHeader('web2py-redirect-location'); var redirect = xhr.getResponseHeader('web2py-redirect-location');
if(redirect !== null) { if(redirect !== null) {
+1 -1
View File
@@ -18,7 +18,7 @@ from fileutils import up, fix_newlines, abspath, recursive_unlink
from fileutils import read_file, write_file, parse_version from fileutils import read_file, write_file, parse_version
from restricted import RestrictedError from restricted import RestrictedError
from settings import global_settings from settings import global_settings
from http import HTTP
if not global_settings.web2py_runtime_gae: if not global_settings.web2py_runtime_gae:
import site import site
+1 -1
View File
@@ -68,7 +68,7 @@ class CacheAbstract(object):
def __init__(self, request=None): def __init__(self, request=None):
""" """
Paremeters Parameters
---------- ----------
request: request:
the global request object the global request object
-1
View File
@@ -13,7 +13,6 @@ FOR INTERNAL USE ONLY
""" """
import re import re
import sys
import fnmatch import fnmatch
import os import os
import copy import copy
+66 -3
View File
@@ -6,6 +6,12 @@
import re import re
from cgi import escape from cgi import escape
from string import maketrans from string import maketrans
try:
from ast import parse as ast_parse
import ast
except ImportError: # python 2.5
from compiler import parse
import compiler.ast as ast
""" """
TODO: next version should use MathJax TODO: next version should use MathJax
@@ -532,7 +538,7 @@ LINK = '\x07'
DISABLED_META = '\x08' DISABLED_META = '\x08'
LATEX = '<img src="http://chart.apis.google.com/chart?cht=tx&chl=%s" />' LATEX = '<img src="http://chart.apis.google.com/chart?cht=tx&chl=%s" />'
regex_URL=re.compile(r'@/(?P<a>\w*)/(?P<c>\w*)/(?P<f>\w*(\.\w+)?)(/(?P<args>[\w\.\-/]+))?') regex_URL=re.compile(r'@/(?P<a>\w*)/(?P<c>\w*)/(?P<f>\w*(\.\w+)?)(/(?P<args>[\w\.\-/]+))?')
regex_env=re.compile(r'@\{(?P<a>[\w\-\.]+?)(\:(?P<b>.*?))?\}') regex_env2=re.compile(r'@\{(?P<a>[\w\-\.]+?)(\:(?P<b>.*?))?\}')
regex_expand_meta = re.compile('('+META+'|'+DISABLED_META+'|````)') regex_expand_meta = re.compile('('+META+'|'+DISABLED_META+'|````)')
regex_dd=re.compile(r'\$\$(?P<latex>.*?)\$\$') regex_dd=re.compile(r'\$\$(?P<latex>.*?)\$\$')
regex_code = re.compile('('+META+'|'+DISABLED_META+r'|````)|(``(?P<t>.+?)``(?::(?P<c>[a-zA-Z][_a-zA-Z\-\d]*)(?:\[(?P<p>[^\]]*)\])?)?)',re.S) regex_code = re.compile('('+META+'|'+DISABLED_META+r'|````)|(``(?P<t>.+?)``(?::(?P<c>[a-zA-Z][_a-zA-Z\-\d]*)(?:\[(?P<p>[^\]]*)\])?)?)',re.S)
@@ -553,6 +559,53 @@ regex_markmin_escape = re.compile(r"(\\*)(['`:*~\\[\]{}@\$+\-.#\n])")
regex_backslash = re.compile(r"\\(['`:*~\\[\]{}@\$+\-.#\n])") regex_backslash = re.compile(r"\\(['`:*~\\[\]{}@\$+\-.#\n])")
ttab_in = maketrans("'`:*~\\[]{}@$+-.#\n", '\x0b\x0c\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x05') ttab_in = maketrans("'`:*~\\[]{}@$+-.#\n", '\x0b\x0c\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x05')
ttab_out = maketrans('\x0b\x0c\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x05',"'`:*~\\[]{}@$+-.#\n") ttab_out = maketrans('\x0b\x0c\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x05',"'`:*~\\[]{}@$+-.#\n")
regex_quote = re.compile('(?P<name>\w+?)\s*\=\s*')
def make_dict(b):
return '{%s}' % regex_quote.sub("'\g<name>':",b)
def safe_eval(node_or_string, env):
"""
Safely evaluate an expression node or a string containing a Python
expression. The string or node provided may only consist of the following
Python literal structures: strings, numbers, tuples, lists, dicts, booleans,
and None.
"""
_safe_names = {'None': None, 'True': True, 'False': False}
_safe_names.update(env)
if isinstance(node_or_string, basestring):
node_or_string = ast_parse(node_or_string, mode='eval')
if isinstance(node_or_string, ast.Expression):
node_or_string = node_or_string.body
def _convert(node):
if isinstance(node, ast.Str):
return node.s
elif isinstance(node, ast.Num):
return node.n
elif isinstance(node, ast.Tuple):
return tuple(map(_convert, node.elts))
elif isinstance(node, ast.List):
return list(map(_convert, node.elts))
elif isinstance(node, ast.Dict):
return dict((_convert(k), _convert(v)) for k, v
in zip(node.keys, node.values))
elif isinstance(node, ast.Name):
if node.id in _safe_names:
return _safe_names[node.id]
elif isinstance(node, ast.BinOp) and \
isinstance(node.op, (Add, Sub)) and \
isinstance(node.right, Num) and \
isinstance(node.right.n, complex) and \
isinstance(node.left, Num) and \
isinstance(node.left.n, (int, long, float)):
left = node.left.n
right = node.right.n
if isinstance(node.op, Add):
return left + right
else:
return left - right
raise ValueError('malformed string')
return _convert(node_or_string)
def markmin_escape(text): def markmin_escape(text):
""" insert \\ before markmin control characters: '`:*~[]{}@$ """ """ insert \\ before markmin control characters: '`:*~[]{}@$ """
@@ -571,15 +624,22 @@ def replace_at_urls(text,url):
return regex_URL.sub(u1,text) return regex_URL.sub(u1,text)
def replace_components(text,env): def replace_components(text,env):
# not perfect but acceptable
def u2(match, env=env): def u2(match, env=env):
f = env.get(match.group('a'), match.group(0)) f = env.get(match.group('a'), match.group(0))
if callable(f): if callable(f):
b = match.group('b')
try: try:
f = f(match.group('b')) b = safe_eval(make_dict(b),env)
except:
pass
try:
f = f(**b) if isinstance(b,dict) else f(b)
except Exception, e: except Exception, e:
f = 'ERROR: %s' % e f = 'ERROR: %s' % e
return str(f) return str(f)
return regex_env.sub(u2, text) text = regex_env2.sub(u2, text)
return text
def autolinks_simple(url): def autolinks_simple(url):
""" """
@@ -844,6 +904,9 @@ def render(text,
>>> render("**@{probe:1}**", environment=dict(probe=lambda t:"test %s" % t)) >>> render("**@{probe:1}**", environment=dict(probe=lambda t:"test %s" % t))
'<p><strong>test 1</strong></p>' '<p><strong>test 1</strong></p>'
>>> render("**@{probe:t=a}**", environment=dict(probe=lambda t:"test %s" % t, a=1))
'<p><strong>test 1</strong></p>'
>>> render('[[id1 [span **messag** in ''markmin''] ]] ... [[**link** to id [link\\\'s title] #mark1]]') >>> render('[[id1 [span **messag** in ''markmin''] ]] ... [[**link** to id [link\\\'s title] #mark1]]')
'<p><span class="anchor" id="markmin_id1">span <strong>messag</strong> in markmin</span> ... <a href="#markmin_mark1" title="link\\\'s title"><strong>link</strong> to id</a></p>' '<p><span class="anchor" id="markmin_id1">span <strong>messag</strong> in markmin</span> ... <a href="#markmin_mark1" title="link\\\'s title"><strong>link</strong> to id</a></p>'
+9 -5
View File
@@ -48,12 +48,12 @@ _DEBUG = True
_HISTORY_KIND = '_Shell_History' _HISTORY_KIND = '_Shell_History'
# Types that can't be pickled. # Types that can't be pickled.
UNPICKLABLE_TYPES = ( UNPICKLABLE_TYPES = [
types.ModuleType, types.ModuleType,
types.TypeType, types.TypeType,
types.ClassType, types.ClassType,
types.FunctionType, types.FunctionType,
) ]
# Unpicklable statements to seed new historys with. # Unpicklable statements to seed new historys with.
INITIAL_UNPICKLABLES = [ INITIAL_UNPICKLABLES = [
@@ -244,8 +244,8 @@ def run(history, statement, env={}):
for name, val in statement_module.__dict__.items(): for name, val in statement_module.__dict__.items():
if name not in old_globals or represent(val) != old_globals[name]: if name not in old_globals or represent(val) != old_globals[name]:
new_globals[name] = val new_globals[name] = val
if True in [isinstance(val, UNPICKLABLE_TYPES) if True in [isinstance(val, tuple(UNPICKLABLE_TYPES))
for val in new_globals.values()]: for val in new_globals.values()]:
# this statement added an unpicklable global. store the statement and # this statement added an unpicklable global. store the statement and
# the names of all of the globals it added in the unpicklables. # the names of all of the globals it added in the unpicklables.
@@ -256,7 +256,11 @@ def run(history, statement, env={}):
# new globals back into the datastore. # new globals back into the datastore.
for name, val in new_globals.items(): for name, val in new_globals.items():
if not name.startswith('__'): if not name.startswith('__'):
history.set_global(name, val) try:
history.set_global(name, val)
except TypeError, ex:
UNPICKLABLE_TYPES.append(type(val))
history.add_unpicklable(statement, new_globals.keys())
finally: finally:
sys.modules['__main__'] = old_main sys.modules['__main__'] = old_main
-1
View File
@@ -3,7 +3,6 @@
import __builtin__ import __builtin__
import os import os
import re
import sys import sys
import threading import threading
import traceback import traceback
+38 -21
View File
@@ -690,7 +690,11 @@ class BaseAdapter(ConnectionPool):
return isinstance(exception, self.driver.ProgrammingError) return isinstance(exception, self.driver.ProgrammingError)
def id_query(self, table): def id_query(self, table):
return table._id != None pkeys = getattr(table,'_primarykey',None)
if pkeys:
return table[pkeys[0]] != None
else:
return table._id != None
def adapt(self, obj): def adapt(self, obj):
return "'%s'" % obj.replace("'", "''") return "'%s'" % obj.replace("'", "''")
@@ -6939,9 +6943,11 @@ def sqlhtml_validators(field):
referenced._format,multiple=True) referenced._format,multiple=True)
else: else:
requires = validators.IS_IN_DB(db,referenced._id, requires = validators.IS_IN_DB(db,referenced._id,
multiple=True) multiple=True)
if field.unique: if field.unique:
requires._and = validators.IS_NOT_IN_DB(db,field) requires._and = validators.IS_NOT_IN_DB(db,field)
if not field.notnull:
requires = validators.IS_EMPTY_OR(requires)
return requires return requires
elif field_type.startswith('list:'): elif field_type.startswith('list:'):
def repr_list(values,row=None): return', '.join(str(v) for v in (values or [])) def repr_list(values,row=None): return', '.join(str(v) for v in (values or []))
@@ -8206,8 +8212,12 @@ class Reference(long):
def __getattr__(self, key): def __getattr__(self, key):
if key == 'id': if key == 'id':
return long(self) return long(self)
self.__allocate() if key in self._table:
return self._record.get(key, None) self.__allocate()
if self._record:
return self._record.get(key,None) # to deal with case self.update_record()
else:
return None
def get(self, key, default=None): def get(self, key, default=None):
return self.__getattr__(key, default) return self.__getattr__(key, default)
@@ -8416,8 +8426,9 @@ class Table(object):
def _enable_record_versioning(self, def _enable_record_versioning(self,
archive_db=None, archive_db=None,
archive_name = '%(tablename)s_archive', archive_name = '%(tablename)s_archive',
is_active = 'is_active',
current_record = 'current_record', current_record = 'current_record',
is_active = 'is_active'): current_record_label = None):
db = self._db db = self._db
archive_db = archive_db or db archive_db = archive_db or db
archive_name = archive_name % dict(tablename=self._tablename) archive_name = archive_name % dict(tablename=self._tablename)
@@ -8432,7 +8443,8 @@ class Table(object):
clones.append(field.clone( clones.append(field.clone(
unique=False, type=field.type if nfk else 'bigint')) unique=False, type=field.type if nfk else 'bigint'))
archive_db.define_table( archive_db.define_table(
archive_name, Field(current_record,field_type), *clones) archive_name, Field(current_record,field_type,
label=current_record_label), *clones)
self._before_update.append( self._before_update.append(
lambda qset,fs,db=archive_db,an=archive_name,cn=current_record: lambda qset,fs,db=archive_db,an=archive_name,cn=current_record:
archive_record(qset,fs,db[an],cn)) archive_record(qset,fs,db[an],cn))
@@ -8875,24 +8887,31 @@ class Table(object):
first = True first = True
unique_idx = None unique_idx = None
for line in reader: for lineno, line in enumerate(reader):
if not line: if not line:
break break
if not colnames: if not colnames:
# assume this is the first line of the input, contains colnames
colnames = [x.split('.',1)[-1] for x in line][:len(line)] colnames = [x.split('.',1)[-1] for x in line][:len(line)]
cols, cid = [], None cols, cid = [], None
for i,colname in enumerate(colnames): for i,colname in enumerate(colnames):
if is_id(colname): if is_id(colname):
cid = i cid = i
else: elif colname in self.fields:
cols.append(i) cols.append((i,self[colname]))
if colname == unique: if colname == unique:
unique_idx = i unique_idx = i
else: else:
items = [fix(self[colnames[i]], line[i], id_map, id_offset) \ # every other line contains instead data
for i in cols if colnames[i] in self.fields] items = []
for i, field in cols:
if not id_map and cid is not None and id_offset is not None and not unique_idx: try:
items.append(fix(field, line[i], id_map, id_offset))
except ValueError:
raise RuntimeError("Unable to parse line:%s field:%s value:'%s'"
% (lineno+1,field,line[i]))
if not (id_map or cid is None or id_offset is None or unique_idx):
csv_id = long(line[cid]) csv_id = long(line[cid])
curr_id = self.insert(**dict(items)) curr_id = self.insert(**dict(items))
if first: if first:
@@ -8900,10 +8919,8 @@ class Table(object):
# First curr_id is bigger than csv_id, # First curr_id is bigger than csv_id,
# then we are not restoring but # then we are not restoring but
# extending db table with csv db table # extending db table with csv db table
if curr_id>csv_id: id_offset[self._tablename] = (curr_id-csv_id) \
id_offset[self._tablename] = curr_id-csv_id if curr_id>csv_id else 0
else:
id_offset[self._tablename] = 0
# create new id until we get the same as old_id+offset # create new id until we get the same as old_id+offset
while curr_id<csv_id+id_offset[self._tablename]: while curr_id<csv_id+id_offset[self._tablename]:
self._db(self._db[self][colnames[cid]] == curr_id).delete() self._db(self._db[self][colnames[cid]] == curr_id).delete()
@@ -10690,7 +10707,7 @@ class Rows(object):
def test_all(): def test_all():
""" """
>>> if len(sys.argv)<2: db = DAL(\"sqlite://test.db\") >>> if len(sys.argv)<2: db = DAL("sqlite://test.db")
>>> if len(sys.argv)>1: db = DAL(sys.argv[1]) >>> if len(sys.argv)>1: db = DAL(sys.argv[1])
>>> tmp = db.define_table('users',\ >>> tmp = db.define_table('users',\
Field('stringf', 'string', length=32, required=True),\ Field('stringf', 'string', length=32, required=True),\
@@ -10726,8 +10743,8 @@ def test_all():
Field('name'),\ Field('name'),\
Field('birth','date'),\ Field('birth','date'),\
migrate='test_person.table') migrate='test_person.table')
>>> person_id = db.person.insert(name=\"Marco\",birth='2005-06-22') >>> person_id = db.person.insert(name='Marco',birth='2005-06-22')
>>> person_id = db.person.insert(name=\"Massimo\",birth='1971-12-21') >>> person_id = db.person.insert(name='Massimo',birth='1971-12-21')
commented len(db().select(db.person.ALL)) commented len(db().select(db.person.ALL))
commented 2 commented 2
@@ -10753,7 +10770,7 @@ def test_all():
Update a single record Update a single record
>>> me.update_record(name=\"Max\") >>> me.update_record(name="Max")
<Row {'name': 'Max', 'birth': datetime.date(1971, 12, 21), 'id': 2}> <Row {'name': 'Max', 'birth': datetime.date(1971, 12, 21), 'id': 2}>
>>> me.name >>> me.name
'Max' 'Max'
-1
View File
@@ -10,7 +10,6 @@ License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
""" """
import logging import logging
import os
import pdb import pdb
import Queue import Queue
import sys import sys
+1 -1
View File
@@ -1,5 +1,5 @@
import codecs import codecs
import encodings
"""Caller will hand this library a buffer and ask it to either convert """Caller will hand this library a buffer and ask it to either convert
it or auto-detect the type. it or auto-detect the type.
+18 -5
View File
@@ -7,7 +7,6 @@ Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
""" """
import sys
import storage import storage
import os import os
import re import re
@@ -360,12 +359,21 @@ def get_session(request, other_application='admin'):
raise KeyError raise KeyError
try: try:
session_id = request.cookies['session_id_' + other_application].value session_id = request.cookies['session_id_' + other_application].value
osession = storage.load_storage(os.path.join( session_filename = os.path.join(
up(request.folder), other_application, 'sessions', session_id)) up(request.folder), other_application, 'sessions', session_id)
osession = storage.load_storage(session_filename)
except Exception, e: except Exception, e:
osession = storage.Storage() osession = storage.Storage()
return osession return osession
def set_session(request, session, other_application='admin'):
""" checks that user is authorized to access other_application"""
if request.application == other_application:
raise KeyError
session_id = request.cookies['session_id_' + other_application].value
session_filename = os.path.join(
up(request.folder), other_application, 'sessions', session_id)
storage.save_storage(session,session_filename)
def check_credentials(request, other_application='admin', def check_credentials(request, other_application='admin',
expiration=60 * 60, gae_login=True): expiration=60 * 60, gae_login=True):
@@ -381,9 +389,14 @@ def check_credentials(request, other_application='admin',
else: else:
return False return False
else: else:
dt = time.time() - expiration t0 = time.time()
dt = t0 - expiration
s = get_session(request, other_application) s = get_session(request, other_application)
return (s.authorized and s.last_time and s.last_time > dt) r = (s.authorized and s.last_time and s.last_time > dt)
if r:
s.last_time = t0
set_session(request,s,other_application)
return r
def fix_newlines(path): def fix_newlines(path):
+17 -9
View File
@@ -841,8 +841,8 @@ class DIV(XmlComponent):
c.latest = self.latest c.latest = self.latest
c.session = self.session c.session = self.session
c.formname = self.formname c.formname = self.formname
c['hideerror'] = hideerror or \ if not c.attributes.get('hideerror'):
self.attributes.get('hideerror', False) c['hideerror'] = hideerror or self.attributes.get('hideerror')
newstatus = c._traverse(status, hideerror) and newstatus newstatus = c._traverse(status, hideerror) and newstatus
# for input, textarea, select, option # for input, textarea, select, option
@@ -1961,7 +1961,7 @@ class FORM(DIV):
example:: example::
>>> from validators import IS_NOT_EMPTY >>> from validators import IS_NOT_EMPTY
>>> form=FORM(INPUT(_name=\"test\", requires=IS_NOT_EMPTY())) >>> form=FORM(INPUT(_name="test", requires=IS_NOT_EMPTY()))
>>> form.xml() >>> form.xml()
'<form action=\"#\" enctype=\"multipart/form-data\" method=\"post\"><input name=\"test\" type=\"text\" /></form>' '<form action=\"#\" enctype=\"multipart/form-data\" method=\"post\"><input name=\"test\" type=\"text\" /></form>'
@@ -2457,15 +2457,23 @@ class MENU(DIV):
def serialize_mobile(self, data, select=None, prefix=''): def serialize_mobile(self, data, select=None, prefix=''):
if not select: if not select:
select = SELECT(**self.attributes) select = SELECT(**self.attributes)
custom_items = []
for item in data: for item in data:
if len(item) <= 4 or item[4] == True: # Custom item aren't serialized as mobile
if len(item) >= 3 and (not item[0]) or (isinstance(item[0], DIV) and not (item[2])):
# ex: ('', False,A('title',_href=URL(...),_title="title"))
# ex: (A('title',_href=URL(...),_title="title"), False, None)
custom_items.append(item)
elif len(item) <= 4 or item[4] == True:
select.append(OPTION(CAT(prefix, item[0]), select.append(OPTION(CAT(prefix, item[0]),
_value=item[2], _selected=item[1])) _value=item[2], _selected=item[1]))
if len(item) > 3 and len(item[3]): if len(item) > 3 and len(item[3]):
self.serialize_mobile( self.serialize_mobile(
item[3], select, prefix=CAT(prefix, item[0], '/')) item[3], select, prefix=CAT(prefix, item[0], '/'))
select['_onchange'] = 'window.location=this.value' select['_onchange'] = 'window.location=this.value'
return select # avoid to wrap the select if no custom items are present
html = DIV(select, self.serialize(custom_items)) if len( custom_items) else select
return html
def xml(self): def xml(self):
if self['mobile']: if self['mobile']:
@@ -2501,11 +2509,11 @@ def test():
Example: Example:
>>> from validators import * >>> from validators import *
>>> print DIV(A('click me', _href=URL(a='a', c='b', f='c')), BR(), HR(), DIV(SPAN(\"World\"), _class='unknown')).xml() >>> print DIV(A('click me', _href=URL(a='a', c='b', f='c')), BR(), HR(), DIV(SPAN("World"), _class='unknown')).xml()
<div><a data-w2p_disable_with="default" href="/a/b/c">click me</a><br /><hr /><div class=\"unknown\"><span>World</span></div></div> <div><a data-w2p_disable_with="default" href="/a/b/c">click me</a><br /><hr /><div class=\"unknown\"><span>World</span></div></div>
>>> print DIV(UL(\"doc\",\"cat\",\"mouse\")).xml() >>> print DIV(UL("doc","cat","mouse")).xml()
<div><ul><li>doc</li><li>cat</li><li>mouse</li></ul></div> <div><ul><li>doc</li><li>cat</li><li>mouse</li></ul></div>
>>> print DIV(UL(\"doc\", LI(\"cat\", _class='feline'), 18)).xml() >>> print DIV(UL("doc", LI("cat", _class='feline'), 18)).xml()
<div><ul><li>doc</li><li class=\"feline\">cat</li><li>18</li></ul></div> <div><ul><li>doc</li><li class=\"feline\">cat</li><li>18</li></ul></div>
>>> print TABLE(['a', 'b', 'c'], TR('d', 'e', 'f'), TR(TD(1), TD(2), TD(3))).xml() >>> print TABLE(['a', 'b', 'c'], TR('d', 'e', 'f'), TR(TD(1), TD(2), TD(3))).xml()
<table><tr><td>a</td><td>b</td><td>c</td></tr><tr><td>d</td><td>e</td><td>f</td></tr><tr><td>1</td><td>2</td><td>3</td></tr></table> <table><tr><td>a</td><td>b</td><td>c</td></tr><tr><td>d</td><td>e</td><td>f</td></tr><tr><td>1</td><td>2</td><td>3</td></tr></table>
@@ -2531,7 +2539,7 @@ def test():
>>> print form.xml() >>> print form.xml()
<form action="#" enctype="multipart/form-data" method="post"><input class="invalidinput" name="myvar" type="text" value="as df" /><div class="error_wrapper"><div class="error" id="myvar__error">only alphanumeric!</div></div></form> <form action="#" enctype="multipart/form-data" method="post"><input class="invalidinput" name="myvar" type="text" value="as df" /><div class="error_wrapper"><div class="error" id="myvar__error">only alphanumeric!</div></div></form>
>>> session={} >>> session={}
>>> form=FORM(INPUT(value=\"Hello World\", _name=\"var\", requires=IS_MATCH('^\w+$'))) >>> form=FORM(INPUT(value="Hello World", _name="var", requires=IS_MATCH('^\w+$')))
>>> isinstance(form.as_dict(), dict) >>> isinstance(form.as_dict(), dict)
True True
>>> form.as_dict(flat=True).has_key("vars") >>> form.as_dict(flat=True).has_key("vars")
-1
View File
@@ -15,7 +15,6 @@ import re
import sys import sys
import pkgutil import pkgutil
import logging import logging
import marshal
from cgi import escape from cgi import escape
from threading import RLock from threading import RLock
-1
View File
@@ -14,7 +14,6 @@ Contains:
if False: import import_all # DO NOT REMOVE PART OF FREEZE PROCESS if False: import import_all # DO NOT REMOVE PART OF FREEZE PROCESS
import gc import gc
import cStringIO
import Cookie import Cookie
import os import os
import re import re
+12 -2
View File
@@ -117,6 +117,10 @@ class Token(object):
if a cron job started before 60 seconds and did not stop, if a cron job started before 60 seconds and did not stop,
a warning is issue "Stale cron.master detected" a warning is issue "Stale cron.master detected"
""" """
if sys.platform == 'win32':
locktime = 59.5
else:
locktime = 59.99
if portalocker.LOCK_EX is None: if portalocker.LOCK_EX is None:
logger.warning('WEB2PY CRON: Disabled because no file locking') logger.warning('WEB2PY CRON: Disabled because no file locking')
return None return None
@@ -128,7 +132,7 @@ class Token(object):
(start, stop) = cPickle.load(self.master) (start, stop) = cPickle.load(self.master)
except: except:
(start, stop) = (0, 1) (start, stop) = (0, 1)
if startup or self.now - start > 59.99: if startup or self.now - start > locktime:
ret = self.now ret = self.now
if not stop: if not stop:
# this happens if previous cron job longer than 1 minute # this happens if previous cron job longer than 1 minute
@@ -136,6 +140,7 @@ class Token(object):
logger.debug('WEB2PY CRON: Acquiring lock') logger.debug('WEB2PY CRON: Acquiring lock')
self.master.seek(0) self.master.seek(0)
cPickle.dump((self.now, 0), self.master) cPickle.dump((self.now, 0), self.master)
self.master.flush()
finally: finally:
portalocker.unlock(self.master) portalocker.unlock(self.master)
if not ret: if not ret:
@@ -246,6 +251,7 @@ class cronlauncher(threading.Thread):
shell=self.shell) shell=self.shell)
_cron_subprocs.append(proc) _cron_subprocs.append(proc)
(stdoutdata, stderrdata) = proc.communicate() (stdoutdata, stderrdata) = proc.communicate()
_cron_subprocs.remove(proc)
if proc.returncode != 0: if proc.returncode != 0:
logger.warning( logger.warning(
'WEB2PY CRON Call returned code %s:\n%s' % 'WEB2PY CRON Call returned code %s:\n%s' %
@@ -303,7 +309,11 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
for task in tasks: for task in tasks:
if _cron_stopping: if _cron_stopping:
break break
commands = [sys.executable] if sys.executable.lower().endswith('pythonservice.exe'):
_python_exe = os.path.join(sys.exec_prefix, 'python.exe')
else:
_python_exe = sys.executable
commands = [_python_exe]
w2p_path = fileutils.abspath('web2py.py', gluon=True) w2p_path = fileutils.abspath('web2py.py', gluon=True)
if os.path.exists(w2p_path): if os.path.exists(w2p_path):
commands.append(w2p_path) commands.append(w2p_path)
-1
View File
@@ -6,7 +6,6 @@ License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
import os import os
import sys import sys
import socket
import platform import platform
from storage import Storage from storage import Storage
+8 -8
View File
@@ -2073,14 +2073,13 @@ class SQLFORM(FORM):
order = request.vars.order or '' order = request.vars.order or ''
if sortable: if sortable:
if order and not order == 'None': if order and not order == 'None':
if order[:1] == '~': otablename, ofieldname = order.split('~')[-1].split('.', 1)
sign, rorder = '~', order[1:] sort_field = db[otablename][ofieldname]
exception = sort_field.type in ('date', 'datetime', 'time')
if exception:
orderby = (order[:1] == '~' and sort_field) or ~sort_field
else: else:
sign, rorder = '', order orderby = (order[:1] == '~' and ~sort_field) or sort_field
tablename, fieldname = rorder.split('.', 1)
orderby = db[tablename][fieldname]
if sign == '~':
orderby = ~orderby
expcolumns = [str(f) for f in columns] expcolumns = [str(f) for f in columns]
if export_type.endswith('with_hidden_cols'): if export_type.endswith('with_hidden_cols'):
@@ -2095,7 +2094,8 @@ class SQLFORM(FORM):
try: try:
dbset = dbset(SQLFORM.build_query( dbset = dbset(SQLFORM.build_query(
fields, request.vars.get('keywords', ''))) fields, request.vars.get('keywords', '')))
rows = dbset.select(cacheable=True, *expcolumns) rows = dbset.select(left=left, orderby=orderby,
cacheable=True, *expcolumns)
except Exception, e: except Exception, e:
response.flash = T('Internal Error') response.flash = T('Internal Error')
rows = [] rows = []
+2 -2
View File
@@ -845,9 +845,9 @@ def render(content="hello world",
'hello world' 'hello world'
>>> render(content='abc') >>> render(content='abc')
'abc' 'abc'
>>> render(content='abc\\'') >>> render(content='abc\'')
"abc'" "abc'"
>>> render(content='a"\\'bc') >>> render(content='a"\'bc')
'a"\\'bc' 'a"\\'bc'
>>> render(content='a\\nbc') >>> render(content='a\\nbc')
'a\\nbc' 'a\\nbc'
+109 -61
View File
@@ -1304,8 +1304,9 @@ class Auth(object):
else: else:
raise HTTP(404) raise HTTP(404)
def navbar(self, mode='Default', action=None, prefix='Welcome', def navbar(self, prefix='Welcome', action=None,
referrer_actions=DEFAULT, user_identifier=DEFAULT): separators=(' [ ', ' | ', ' ] '), user_identifier=DEFAULT,
referrer_actions=DEFAULT, mode='default'):
""" Navbar with support for more templates """ Navbar with support for more templates
This uses some code from the old navbar. This uses some code from the old navbar.
@@ -1490,21 +1491,21 @@ class Auth(object):
'bare': bare 'bare': bare
} # Define custom modes. } # Define custom modes.
try: if mode in options and callable(options[mode]):
options[mode]() options[mode]()
except KeyError: # KeyError if mode is not in options (do Default) else:
s1, s2, s3 = separators
if self.user_id: if self.user_id:
self.bar = SPAN(prefix, user_identifier, '[', self.bar = SPAN(prefix, user_identifier, s1,
Anr(items[0]['name'], Anr(items[0]['name'],
_href=items[0]['href']), ']', _href=items[0]['href']), s3,
_class='auth_navbar') _class='auth_navbar')
else: else:
self.bar = SPAN('[', Anr(items[0]['name'], self.bar = SPAN(s1, Anr(items[0]['name'],
_href=items[0]['href']), ']', _href=items[0]['href']), s3,
_class='auth_navbar') _class='auth_navbar')
del items[0]
for item in items: for item in items:
self.bar.insert(-1, ']') self.bar.insert(-1, s2)
self.bar.insert(-1, Anr(item['name'], _href=item['href'])) self.bar.insert(-1, Anr(item['name'], _href=item['href']))
return self.bar return self.bar
@@ -1522,7 +1523,8 @@ class Auth(object):
tables, tables,
archive_db=None, archive_db=None,
archive_names='%(tablename)s_archive', archive_names='%(tablename)s_archive',
current_record='current_record'): current_record='current_record',
current_record_label=None):
""" """
to enable full record versioning (including auth tables): to enable full record versioning (including auth tables):
@@ -1549,6 +1551,8 @@ class Auth(object):
does automatically. does automatically.
""" """
current_record_label = current_record_label or current.T(
current_record.replace('_',' ').title())
for table in tables: for table in tables:
fieldnames = table.fields() fieldnames = table.fields()
if ('id' in fieldnames and if ('id' in fieldnames and
@@ -1557,7 +1561,8 @@ class Auth(object):
table._enable_record_versioning( table._enable_record_versioning(
archive_db=archive_db, archive_db=archive_db,
archive_name=archive_names, archive_name=archive_names,
current_record=current_record) current_record=current_record,
current_record_label=current_record_label)
def define_signature(self): def define_signature(self):
db = self.db db = self.db
@@ -2157,12 +2162,17 @@ class Auth(object):
elif 'username' in table_user.fields: elif 'username' in table_user.fields:
username = 'username' username = 'username'
else: else:
username = 'email' username = 'email'
settings = self.settings
if 'username' in table_user.fields or \ if 'username' in table_user.fields or \
not self.settings.login_email_validate: not settings.login_email_validate:
tmpvalidator = IS_NOT_EMPTY(error_message=self.messages.is_empty) tmpvalidator = IS_NOT_EMPTY(error_message=self.messages.is_empty)
if not settings.username_case_sensitive:
tmpvalidator = [IS_LOWER(), tmpvalidator]
else: else:
tmpvalidator = IS_EMAIL(error_message=self.messages.invalid_email) tmpvalidator = IS_EMAIL(error_message=self.messages.invalid_email)
if not settings.email_case_sensitive:
tmpvalidator = [IS_LOWER(), tmpvalidator]
old_requires = table_user[username].requires old_requires = table_user[username].requires
table_user[username].requires = tmpvalidator table_user[username].requires = tmpvalidator
@@ -2170,7 +2180,7 @@ class Auth(object):
response = current.response response = current.response
session = current.session session = current.session
passfield = self.settings.password_field passfield = settings.password_field
try: try:
table_user[passfield].requires[-1].min_length = 0 table_user[passfield].requires[-1].min_length = 0
except: except:
@@ -2186,43 +2196,43 @@ class Auth(object):
if next is DEFAULT: if next is DEFAULT:
# important for security # important for security
next = self.settings.login_next next = settings.login_next
user_next = snext user_next = snext
if user_next: if user_next:
external = user_next.split('://') external = user_next.split('://')
if external[0].lower() in ['http', 'https', 'ftp']: if external[0].lower() in ['http', 'https', 'ftp']:
host_next = user_next.split('//', 1)[-1].split('/')[0] host_next = user_next.split('//', 1)[-1].split('/')[0]
if host_next in self.settings.cas_domains: if host_next in settings.cas_domains:
next = user_next next = user_next
else: else:
next = user_next next = user_next
if onvalidation is DEFAULT: if onvalidation is DEFAULT:
onvalidation = self.settings.login_onvalidation onvalidation = settings.login_onvalidation
if onaccept is DEFAULT: if onaccept is DEFAULT:
onaccept = self.settings.login_onaccept onaccept = settings.login_onaccept
if log is DEFAULT: if log is DEFAULT:
log = self.messages['login_log'] log = self.messages['login_log']
onfail = self.settings.login_onfail onfail = settings.login_onfail
user = None # default user = None # default
# do we use our own login form, or from a central source? # do we use our own login form, or from a central source?
if self.settings.login_form == self: if settings.login_form == self:
form = SQLFORM( form = SQLFORM(
table_user, table_user,
fields=[username, passfield], fields=[username, passfield],
hidden=dict(_next=next), hidden=dict(_next=next),
showid=self.settings.showid, showid=settings.showid,
submit_button=self.messages.login_button, submit_button=self.messages.login_button,
delete_label=self.messages.delete_label, delete_label=self.messages.delete_label,
formstyle=self.settings.formstyle, formstyle=settings.formstyle,
separator=self.settings.label_separator separator=settings.label_separator
) )
if self.settings.remember_me_form: if settings.remember_me_form:
## adds a new input checkbox "remember me for longer" ## adds a new input checkbox "remember me for longer"
if self.settings.formstyle != 'bootstrap': if settings.formstyle != 'bootstrap':
addrow(form, XML("&nbsp;"), addrow(form, XML("&nbsp;"),
DIV(XML("&nbsp;"), DIV(XML("&nbsp;"),
INPUT(_type='checkbox', INPUT(_type='checkbox',
@@ -2235,9 +2245,9 @@ class Auth(object):
self.messages.label_remember_me, self.messages.label_remember_me,
_for="auth_user_remember", _for="auth_user_remember",
)), "", )), "",
self.settings.formstyle, settings.formstyle,
'auth_user_remember__row') 'auth_user_remember__row')
elif self.settings.formstyle == 'bootstrap': elif settings.formstyle == 'bootstrap':
addrow(form, addrow(form,
"", "",
LABEL( LABEL(
@@ -2247,20 +2257,20 @@ class Auth(object):
self.messages.label_remember_me, self.messages.label_remember_me,
_class="checkbox"), _class="checkbox"),
"", "",
self.settings.formstyle, settings.formstyle,
'auth_user_remember__row') 'auth_user_remember__row')
captcha = self.settings.login_captcha or \ captcha = settings.login_captcha or \
(self.settings.login_captcha != False and self.settings.captcha) (settings.login_captcha != False and settings.captcha)
if captcha: if captcha:
addrow(form, captcha.label, captcha, captcha.comment, addrow(form, captcha.label, captcha, captcha.comment,
self.settings.formstyle, 'captcha__row') settings.formstyle, 'captcha__row')
accepted_form = False accepted_form = False
if form.accepts(request, session, if form.accepts(request, session,
formname='login', dbio=False, formname='login', dbio=False,
onvalidation=onvalidation, onvalidation=onvalidation,
hideerror=self.settings.hideerror): hideerror=settings.hideerror):
accepted_form = True accepted_form = True
# check for username in db # check for username in db
@@ -2282,36 +2292,36 @@ class Auth(object):
# try alternate logins 1st as these have the # try alternate logins 1st as these have the
# current version of the password # current version of the password
user = None user = None
for login_method in self.settings.login_methods: for login_method in settings.login_methods:
if login_method != self and \ if login_method != self and \
login_method(request.vars[username], login_method(request.vars[username],
request.vars[passfield]): request.vars[passfield]):
if not self in self.settings.login_methods: if not self in settings.login_methods:
# do not store password in db # do not store password in db
form.vars[passfield] = None form.vars[passfield] = None
user = self.get_or_create_user( user = self.get_or_create_user(
form.vars, self.settings.update_fields) form.vars, settings.update_fields)
break break
if not user: if not user:
# alternates have failed, maybe because service inaccessible # alternates have failed, maybe because service inaccessible
if self.settings.login_methods[0] == self: if settings.login_methods[0] == self:
# try logging in locally using cached credentials # try logging in locally using cached credentials
if form.vars.get(passfield, '') == temp_user[passfield]: if form.vars.get(passfield, '') == temp_user[passfield]:
# success # success
user = temp_user user = temp_user
else: else:
# user not in db # user not in db
if not self.settings.alternate_requires_registration: if not settings.alternate_requires_registration:
# we're allowed to auto-register users from external systems # we're allowed to auto-register users from external systems
for login_method in self.settings.login_methods: for login_method in settings.login_methods:
if login_method != self and \ if login_method != self and \
login_method(request.vars[username], login_method(request.vars[username],
request.vars[passfield]): request.vars[passfield]):
if not self in self.settings.login_methods: if not self in settings.login_methods:
# do not store password in db # do not store password in db
form.vars[passfield] = None form.vars[passfield] = None
user = self.get_or_create_user( user = self.get_or_create_user(
form.vars, self.settings.update_fields) form.vars, settings.update_fields)
break break
if not user: if not user:
self.log_event(self.messages['login_failed_log'], self.log_event(self.messages['login_failed_log'],
@@ -2321,25 +2331,25 @@ class Auth(object):
callback(onfail, None) callback(onfail, None)
redirect( redirect(
self.url(args=request.args, vars=request.get_vars), self.url(args=request.args, vars=request.get_vars),
client_side=self.settings.client_side) client_side=settings.client_side)
else: else:
# use a central authentication server # use a central authentication server
cas = self.settings.login_form cas = settings.login_form
cas_user = cas.get_user() cas_user = cas.get_user()
if cas_user: if cas_user:
cas_user[passfield] = None cas_user[passfield] = None
user = self.get_or_create_user( user = self.get_or_create_user(
table_user._filter_fields(cas_user), table_user._filter_fields(cas_user),
self.settings.update_fields) settings.update_fields)
elif hasattr(cas, 'login_form'): elif hasattr(cas, 'login_form'):
return cas.login_form() return cas.login_form()
else: else:
# we need to pass through login again before going on # we need to pass through login again before going on
next = self.url(self.settings.function, args='login') next = self.url(settings.function, args='login')
redirect(cas.login_url(next), redirect(cas.login_url(next),
client_side=self.settings.client_side) client_side=settings.client_side)
# process authenticated users # process authenticated users
if user: if user:
@@ -2349,20 +2359,20 @@ class Auth(object):
self.login_user(user) self.login_user(user)
session.auth.expiration = \ session.auth.expiration = \
request.vars.get('remember', False) and \ request.vars.get('remember', False) and \
self.settings.long_expiration or \ settings.long_expiration or \
self.settings.expiration settings.expiration
session.auth.remember = 'remember' in request.vars session.auth.remember = 'remember' in request.vars
self.log_event(log, user) self.log_event(log, user)
session.flash = self.messages.logged_in session.flash = self.messages.logged_in
# how to continue # how to continue
if self.settings.login_form == self: if settings.login_form == self:
if accepted_form: if accepted_form:
callback(onaccept, form) callback(onaccept, form)
if next == session._auth_next: if next == session._auth_next:
session._auth_next = None session._auth_next = None
next = replace_id(next, form) next = replace_id(next, form)
redirect(next, client_side=self.settings.client_side) redirect(next, client_side=settings.client_side)
table_user[username].requires = old_requires table_user[username].requires = old_requires
return form return form
@@ -2371,7 +2381,7 @@ class Auth(object):
if next == session._auth_next: if next == session._auth_next:
del session._auth_next del session._auth_next
redirect(next, client_side=self.settings.client_side) redirect(next, client_side=settings.client_side)
def logout(self, next=DEFAULT, onlogout=DEFAULT, log=DEFAULT): def logout(self, next=DEFAULT, onlogout=DEFAULT, log=DEFAULT):
""" """
@@ -3584,7 +3594,8 @@ class Auth(object):
templates=None, templates=None,
migrate=True, migrate=True,
controller=None, controller=None,
function=None): function=None,
force_render=False):
if controller and function: resolve = False if controller and function: resolve = False
@@ -3608,7 +3619,7 @@ class Auth(object):
if resolve: if resolve:
action = str(current.request.args(0)).startswith("_") action = str(current.request.args(0)).startswith("_")
if slug and not action: if slug and not action:
wiki = self._wiki.read(slug) wiki = self._wiki.read(slug,force_render)
if isinstance(wiki, dict) and wiki.has_key('content'): if isinstance(wiki, dict) and wiki.has_key('content'):
# We don't want to return a dict object, just the wiki # We don't want to return a dict object, just the wiki
wiki = wiki['content'] wiki = wiki['content']
@@ -5189,13 +5200,18 @@ class Wiki(object):
controller, function, args = items[0], items[1], items[2:] controller, function, args = items[0], items[1], items[2:]
return LOAD(controller, function, args=args, ajax=True).xml() return LOAD(controller, function, args=args, ajax=True).xml()
def get_render(self): def get_renderer(self):
if isinstance(self.settings.render, basestring): if isinstance(self.settings.render, basestring):
r = getattr(self, "%s_render" % self.settings.render) r = getattr(self, "%s_render" % self.settings.render)
elif callable(self.settings.render): elif callable(self.settings.render):
r = self.settings.render r = self.settings.render
elif isinstance(self.settings.render, dict):
return lambda page: self.settings.render.get(page.render,
getattr(self,
"%s_render" % (page.render or 'markmin')))(page)
else: else:
raise ValueError("Invalid render type %s" % type(render)) raise ValueError(
"Invalid render type %s" % type(self.settings.render))
return r return r
def __init__(self, auth, env=None, render='markmin', def __init__(self, auth, env=None, render='markmin',
@@ -5206,7 +5222,24 @@ class Wiki(object):
settings = self.settings = auth.settings.wiki settings = self.settings = auth.settings.wiki
# render: "markmin", "html", ..., <function> """render argument options:
- "markmin"
- "html"
- <function>
Sets a custom render function
- dict(html=<function>, markmin=...):
dict(...) allows multiple custom render functions
- "multiple"
Is the same as {}. It enables per-record formats
using builtins
"""
engines = set(['markmin', 'html'])
show_engine = False
if render == "multiple":
render = {}
if isinstance(render, dict):
[engines.add(key) for key in render]
show_engine = True
settings.render = render settings.render = render
perms = settings.manage_permissions = manage_permissions perms = settings.manage_permissions = manage_permissions
@@ -5250,8 +5283,13 @@ class Wiki(object):
default=[Wiki.everybody]), default=[Wiki.everybody]),
Field('changelog'), Field('changelog'),
Field('html', 'text', Field('html', 'text',
compute=self.get_render(), compute=self.get_renderer(),
readable=False, writable=False), readable=False, writable=False),
Field('render', default="markmin",
readable=show_engine,
writable=show_engine,
requires=IS_EMPTY_OR(
IS_IN_SET(engines))),
auth.signature], auth.signature],
'vars':{'format':'%(title)s', 'migrate':migrate}}), 'vars':{'format':'%(title)s', 'migrate':migrate}}),
('wiki_tag', { ('wiki_tag', {
@@ -5403,7 +5441,7 @@ class Wiki(object):
elif zero == '_cloud': elif zero == '_cloud':
return self.cloud() return self.cloud()
elif zero == '_preview': elif zero == '_preview':
return self.preview(self.get_render()) return self.preview(self.get_renderer())
def first_paragraph(self, page): def first_paragraph(self, page):
if not self.can_read(page): if not self.can_read(page):
@@ -5417,7 +5455,7 @@ class Wiki(object):
def fix_hostname(self, body): def fix_hostname(self, body):
return (body or '').replace('://HOSTNAME', '://%s' % self.host) return (body or '').replace('://HOSTNAME', '://%s' % self.host)
def read(self, slug): def read(self, slug, force_render=False):
if slug in '_cloud': if slug in '_cloud':
return self.cloud() return self.cloud()
elif slug in '_search': elif slug in '_search':
@@ -5432,10 +5470,12 @@ class Wiki(object):
url = URL(args=('_edit', slug)) url = URL(args=('_edit', slug))
return dict(content=A('Create page "%s"' % slug, _href=url, _class="btn")) return dict(content=A('Create page "%s"' % slug, _href=url, _class="btn"))
else: else:
html = page.html if not force_render else self.get_renderer(page)
content = XML(self.fix_hostname(html))
return dict(title=page.title, return dict(title=page.title,
slug=page.slug, slug=page.slug,
page=page, page=page,
content=XML(self.fix_hostname(page.html)), content=content,
tags=page.tags, tags=page.tags,
created_on=page.created_on, created_on=page.created_on,
modified_on=page.modified_on) modified_on=page.modified_on)
@@ -5524,7 +5564,11 @@ class Wiki(object):
if (prevbutton.hasClass('nopreview')) { if (prevbutton.hasClass('nopreview')) {
prevbutton.addClass('preview').removeClass( prevbutton.addClass('preview').removeClass(
'nopreview').html('Edit Source'); 'nopreview').html('Edit Source');
web2py_ajax_page('post', '%(url)s', {body : jQuery('#wiki_page_body').val()}, 'preview'); try{var wiki_render = jQuery('#wiki_page_render').val()}
catch(e){var wiki_render = null;}
web2py_ajax_page('post', \
'%(url)s', {body: jQuery('#wiki_page_body').val(), \
render: wiki_render}, 'preview');
form.fadeOut('fast', function() {preview.fadeIn()}); form.fadeOut('fast', function() {preview.fadeIn()});
} else { } else {
prevbutton.addClass( prevbutton.addClass(
@@ -5798,6 +5842,10 @@ class Wiki(object):
def preview(self, render): def preview(self, render):
request = current.request request = current.request
# FIXME: This is an ugly hack to ensure a default render
# engine if not specified (with multiple render engines)
if not "render" in request.post_vars:
request.post_vars.render = None
return render(request.post_vars) return render(request.post_vars)
+1 -3
View File
@@ -11,8 +11,6 @@ This file specifically includes utilities for security.
import threading import threading
import struct import struct
#import hashlib
#import hmac
import uuid import uuid
import random import random
import time import time
@@ -23,7 +21,7 @@ import logging
import socket import socket
import base64 import base64
import zlib import zlib
from types import ModuleType
_struct_2_long_long = struct.Struct('=QQ') _struct_2_long_long = struct.Struct('=QQ')
+53 -15
View File
@@ -36,6 +36,7 @@ class Service(win32serviceutil.ServiceFramework):
_svc_name_ = '_unNamed' _svc_name_ = '_unNamed'
_svc_display_name_ = '_Service Template' _svc_display_name_ = '_Service Template'
_svc_description_ = '_Service Template description'
def __init__(self, *args): def __init__(self, *args):
win32serviceutil.ServiceFramework.__init__(self, *args) win32serviceutil.ServiceFramework.__init__(self, *args)
@@ -44,6 +45,12 @@ class Service(win32serviceutil.ServiceFramework):
def log(self, msg): def log(self, msg):
servicemanager.LogInfoMsg(str(msg)) servicemanager.LogInfoMsg(str(msg))
def log_error(self, msg):
"""
Log an error message to windows application event log.
"""
servicemanager.LogErrorMsg(str(msg))
def SvcDoRun(self): def SvcDoRun(self):
self.ReportServiceStatus(win32service.SERVICE_START_PENDING) self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
try: try:
@@ -80,6 +87,7 @@ class Web2pyService(Service):
_svc_name_ = 'web2py' _svc_name_ = 'web2py'
_svc_display_name_ = 'web2py Service' _svc_display_name_ = 'web2py Service'
_svc_description_ = 'Web2py application framework service'
_exe_args_ = 'options' _exe_args_ = 'options'
server = None server = None
@@ -132,17 +140,16 @@ class Web2pyService(Service):
server_name=options.server_name, server_name=options.server_name,
request_queue_size=options.request_queue_size, request_queue_size=options.request_queue_size,
timeout=options.timeout, timeout=options.timeout,
socket_timeout=options.socket_timeout,
shutdown_timeout=options.shutdown_timeout, shutdown_timeout=options.shutdown_timeout,
path=options.folder path=options.folder,
interfaces=options.interfaces
) )
try: try:
from rewrite import load from rewrite import load
load() load()
self.server.start() self.server.start()
except: except:
# self.server.stop()
self.server = None self.server = None
raise raise
@@ -159,12 +166,18 @@ class Web2pyCronService(Web2pyService):
_svc_name_ = 'web2py_cron' _svc_name_ = 'web2py_cron'
_svc_display_name_ = 'web2py Cron Service' _svc_display_name_ = 'web2py Cron Service'
_svc_description_ = 'web2py Windows cron service for scheduled tasks'
_exe_args_ = 'options' _exe_args_ = 'options'
def start(self): def start(self):
import newcron import newcron
import global_settings import logging
self.log('web2py server starting') import logging.config
from settings import global_settings
from fileutils import abspath
from os.path import exists, join
self.log('web2py Cron service starting')
if not self.chdir(): if not self.chdir():
return return
if len(sys.argv) == 2: if len(sys.argv) == 2:
@@ -172,25 +185,50 @@ class Web2pyCronService(Web2pyService):
else: else:
opt_mod = self._exe_args_ opt_mod = self._exe_args_
options = __import__(opt_mod, [], [], '') options = __import__(opt_mod, [], [], '')
global_settings.global_settings.web2py_crontype = 'external' logpath = abspath(join(options.folder, "logging.conf"))
if exists(logpath):
logging.config.fileConfig(logpath)
else:
logging.basicConfig()
logger = logging.getLogger("web2py.cron")
global_settings.web2py_crontype = 'external'
if options.scheduler: # -K if options.scheduler: # -K
apps = [app.strip() for app in options.scheduler.split( apps = [app.strip() for app in options.scheduler.split(
',') if check_existent_app(options, app.strip())] ',') if check_existent_app(options, app.strip())]
else: else:
apps = None apps = None
self.extcron = newcron.extcron(options.folder, apps=apps)
try: misfire_gracetime = float(options.misfire_gracetime) if 'misfire_gracetime' in dir(options) else 0.0
self.extcron.start() logger.info('Starting Window cron service with %0.2f secs gracetime.' % misfire_gracetime)
except: self._started = True
# self.server.stop() wait_full_min = lambda: 60 - time.time() % 60
self.extcron = None while True:
raise try:
if wait_full_min() >= misfire_gracetime: # an offset of max. 5 secs before full minute (e.g. time.sleep(60) == 58.99 secs)
self.extcron = newcron.extcron(options.folder, apps=apps)
self.extcron.start()
time.sleep(wait_full_min())
else:
logger.debug('time.sleep() offset detected: %0.3f s' % wait_full_min())
while wait_full_min() <= misfire_gracetime:
pass
if apps != None:
break
if not self._started:
break
except Exception, ex:
self.extcron = None
self.log_error('%s, restarting service.' % ex)
logger.exception('Exception! Restarting Windows cron service.' % ex)
self.start()
def stop(self): def stop(self):
self.log('web2py cron stopping') self.log('web2py Cron service stopping')
if not self.chdir(): if not self.chdir():
return return
if self.extcron: if self.extcron:
self._started = False
self.extcron.join() self.extcron.join()
def register_service_handler(argv=None, opt_file='options', cls=Web2pyService): def register_service_handler(argv=None, opt_file='options', cls=Web2pyService):
@@ -453,10 +453,9 @@ cd /home/www-data
wget http://web2py.com/examples/static/web2py_src.zip wget http://web2py.com/examples/static/web2py_src.zip
unzip web2py_src.zip unzip web2py_src.zip
rm web2py_src.zip rm web2py_src.zip
# Download latest version of sessions2trash.py
wget http://web2py.googlecode.com/hg/scripts/sessions2trash.py -O /home/www-data/web2py/scripts/sessions2trash.py
chown -R www-data:www-data web2py chown -R www-data:www-data web2py
cd /home/www-data/web2py cd /home/www-data/web2py
mv handlers/wsgihandler.py wsgihandler.py
sudo -u www-data python -c "from gluon.main import save_password; save_password('$PW',443)" sudo -u www-data python -c "from gluon.main import save_password; save_password('$PW',443)"
/etc/init.d/redmine start /etc/init.d/redmine start
start uwsgi-emperor start uwsgi-emperor
+1
View File
@@ -159,6 +159,7 @@ fi
wget http://web2py.com/examples/static/web2py_src.zip wget http://web2py.com/examples/static/web2py_src.zip
unzip web2py_src.zip unzip web2py_src.zip
mv web2py/handlers/wsgihandler.py web2py/wsgihandler.py
chown -R apache:apache web2py chown -R apache:apache web2py
### ###
+1
View File
@@ -159,6 +159,7 @@ fi
wget http://web2py.com/examples/static/web2py_src.zip wget http://web2py.com/examples/static/web2py_src.zip
unzip web2py_src.zip unzip web2py_src.zip
mv web2py/handlers/wsgihandler.py web2py/wsgihandler.py
chown -R apache:apache web2py chown -R apache:apache web2py
### ###
+43 -43
View File
@@ -3,53 +3,53 @@
echo 'setup-web2py-nginx-uwsgi-centos64.sh' echo 'setup-web2py-nginx-uwsgi-centos64.sh'
echo 'Support CentOS 6.4' echo 'Support CentOS 6.4'
echo 'Installs Nginx 1.4.1 + uWSGI + Web2py' echo 'Installs Nginx 1.4.1 + uWSGI + Web2py'
# Get Web2py Admin Password # Get Web2py Admin Password
echo -e "Web2py Admin Password: \c " echo -e "Web2py Admin Password: \c "
read PW read PW
echo -e "Set Server Name Ex: web2py.domain.com : \c " echo -e "Set Server Name Ex: web2py.domain.com : \c "
read SERVER_FQDN read SERVER_FQDN
echo -e "Set Server IP: \c " echo -e "Set Server IP: \c "
read SERVER_IP read SERVER_IP
echo "" >>/etc/hosts echo "" >>/etc/hosts
echo "$SERVER_IP $SERVER_FQDN" >>/etc/hosts echo "$SERVER_IP $SERVER_FQDN" >>/etc/hosts
yum update -y yum update -y
yum install -y http://mirror-fpt-telecom.fpt.net/fedora/epel/6/i386/epel-release-6-8.noarch.rpm yum install -y http://mirror-fpt-telecom.fpt.net/fedora/epel/6/i386/epel-release-6-8.noarch.rpm
yum clean all yum clean all
yum install -y gcc libxml2-devel python-devel python-pip PyXML unzip make sudo yum install -y gcc libxml2-devel python-devel python-pip PyXML unzip make sudo
## 64Bits System ## 64Bits System
## yum install -y http://nginx.org/packages/rhel/6/x86_64/RPMS/nginx-1.4.1-1.el6.ngx.x86_64.rpm ## yum install -y http://nginx.org/packages/rhel/6/x86_64/RPMS/nginx-1.4.1-1.el6.ngx.x86_64.rpm
yum install -y http://nginx.org/packages/rhel/6/i386/RPMS/nginx-1.4.1-1.el6.ngx.i386.rpm yum install -y http://nginx.org/packages/rhel/6/i386/RPMS/nginx-1.4.1-1.el6.ngx.i386.rpm
pip-python install --upgrade pip pip-python install --upgrade pip
PIPPATH=`which pip` PIPPATH=`which pip`
$PIPPATH install --upgrade uwsgi $PIPPATH install --upgrade uwsgi
# Prepare folders for uwsgi # Prepare folders for uwsgi
mkdir /etc/uwsgi mkdir /etc/uwsgi
mkdir /var/log/uwsgi mkdir /var/log/uwsgi
mkdir -p /var/www/ mkdir -p /var/www/
#usermod -a -G apache nginx #usermod -a -G apache nginx
mkdir -p /etc/nginx/ssl/ mkdir -p /etc/nginx/ssl/
cd /etc/nginx/ssl cd /etc/nginx/ssl
openssl genrsa 1024 > web2py.key && chmod 400 web2py.key openssl genrsa 1024 > web2py.key && chmod 400 web2py.key
openssl req -new -x509 -nodes -sha1 -days 1780 -key web2py.key > web2py.crt openssl req -new -x509 -nodes -sha1 -days 1780 -key web2py.key > web2py.crt
openssl x509 -noout -fingerprint -text < web2py.crt > web2py.info openssl x509 -noout -fingerprint -text < web2py.crt > web2py.info
echo 'server { echo 'server {
listen YOUR_SERVER_IP:80; listen YOUR_SERVER_IP:80;
server_name YOUR_SERVER_FQDN; server_name YOUR_SERVER_FQDN;
@@ -97,13 +97,13 @@ server {
#client_max_body_size 10m; #client_max_body_size 10m;
### ###
} }
}' >/etc/nginx/conf.d/web2py.conf }' >/etc/nginx/conf.d/web2py.conf
sed -i "s/YOUR_SERVER_IP/$SERVER_IP/" /etc/nginx/conf.d/web2py.conf sed -i "s/YOUR_SERVER_IP/$SERVER_IP/" /etc/nginx/conf.d/web2py.conf
sed -i "s/YOUR_SERVER_FQDN/$SERVER_FQDN/" /etc/nginx/conf.d/web2py.conf sed -i "s/YOUR_SERVER_FQDN/$SERVER_FQDN/" /etc/nginx/conf.d/web2py.conf
# Create configuration file /etc/uwsgi/web2py.ini # Create configuration file /etc/uwsgi/web2py.ini
echo '[uwsgi] echo '[uwsgi]
@@ -126,21 +126,21 @@ cron = 0 0 -1 -1 -1 python /var/www/web2py/web2py.py -Q -S welcome -M -R scripts
no-orphans = true no-orphans = true
chmod-socket = 666 chmod-socket = 666
' >/etc/uwsgi/web2py.ini ' >/etc/uwsgi/web2py.ini
cd /var/www/ cd /var/www/
curl --progress -O http://web2py.com/examples/static/web2py_src.zip curl --progress -O http://web2py.com/examples/static/web2py_src.zip
unzip web2py_src.zip && rm -rf web2py_src.zip unzip web2py_src.zip && rm -rf web2py_src.zip
# Download latest version of sessions2trash.py # Download latest version of sessions2trash.py
curl --output /var/www/web2py/scripts/sessions2trash.py http://web2py.googlecode.com/hg/scripts/sessions2trash.py mv web2py/handlers/wsgihandler.py web2py/wsgihandler.py
chown -R nginx:nginx web2py chown -R nginx:nginx web2py
cd /var/www/web2py cd /var/www/web2py
sudo -u nginx python -c "from gluon.main import save_password; save_password('$PW',443)" sudo -u nginx python -c "from gluon.main import save_password; save_password('$PW',443)"
## Daemons /start/stop ## Daemons /start/stop
echo '#!/bin/sh echo '#!/bin/sh
# Autor: Nilton OS -- www.linuxpro.com.br # Autor: Nilton OS -- www.linuxpro.com.br
# #
@@ -154,27 +154,27 @@ echo '#!/bin/sh
# Default-Start: 3 5 # Default-Start: 3 5
# Default-Stop: 0 1 2 6 # Default-Stop: 0 1 2 6
### END INIT INFO ### END INIT INFO
# Source function library. # Source function library.
. /etc/rc.d/init.d/functions . /etc/rc.d/init.d/functions
# Check for missing binaries (stale symlinks should not happen) # Check for missing binaries (stale symlinks should not happen)
UWSGI_BIN=`which uwsgi` UWSGI_BIN=`which uwsgi`
test -x $UWSGI_BIN || { echo "$UWSGI_BIN not installed"; test -x $UWSGI_BIN || { echo "$UWSGI_BIN not installed";
if [ "$1" = "stop" ]; then exit 0; if [ "$1" = "stop" ]; then exit 0;
else exit 5; fi; } else exit 5; fi; }
UWSGI_EMPEROR_MODE=true UWSGI_EMPEROR_MODE=true
UWSGI_VASSALS="/etc/uwsgi/" UWSGI_VASSALS="/etc/uwsgi/"
UWSGI_OPTIONS="--enable-threads --logto /var/log/uwsgi/uwsgi.log" UWSGI_OPTIONS="--enable-threads --logto /var/log/uwsgi/uwsgi.log"
lockfile=/var/lock/subsys/uwsgi lockfile=/var/lock/subsys/uwsgi
UWSGI_OPTIONS="$UWSGI_OPTIONS --autoload" UWSGI_OPTIONS="$UWSGI_OPTIONS --autoload"
if [ "$UWSGI_EMPEROR_MODE" = "true" ] ; then if [ "$UWSGI_EMPEROR_MODE" = "true" ] ; then
UWSGI_OPTIONS="$UWSGI_OPTIONS --emperor $UWSGI_VASSALS" UWSGI_OPTIONS="$UWSGI_OPTIONS --emperor $UWSGI_VASSALS"
fi fi
case "$1" in case "$1" in
start) start)
echo -n "Starting uWSGI " echo -n "Starting uWSGI "
@@ -198,19 +198,19 @@ case "$1" in
;; ;;
esac esac
exit 0 '> /etc/init.d/uwsgi exit 0 '> /etc/init.d/uwsgi
chmod +x /etc/init.d/uwsgi chmod +x /etc/init.d/uwsgi
/etc/init.d/uwsgi start /etc/init.d/uwsgi start
/etc/init.d/nginx start /etc/init.d/nginx start
/etc/init.d/iptables stop /etc/init.d/iptables stop
chkconfig --del iptables chkconfig --del iptables
chkconfig --levels 235 uwsgi on chkconfig --levels 235 uwsgi on
chkconfig --levels 235 nginx on chkconfig --levels 235 nginx on
## you can reload uwsgi with ## you can reload uwsgi with
#/etc/init.d/uwsgi restart #/etc/init.d/uwsgi restart
## to reload web2py only (without restarting uwsgi) ## to reload web2py only (without restarting uwsgi)
# touch /etc/uwsgi/web2py.ini # touch /etc/uwsgi/web2py.ini
@@ -122,6 +122,7 @@ mkdir ./web-apps
cd ./web-apps cd ./web-apps
curl -O http://www.web2py.com/examples/static/web2py_src.zip curl -O http://www.web2py.com/examples/static/web2py_src.zip
unzip web2py_src.zip unzip web2py_src.zip
mv web2py/handlers/wsgihandler.py web2py/wsgihandler.py
echo "Set the ownership for web2py application to uwsgi" echo "Set the ownership for web2py application to uwsgi"
chown -R uwsgi /opt/web-apps/web2py chown -R uwsgi /opt/web-apps/web2py
+11 -12
View File
@@ -71,7 +71,7 @@ server {
uwsgi_param UWSGI_SCHEME $scheme; uwsgi_param UWSGI_SCHEME $scheme;
uwsgi_param SERVER_SOFTWARE nginx/$nginx_version; uwsgi_param SERVER_SOFTWARE nginx/$nginx_version;
} }
}' >/etc/nginx/vhosts.d/web2py.conf }' >/etc/nginx/vhosts.d/web2py.conf
@@ -102,8 +102,7 @@ cd /srv/www/
wget http://web2py.com/examples/static/web2py_src.zip wget http://web2py.com/examples/static/web2py_src.zip
unzip web2py_src.zip unzip web2py_src.zip
rm web2py_src.zip rm web2py_src.zip
# Download latest version of sessions2trash.py mv web2py/handlers/wsgihandler.py web2py/wsgihandler.py
wget http://web2py.googlecode.com/hg/scripts/sessions2trash.py -O /srv/www/web2py/scripts/sessions2trash.py
chown -R nginx:www web2py chown -R nginx:www web2py
cd /srv/www/web2py cd /srv/www/web2py
sudo -u nginx python -c "from gluon.main import save_password; save_password('$PW',443)" sudo -u nginx python -c "from gluon.main import save_password; save_password('$PW',443)"
@@ -137,7 +136,7 @@ echo '#!/bin/sh
# Check for missing binaries (stale symlinks should not happen) # Check for missing binaries (stale symlinks should not happen)
UWSGI_BIN=/usr/bin/uwsgi UWSGI_BIN=/usr/bin/uwsgi
test -x $UWSGI_BIN || { echo "$UWSGI_BIN not installed"; test -x $UWSGI_BIN || { echo "$UWSGI_BIN not installed";
if [ "$1" = "stop" ]; then exit 0; if [ "$1" = "stop" ]; then exit 0;
else exit 5; fi; } else exit 5; fi; }
@@ -150,13 +149,13 @@ UWSGI_OPTIONS="$UWSGI_OPTIONS --autoload"
if [ "$UWSGI_EMPEROR_MODE" = "true" ] ; then if [ "$UWSGI_EMPEROR_MODE" = "true" ] ; then
UWSGI_OPTIONS="$UWSGI_OPTIONS --emperor $UWSGI_VASSALS" UWSGI_OPTIONS="$UWSGI_OPTIONS --emperor $UWSGI_VASSALS"
fi fi
. /etc/rc.status . /etc/rc.status
rc_reset rc_reset
case "$1" in case "$1" in
start) start)
echo -n "Starting uWSGI " echo -n "Starting uWSGI "
/sbin/startproc $UWSGI_BIN $UWSGI_OPTIONS /sbin/startproc $UWSGI_BIN $UWSGI_OPTIONS
@@ -219,7 +218,7 @@ chmod +x /etc/init.d/uwsgi
chkconfig --add uwsgi chkconfig --add uwsgi
chkconfig --add nginx chkconfig --add nginx
## you can reload uwsgi with ## you can reload uwsgi with
#/etc/init.d/uwsgi restart #/etc/init.d/uwsgi restart
## to reload web2py only (without restarting uwsgi) ## to reload web2py only (without restarting uwsgi)
+1 -2
View File
@@ -169,9 +169,8 @@ mkdir /home/www-data
cd /home/www-data cd /home/www-data
wget http://web2py.com/examples/static/web2py_src.zip wget http://web2py.com/examples/static/web2py_src.zip
unzip web2py_src.zip unzip web2py_src.zip
mv web2py/handlers/wsgihandler.py web2py/wsgihandler.py
rm web2py_src.zip rm web2py_src.zip
# Download latest version of sessions2trash.py
wget http://web2py.googlecode.com/hg/scripts/sessions2trash.py -O /home/www-data/web2py/scripts/sessions2trash.py
chown -R www-data:www-data web2py chown -R www-data:www-data web2py
cd /home/www-data/web2py cd /home/www-data/web2py
sudo -u www-data python -c "from gluon.main import save_password; save_password('$PW',443)" sudo -u www-data python -c "from gluon.main import save_password; save_password('$PW',443)"
+1
View File
@@ -59,6 +59,7 @@ cd www-data
rm web2py_src.zip* rm web2py_src.zip*
wget http://web2py.com/examples/static/web2py_src.zip wget http://web2py.com/examples/static/web2py_src.zip
unzip web2py_src.zip unzip web2py_src.zip
mv web2py/handlers/wsgihandler.py web2py/wsgihandler.py
chown -R www-data:www-data web2py chown -R www-data:www-data web2py
echo "setting up apache modules" echo "setting up apache modules"