Merge ssh://github.com/web2py/web2py
This commit is contained in:
+6
-6
@@ -6,17 +6,17 @@ python:
|
||||
- '2.7'
|
||||
- 'pypy'
|
||||
install:
|
||||
- pip install -e . --use-mirrors
|
||||
- pip install -e .
|
||||
env:
|
||||
- DB=sqlite:memory
|
||||
- DB=mysql://root:@localhost/test_w2p
|
||||
- DB=postgres://postgres:@localhost/test_w2p
|
||||
before_script:
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != '2.7' ]]; then pip install --use-mirrors unittest2; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --use-mirrors coverage; fi;
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --use-mirrors python-coveralls; fi
|
||||
- if [[ $DB == postgres* ]]; then pip install --use-mirrors psycopg2; fi;
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.5' ]]; then pip install --use-mirrors pysqlite; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != '2.7' ]]; then pip install unittest2; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install coverage; fi;
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install python-coveralls; fi
|
||||
- if [[ $DB == postgres* ]]; then pip install psycopg2; 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 == postgres* ]]; then psql -c 'create database test_w2p;' -U postgres; fi
|
||||
#Temporal solution to travis issue #155
|
||||
|
||||
@@ -54,7 +54,7 @@ src:
|
||||
### build web2py_src.zip
|
||||
echo '' > NEWINSTALL
|
||||
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:
|
||||
make src
|
||||
|
||||
@@ -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
@@ -189,7 +189,7 @@ def run(servername, ip, port, softcron=True, logging=False, profiler=None):
|
||||
if logging:
|
||||
application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase,
|
||||
logfilename='httpserver.log',
|
||||
profilerfilename=profiler)
|
||||
profiler_dir=profiler)
|
||||
else:
|
||||
application = gluon.main.wsgibase
|
||||
if softcron:
|
||||
@@ -319,8 +319,8 @@ def main():
|
||||
parser.add_option('-P',
|
||||
'--profiler',
|
||||
default=False,
|
||||
dest='profiler',
|
||||
help='profiler filename')
|
||||
dest='profiler_dir',
|
||||
help='profiler dir')
|
||||
servers = ', '.join(x for x in dir(Servers) if not x[0] == '_')
|
||||
parser.add_option('-s',
|
||||
'--server',
|
||||
@@ -346,7 +346,7 @@ def main():
|
||||
print 'starting %s on %s:%s...' % (
|
||||
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__':
|
||||
main()
|
||||
|
||||
@@ -1016,8 +1016,9 @@ def design():
|
||||
privates.sort()
|
||||
|
||||
# Get all static files
|
||||
MAXNFILES = 1000
|
||||
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()
|
||||
|
||||
# Get all languages
|
||||
|
||||
@@ -248,6 +248,7 @@
|
||||
'Next Edit Point': 'nächster Bearbeitungsschritt',
|
||||
'NO': 'NEIN',
|
||||
'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',
|
||||
'online designer': 'online designer',
|
||||
'or alternatively': 'or alternatively',
|
||||
|
||||
@@ -74,28 +74,6 @@
|
||||
* 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
|
||||
*/
|
||||
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*/
|
||||
$('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
|
||||
* Ideally it should bound all the things that are needed
|
||||
* and require no dom manipulations
|
||||
*/
|
||||
var doc = $(document);
|
||||
doc.on('click', '.flash', function (e) {
|
||||
var t = $(this);
|
||||
if(t.css('top') == '0px') t.slideUp('slow');
|
||||
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 () {
|
||||
this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse();
|
||||
@@ -205,7 +178,48 @@
|
||||
if(this.checked)
|
||||
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) {
|
||||
var redirect = xhr.getResponseHeader('web2py-redirect-location');
|
||||
if(redirect !== null) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import time
|
||||
|
||||
response.view = 'cache_examples/generic.html'
|
||||
|
||||
def cache_in_ram():
|
||||
"""cache the output of the lambda function in ram"""
|
||||
|
||||
@@ -74,28 +74,6 @@
|
||||
* 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
|
||||
*/
|
||||
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*/
|
||||
$('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
|
||||
* Ideally it should bound all the things that are needed
|
||||
* and require no dom manipulations
|
||||
*/
|
||||
var doc = $(document);
|
||||
doc.on('click', '.flash', function (e) {
|
||||
var t = $(this);
|
||||
if(t.css('top') == '0px') t.slideUp('slow');
|
||||
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 () {
|
||||
this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse();
|
||||
@@ -205,7 +178,48 @@
|
||||
if(this.checked)
|
||||
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) {
|
||||
var redirect = xhr.getResponseHeader('web2py-redirect-location');
|
||||
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}}
|
||||
@@ -74,28 +74,6 @@
|
||||
* 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
|
||||
*/
|
||||
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*/
|
||||
$('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
|
||||
* Ideally it should bound all the things that are needed
|
||||
* and require no dom manipulations
|
||||
*/
|
||||
var doc = $(document);
|
||||
doc.on('click', '.flash', function (e) {
|
||||
var t = $(this);
|
||||
if(t.css('top') == '0px') t.slideUp('slow');
|
||||
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 () {
|
||||
this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse();
|
||||
@@ -205,7 +178,48 @@
|
||||
if(this.checked)
|
||||
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) {
|
||||
var redirect = xhr.getResponseHeader('web2py-redirect-location');
|
||||
if(redirect !== null) {
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ from fileutils import up, fix_newlines, abspath, recursive_unlink
|
||||
from fileutils import read_file, write_file, parse_version
|
||||
from restricted import RestrictedError
|
||||
from settings import global_settings
|
||||
from http import HTTP
|
||||
|
||||
|
||||
if not global_settings.web2py_runtime_gae:
|
||||
import site
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ class CacheAbstract(object):
|
||||
|
||||
def __init__(self, request=None):
|
||||
"""
|
||||
Paremeters
|
||||
Parameters
|
||||
----------
|
||||
request:
|
||||
the global request object
|
||||
|
||||
@@ -13,7 +13,6 @@ FOR INTERNAL USE ONLY
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import fnmatch
|
||||
import os
|
||||
import copy
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
import re
|
||||
from cgi import escape
|
||||
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
|
||||
@@ -532,7 +538,7 @@ LINK = '\x07'
|
||||
DISABLED_META = '\x08'
|
||||
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_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_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)
|
||||
@@ -553,6 +559,53 @@ regex_markmin_escape = 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_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):
|
||||
""" insert \\ before markmin control characters: '`:*~[]{}@$ """
|
||||
@@ -571,15 +624,22 @@ def replace_at_urls(text,url):
|
||||
return regex_URL.sub(u1,text)
|
||||
|
||||
def replace_components(text,env):
|
||||
# not perfect but acceptable
|
||||
def u2(match, env=env):
|
||||
f = env.get(match.group('a'), match.group(0))
|
||||
if callable(f):
|
||||
b = match.group('b')
|
||||
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:
|
||||
f = 'ERROR: %s' % e
|
||||
return str(f)
|
||||
return regex_env.sub(u2, text)
|
||||
text = regex_env2.sub(u2, text)
|
||||
return text
|
||||
|
||||
def autolinks_simple(url):
|
||||
"""
|
||||
@@ -844,6 +904,9 @@ def render(text,
|
||||
>>> render("**@{probe:1}**", environment=dict(probe=lambda t:"test %s" % t))
|
||||
'<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]]')
|
||||
'<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>'
|
||||
|
||||
|
||||
@@ -48,12 +48,12 @@ _DEBUG = True
|
||||
_HISTORY_KIND = '_Shell_History'
|
||||
|
||||
# Types that can't be pickled.
|
||||
UNPICKLABLE_TYPES = (
|
||||
UNPICKLABLE_TYPES = [
|
||||
types.ModuleType,
|
||||
types.TypeType,
|
||||
types.ClassType,
|
||||
types.FunctionType,
|
||||
)
|
||||
]
|
||||
|
||||
# Unpicklable statements to seed new historys with.
|
||||
INITIAL_UNPICKLABLES = [
|
||||
@@ -244,8 +244,8 @@ def run(history, statement, env={}):
|
||||
for name, val in statement_module.__dict__.items():
|
||||
if name not in old_globals or represent(val) != old_globals[name]:
|
||||
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()]:
|
||||
# this statement added an unpicklable global. store the statement and
|
||||
# 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.
|
||||
for name, val in new_globals.items():
|
||||
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:
|
||||
sys.modules['__main__'] = old_main
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import __builtin__
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
+38
-21
@@ -690,7 +690,11 @@ class BaseAdapter(ConnectionPool):
|
||||
return isinstance(exception, self.driver.ProgrammingError)
|
||||
|
||||
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):
|
||||
return "'%s'" % obj.replace("'", "''")
|
||||
@@ -6939,9 +6943,11 @@ def sqlhtml_validators(field):
|
||||
referenced._format,multiple=True)
|
||||
else:
|
||||
requires = validators.IS_IN_DB(db,referenced._id,
|
||||
multiple=True)
|
||||
multiple=True)
|
||||
if field.unique:
|
||||
requires._and = validators.IS_NOT_IN_DB(db,field)
|
||||
if not field.notnull:
|
||||
requires = validators.IS_EMPTY_OR(requires)
|
||||
return requires
|
||||
elif field_type.startswith('list:'):
|
||||
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):
|
||||
if key == 'id':
|
||||
return long(self)
|
||||
self.__allocate()
|
||||
return self._record.get(key, None)
|
||||
if key in self._table:
|
||||
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):
|
||||
return self.__getattr__(key, default)
|
||||
@@ -8416,8 +8426,9 @@ class Table(object):
|
||||
def _enable_record_versioning(self,
|
||||
archive_db=None,
|
||||
archive_name = '%(tablename)s_archive',
|
||||
is_active = 'is_active',
|
||||
current_record = 'current_record',
|
||||
is_active = 'is_active'):
|
||||
current_record_label = None):
|
||||
db = self._db
|
||||
archive_db = archive_db or db
|
||||
archive_name = archive_name % dict(tablename=self._tablename)
|
||||
@@ -8432,7 +8443,8 @@ class Table(object):
|
||||
clones.append(field.clone(
|
||||
unique=False, type=field.type if nfk else 'bigint'))
|
||||
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(
|
||||
lambda qset,fs,db=archive_db,an=archive_name,cn=current_record:
|
||||
archive_record(qset,fs,db[an],cn))
|
||||
@@ -8875,24 +8887,31 @@ class Table(object):
|
||||
|
||||
first = True
|
||||
unique_idx = None
|
||||
for line in reader:
|
||||
for lineno, line in enumerate(reader):
|
||||
if not line:
|
||||
break
|
||||
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)]
|
||||
cols, cid = [], None
|
||||
for i,colname in enumerate(colnames):
|
||||
if is_id(colname):
|
||||
cid = i
|
||||
else:
|
||||
cols.append(i)
|
||||
elif colname in self.fields:
|
||||
cols.append((i,self[colname]))
|
||||
if colname == unique:
|
||||
unique_idx = i
|
||||
else:
|
||||
items = [fix(self[colnames[i]], line[i], id_map, id_offset) \
|
||||
for i in cols if colnames[i] in self.fields]
|
||||
|
||||
if not id_map and cid is not None and id_offset is not None and not unique_idx:
|
||||
# every other line contains instead data
|
||||
items = []
|
||||
for i, field in cols:
|
||||
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])
|
||||
curr_id = self.insert(**dict(items))
|
||||
if first:
|
||||
@@ -8900,10 +8919,8 @@ class Table(object):
|
||||
# First curr_id is bigger than csv_id,
|
||||
# then we are not restoring but
|
||||
# extending db table with csv db table
|
||||
if curr_id>csv_id:
|
||||
id_offset[self._tablename] = curr_id-csv_id
|
||||
else:
|
||||
id_offset[self._tablename] = 0
|
||||
id_offset[self._tablename] = (curr_id-csv_id) \
|
||||
if curr_id>csv_id else 0
|
||||
# create new id until we get the same as old_id+offset
|
||||
while curr_id<csv_id+id_offset[self._tablename]:
|
||||
self._db(self._db[self][colnames[cid]] == curr_id).delete()
|
||||
@@ -10690,7 +10707,7 @@ class Rows(object):
|
||||
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])
|
||||
>>> tmp = db.define_table('users',\
|
||||
Field('stringf', 'string', length=32, required=True),\
|
||||
@@ -10726,8 +10743,8 @@ def test_all():
|
||||
Field('name'),\
|
||||
Field('birth','date'),\
|
||||
migrate='test_person.table')
|
||||
>>> 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='Marco',birth='2005-06-22')
|
||||
>>> person_id = db.person.insert(name='Massimo',birth='1971-12-21')
|
||||
|
||||
commented len(db().select(db.person.ALL))
|
||||
commented 2
|
||||
@@ -10753,7 +10770,7 @@ def test_all():
|
||||
|
||||
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}>
|
||||
>>> me.name
|
||||
'Max'
|
||||
|
||||
@@ -10,7 +10,6 @@ License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import pdb
|
||||
import Queue
|
||||
import sys
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import codecs
|
||||
import encodings
|
||||
|
||||
|
||||
"""Caller will hand this library a buffer and ask it to either convert
|
||||
it or auto-detect the type.
|
||||
|
||||
+18
-5
@@ -7,7 +7,6 @@ Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
"""
|
||||
|
||||
import sys
|
||||
import storage
|
||||
import os
|
||||
import re
|
||||
@@ -360,12 +359,21 @@ def get_session(request, other_application='admin'):
|
||||
raise KeyError
|
||||
try:
|
||||
session_id = request.cookies['session_id_' + other_application].value
|
||||
osession = storage.load_storage(os.path.join(
|
||||
up(request.folder), other_application, 'sessions', session_id))
|
||||
session_filename = os.path.join(
|
||||
up(request.folder), other_application, 'sessions', session_id)
|
||||
osession = storage.load_storage(session_filename)
|
||||
except Exception, e:
|
||||
osession = storage.Storage()
|
||||
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',
|
||||
expiration=60 * 60, gae_login=True):
|
||||
@@ -381,9 +389,14 @@ def check_credentials(request, other_application='admin',
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
dt = time.time() - expiration
|
||||
t0 = time.time()
|
||||
dt = t0 - expiration
|
||||
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):
|
||||
|
||||
+17
-9
@@ -841,8 +841,8 @@ class DIV(XmlComponent):
|
||||
c.latest = self.latest
|
||||
c.session = self.session
|
||||
c.formname = self.formname
|
||||
c['hideerror'] = hideerror or \
|
||||
self.attributes.get('hideerror', False)
|
||||
if not c.attributes.get('hideerror'):
|
||||
c['hideerror'] = hideerror or self.attributes.get('hideerror')
|
||||
newstatus = c._traverse(status, hideerror) and newstatus
|
||||
|
||||
# for input, textarea, select, option
|
||||
@@ -1961,7 +1961,7 @@ class FORM(DIV):
|
||||
example::
|
||||
|
||||
>>> 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 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=''):
|
||||
if not select:
|
||||
select = SELECT(**self.attributes)
|
||||
custom_items = []
|
||||
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]),
|
||||
_value=item[2], _selected=item[1]))
|
||||
if len(item) > 3 and len(item[3]):
|
||||
self.serialize_mobile(
|
||||
item[3], select, prefix=CAT(prefix, item[0], '/'))
|
||||
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):
|
||||
if self['mobile']:
|
||||
@@ -2501,11 +2509,11 @@ def test():
|
||||
Example:
|
||||
|
||||
>>> 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>
|
||||
>>> 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>
|
||||
>>> 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>
|
||||
>>> 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>
|
||||
@@ -2531,7 +2539,7 @@ def test():
|
||||
>>> 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>
|
||||
>>> 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)
|
||||
True
|
||||
>>> form.as_dict(flat=True).has_key("vars")
|
||||
|
||||
@@ -15,7 +15,6 @@ import re
|
||||
import sys
|
||||
import pkgutil
|
||||
import logging
|
||||
import marshal
|
||||
from cgi import escape
|
||||
from threading import RLock
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ Contains:
|
||||
|
||||
if False: import import_all # DO NOT REMOVE PART OF FREEZE PROCESS
|
||||
import gc
|
||||
import cStringIO
|
||||
import Cookie
|
||||
import os
|
||||
import re
|
||||
|
||||
+12
-2
@@ -117,6 +117,10 @@ class Token(object):
|
||||
if a cron job started before 60 seconds and did not stop,
|
||||
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:
|
||||
logger.warning('WEB2PY CRON: Disabled because no file locking')
|
||||
return None
|
||||
@@ -128,7 +132,7 @@ class Token(object):
|
||||
(start, stop) = cPickle.load(self.master)
|
||||
except:
|
||||
(start, stop) = (0, 1)
|
||||
if startup or self.now - start > 59.99:
|
||||
if startup or self.now - start > locktime:
|
||||
ret = self.now
|
||||
if not stop:
|
||||
# this happens if previous cron job longer than 1 minute
|
||||
@@ -136,6 +140,7 @@ class Token(object):
|
||||
logger.debug('WEB2PY CRON: Acquiring lock')
|
||||
self.master.seek(0)
|
||||
cPickle.dump((self.now, 0), self.master)
|
||||
self.master.flush()
|
||||
finally:
|
||||
portalocker.unlock(self.master)
|
||||
if not ret:
|
||||
@@ -246,6 +251,7 @@ class cronlauncher(threading.Thread):
|
||||
shell=self.shell)
|
||||
_cron_subprocs.append(proc)
|
||||
(stdoutdata, stderrdata) = proc.communicate()
|
||||
_cron_subprocs.remove(proc)
|
||||
if proc.returncode != 0:
|
||||
logger.warning(
|
||||
'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:
|
||||
if _cron_stopping:
|
||||
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)
|
||||
if os.path.exists(w2p_path):
|
||||
commands.append(w2p_path)
|
||||
|
||||
@@ -6,7 +6,6 @@ License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
import platform
|
||||
from storage import Storage
|
||||
|
||||
|
||||
+8
-8
@@ -2073,14 +2073,13 @@ class SQLFORM(FORM):
|
||||
order = request.vars.order or ''
|
||||
if sortable:
|
||||
if order and not order == 'None':
|
||||
if order[:1] == '~':
|
||||
sign, rorder = '~', order[1:]
|
||||
otablename, ofieldname = order.split('~')[-1].split('.', 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:
|
||||
sign, rorder = '', order
|
||||
tablename, fieldname = rorder.split('.', 1)
|
||||
orderby = db[tablename][fieldname]
|
||||
if sign == '~':
|
||||
orderby = ~orderby
|
||||
orderby = (order[:1] == '~' and ~sort_field) or sort_field
|
||||
|
||||
expcolumns = [str(f) for f in columns]
|
||||
if export_type.endswith('with_hidden_cols'):
|
||||
@@ -2095,7 +2094,8 @@ class SQLFORM(FORM):
|
||||
try:
|
||||
dbset = dbset(SQLFORM.build_query(
|
||||
fields, request.vars.get('keywords', '')))
|
||||
rows = dbset.select(cacheable=True, *expcolumns)
|
||||
rows = dbset.select(left=left, orderby=orderby,
|
||||
cacheable=True, *expcolumns)
|
||||
except Exception, e:
|
||||
response.flash = T('Internal Error')
|
||||
rows = []
|
||||
|
||||
+2
-2
@@ -845,9 +845,9 @@ def render(content="hello world",
|
||||
'hello world'
|
||||
>>> render(content='abc')
|
||||
'abc'
|
||||
>>> render(content='abc\\'')
|
||||
>>> render(content='abc\'')
|
||||
"abc'"
|
||||
>>> render(content='a"\\'bc')
|
||||
>>> render(content='a"\'bc')
|
||||
'a"\\'bc'
|
||||
>>> render(content='a\\nbc')
|
||||
'a\\nbc'
|
||||
|
||||
+109
-61
@@ -1304,8 +1304,9 @@ class Auth(object):
|
||||
else:
|
||||
raise HTTP(404)
|
||||
|
||||
def navbar(self, mode='Default', action=None, prefix='Welcome',
|
||||
referrer_actions=DEFAULT, user_identifier=DEFAULT):
|
||||
def navbar(self, prefix='Welcome', action=None,
|
||||
separators=(' [ ', ' | ', ' ] '), user_identifier=DEFAULT,
|
||||
referrer_actions=DEFAULT, mode='default'):
|
||||
""" Navbar with support for more templates
|
||||
This uses some code from the old navbar.
|
||||
|
||||
@@ -1490,21 +1491,21 @@ class Auth(object):
|
||||
'bare': bare
|
||||
} # Define custom modes.
|
||||
|
||||
try:
|
||||
if mode in options and callable(options[mode]):
|
||||
options[mode]()
|
||||
except KeyError: # KeyError if mode is not in options (do Default)
|
||||
else:
|
||||
s1, s2, s3 = separators
|
||||
if self.user_id:
|
||||
self.bar = SPAN(prefix, user_identifier, '[',
|
||||
self.bar = SPAN(prefix, user_identifier, s1,
|
||||
Anr(items[0]['name'],
|
||||
_href=items[0]['href']), ']',
|
||||
_href=items[0]['href']), s3,
|
||||
_class='auth_navbar')
|
||||
else:
|
||||
self.bar = SPAN('[', Anr(items[0]['name'],
|
||||
_href=items[0]['href']), ']',
|
||||
self.bar = SPAN(s1, Anr(items[0]['name'],
|
||||
_href=items[0]['href']), s3,
|
||||
_class='auth_navbar')
|
||||
del items[0]
|
||||
for item in items:
|
||||
self.bar.insert(-1, ']')
|
||||
self.bar.insert(-1, s2)
|
||||
self.bar.insert(-1, Anr(item['name'], _href=item['href']))
|
||||
|
||||
return self.bar
|
||||
@@ -1522,7 +1523,8 @@ class Auth(object):
|
||||
tables,
|
||||
archive_db=None,
|
||||
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):
|
||||
|
||||
@@ -1549,6 +1551,8 @@ class Auth(object):
|
||||
does automatically.
|
||||
|
||||
"""
|
||||
current_record_label = current_record_label or current.T(
|
||||
current_record.replace('_',' ').title())
|
||||
for table in tables:
|
||||
fieldnames = table.fields()
|
||||
if ('id' in fieldnames and
|
||||
@@ -1557,7 +1561,8 @@ class Auth(object):
|
||||
table._enable_record_versioning(
|
||||
archive_db=archive_db,
|
||||
archive_name=archive_names,
|
||||
current_record=current_record)
|
||||
current_record=current_record,
|
||||
current_record_label=current_record_label)
|
||||
|
||||
def define_signature(self):
|
||||
db = self.db
|
||||
@@ -2157,12 +2162,17 @@ class Auth(object):
|
||||
elif 'username' in table_user.fields:
|
||||
username = 'username'
|
||||
else:
|
||||
username = 'email'
|
||||
username = 'email'
|
||||
settings = self.settings
|
||||
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)
|
||||
if not settings.username_case_sensitive:
|
||||
tmpvalidator = [IS_LOWER(), tmpvalidator]
|
||||
else:
|
||||
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
|
||||
table_user[username].requires = tmpvalidator
|
||||
|
||||
@@ -2170,7 +2180,7 @@ class Auth(object):
|
||||
response = current.response
|
||||
session = current.session
|
||||
|
||||
passfield = self.settings.password_field
|
||||
passfield = settings.password_field
|
||||
try:
|
||||
table_user[passfield].requires[-1].min_length = 0
|
||||
except:
|
||||
@@ -2186,43 +2196,43 @@ class Auth(object):
|
||||
|
||||
if next is DEFAULT:
|
||||
# important for security
|
||||
next = self.settings.login_next
|
||||
next = settings.login_next
|
||||
user_next = snext
|
||||
if user_next:
|
||||
external = user_next.split('://')
|
||||
if external[0].lower() in ['http', 'https', 'ftp']:
|
||||
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
|
||||
else:
|
||||
next = user_next
|
||||
if onvalidation is DEFAULT:
|
||||
onvalidation = self.settings.login_onvalidation
|
||||
onvalidation = settings.login_onvalidation
|
||||
if onaccept is DEFAULT:
|
||||
onaccept = self.settings.login_onaccept
|
||||
onaccept = settings.login_onaccept
|
||||
if log is DEFAULT:
|
||||
log = self.messages['login_log']
|
||||
|
||||
onfail = self.settings.login_onfail
|
||||
onfail = settings.login_onfail
|
||||
|
||||
user = None # default
|
||||
|
||||
# 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(
|
||||
table_user,
|
||||
fields=[username, passfield],
|
||||
hidden=dict(_next=next),
|
||||
showid=self.settings.showid,
|
||||
showid=settings.showid,
|
||||
submit_button=self.messages.login_button,
|
||||
delete_label=self.messages.delete_label,
|
||||
formstyle=self.settings.formstyle,
|
||||
separator=self.settings.label_separator
|
||||
formstyle=settings.formstyle,
|
||||
separator=settings.label_separator
|
||||
)
|
||||
|
||||
if self.settings.remember_me_form:
|
||||
if settings.remember_me_form:
|
||||
## adds a new input checkbox "remember me for longer"
|
||||
if self.settings.formstyle != 'bootstrap':
|
||||
if settings.formstyle != 'bootstrap':
|
||||
addrow(form, XML(" "),
|
||||
DIV(XML(" "),
|
||||
INPUT(_type='checkbox',
|
||||
@@ -2235,9 +2245,9 @@ class Auth(object):
|
||||
self.messages.label_remember_me,
|
||||
_for="auth_user_remember",
|
||||
)), "",
|
||||
self.settings.formstyle,
|
||||
settings.formstyle,
|
||||
'auth_user_remember__row')
|
||||
elif self.settings.formstyle == 'bootstrap':
|
||||
elif settings.formstyle == 'bootstrap':
|
||||
addrow(form,
|
||||
"",
|
||||
LABEL(
|
||||
@@ -2247,20 +2257,20 @@ class Auth(object):
|
||||
self.messages.label_remember_me,
|
||||
_class="checkbox"),
|
||||
"",
|
||||
self.settings.formstyle,
|
||||
settings.formstyle,
|
||||
'auth_user_remember__row')
|
||||
|
||||
captcha = self.settings.login_captcha or \
|
||||
(self.settings.login_captcha != False and self.settings.captcha)
|
||||
captcha = settings.login_captcha or \
|
||||
(settings.login_captcha != False and settings.captcha)
|
||||
if captcha:
|
||||
addrow(form, captcha.label, captcha, captcha.comment,
|
||||
self.settings.formstyle, 'captcha__row')
|
||||
settings.formstyle, 'captcha__row')
|
||||
accepted_form = False
|
||||
|
||||
if form.accepts(request, session,
|
||||
formname='login', dbio=False,
|
||||
onvalidation=onvalidation,
|
||||
hideerror=self.settings.hideerror):
|
||||
hideerror=settings.hideerror):
|
||||
|
||||
accepted_form = True
|
||||
# check for username in db
|
||||
@@ -2282,36 +2292,36 @@ class Auth(object):
|
||||
# try alternate logins 1st as these have the
|
||||
# current version of the password
|
||||
user = None
|
||||
for login_method in self.settings.login_methods:
|
||||
for login_method in settings.login_methods:
|
||||
if login_method != self and \
|
||||
login_method(request.vars[username],
|
||||
request.vars[passfield]):
|
||||
if not self in self.settings.login_methods:
|
||||
if not self in settings.login_methods:
|
||||
# do not store password in db
|
||||
form.vars[passfield] = None
|
||||
user = self.get_or_create_user(
|
||||
form.vars, self.settings.update_fields)
|
||||
form.vars, settings.update_fields)
|
||||
break
|
||||
if not user:
|
||||
# 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
|
||||
if form.vars.get(passfield, '') == temp_user[passfield]:
|
||||
# success
|
||||
user = temp_user
|
||||
else:
|
||||
# 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
|
||||
for login_method in self.settings.login_methods:
|
||||
for login_method in settings.login_methods:
|
||||
if login_method != self and \
|
||||
login_method(request.vars[username],
|
||||
request.vars[passfield]):
|
||||
if not self in self.settings.login_methods:
|
||||
if not self in settings.login_methods:
|
||||
# do not store password in db
|
||||
form.vars[passfield] = None
|
||||
user = self.get_or_create_user(
|
||||
form.vars, self.settings.update_fields)
|
||||
form.vars, settings.update_fields)
|
||||
break
|
||||
if not user:
|
||||
self.log_event(self.messages['login_failed_log'],
|
||||
@@ -2321,25 +2331,25 @@ class Auth(object):
|
||||
callback(onfail, None)
|
||||
redirect(
|
||||
self.url(args=request.args, vars=request.get_vars),
|
||||
client_side=self.settings.client_side)
|
||||
client_side=settings.client_side)
|
||||
|
||||
else:
|
||||
# use a central authentication server
|
||||
cas = self.settings.login_form
|
||||
cas = settings.login_form
|
||||
cas_user = cas.get_user()
|
||||
|
||||
if cas_user:
|
||||
cas_user[passfield] = None
|
||||
user = self.get_or_create_user(
|
||||
table_user._filter_fields(cas_user),
|
||||
self.settings.update_fields)
|
||||
settings.update_fields)
|
||||
elif hasattr(cas, 'login_form'):
|
||||
return cas.login_form()
|
||||
else:
|
||||
# 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),
|
||||
client_side=self.settings.client_side)
|
||||
client_side=settings.client_side)
|
||||
|
||||
# process authenticated users
|
||||
if user:
|
||||
@@ -2349,20 +2359,20 @@ class Auth(object):
|
||||
self.login_user(user)
|
||||
session.auth.expiration = \
|
||||
request.vars.get('remember', False) and \
|
||||
self.settings.long_expiration or \
|
||||
self.settings.expiration
|
||||
settings.long_expiration or \
|
||||
settings.expiration
|
||||
session.auth.remember = 'remember' in request.vars
|
||||
self.log_event(log, user)
|
||||
session.flash = self.messages.logged_in
|
||||
|
||||
# how to continue
|
||||
if self.settings.login_form == self:
|
||||
if settings.login_form == self:
|
||||
if accepted_form:
|
||||
callback(onaccept, form)
|
||||
if next == session._auth_next:
|
||||
session._auth_next = None
|
||||
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
|
||||
return form
|
||||
@@ -2371,7 +2381,7 @@ class Auth(object):
|
||||
|
||||
if next == 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):
|
||||
"""
|
||||
@@ -3584,7 +3594,8 @@ class Auth(object):
|
||||
templates=None,
|
||||
migrate=True,
|
||||
controller=None,
|
||||
function=None):
|
||||
function=None,
|
||||
force_render=False):
|
||||
|
||||
if controller and function: resolve = False
|
||||
|
||||
@@ -3608,7 +3619,7 @@ class Auth(object):
|
||||
if resolve:
|
||||
action = str(current.request.args(0)).startswith("_")
|
||||
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'):
|
||||
# We don't want to return a dict object, just the wiki
|
||||
wiki = wiki['content']
|
||||
@@ -5189,13 +5200,18 @@ class Wiki(object):
|
||||
controller, function, args = items[0], items[1], items[2:]
|
||||
return LOAD(controller, function, args=args, ajax=True).xml()
|
||||
|
||||
def get_render(self):
|
||||
def get_renderer(self):
|
||||
if isinstance(self.settings.render, basestring):
|
||||
r = getattr(self, "%s_render" % self.settings.render)
|
||||
elif callable(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:
|
||||
raise ValueError("Invalid render type %s" % type(render))
|
||||
raise ValueError(
|
||||
"Invalid render type %s" % type(self.settings.render))
|
||||
return r
|
||||
|
||||
def __init__(self, auth, env=None, render='markmin',
|
||||
@@ -5206,7 +5222,24 @@ class Wiki(object):
|
||||
|
||||
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
|
||||
perms = settings.manage_permissions = manage_permissions
|
||||
|
||||
@@ -5250,8 +5283,13 @@ class Wiki(object):
|
||||
default=[Wiki.everybody]),
|
||||
Field('changelog'),
|
||||
Field('html', 'text',
|
||||
compute=self.get_render(),
|
||||
compute=self.get_renderer(),
|
||||
readable=False, writable=False),
|
||||
Field('render', default="markmin",
|
||||
readable=show_engine,
|
||||
writable=show_engine,
|
||||
requires=IS_EMPTY_OR(
|
||||
IS_IN_SET(engines))),
|
||||
auth.signature],
|
||||
'vars':{'format':'%(title)s', 'migrate':migrate}}),
|
||||
('wiki_tag', {
|
||||
@@ -5403,7 +5441,7 @@ class Wiki(object):
|
||||
elif zero == '_cloud':
|
||||
return self.cloud()
|
||||
elif zero == '_preview':
|
||||
return self.preview(self.get_render())
|
||||
return self.preview(self.get_renderer())
|
||||
|
||||
def first_paragraph(self, page):
|
||||
if not self.can_read(page):
|
||||
@@ -5417,7 +5455,7 @@ class Wiki(object):
|
||||
def fix_hostname(self, body):
|
||||
return (body or '').replace('://HOSTNAME', '://%s' % self.host)
|
||||
|
||||
def read(self, slug):
|
||||
def read(self, slug, force_render=False):
|
||||
if slug in '_cloud':
|
||||
return self.cloud()
|
||||
elif slug in '_search':
|
||||
@@ -5432,10 +5470,12 @@ class Wiki(object):
|
||||
url = URL(args=('_edit', slug))
|
||||
return dict(content=A('Create page "%s"' % slug, _href=url, _class="btn"))
|
||||
else:
|
||||
html = page.html if not force_render else self.get_renderer(page)
|
||||
content = XML(self.fix_hostname(html))
|
||||
return dict(title=page.title,
|
||||
slug=page.slug,
|
||||
page=page,
|
||||
content=XML(self.fix_hostname(page.html)),
|
||||
content=content,
|
||||
tags=page.tags,
|
||||
created_on=page.created_on,
|
||||
modified_on=page.modified_on)
|
||||
@@ -5524,7 +5564,11 @@ class Wiki(object):
|
||||
if (prevbutton.hasClass('nopreview')) {
|
||||
prevbutton.addClass('preview').removeClass(
|
||||
'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()});
|
||||
} else {
|
||||
prevbutton.addClass(
|
||||
@@ -5798,6 +5842,10 @@ class Wiki(object):
|
||||
|
||||
def preview(self, render):
|
||||
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)
|
||||
|
||||
|
||||
|
||||
+1
-3
@@ -11,8 +11,6 @@ This file specifically includes utilities for security.
|
||||
|
||||
import threading
|
||||
import struct
|
||||
#import hashlib
|
||||
#import hmac
|
||||
import uuid
|
||||
import random
|
||||
import time
|
||||
@@ -23,7 +21,7 @@ import logging
|
||||
import socket
|
||||
import base64
|
||||
import zlib
|
||||
from types import ModuleType
|
||||
|
||||
|
||||
_struct_2_long_long = struct.Struct('=QQ')
|
||||
|
||||
|
||||
+53
-15
@@ -36,6 +36,7 @@ class Service(win32serviceutil.ServiceFramework):
|
||||
|
||||
_svc_name_ = '_unNamed'
|
||||
_svc_display_name_ = '_Service Template'
|
||||
_svc_description_ = '_Service Template description'
|
||||
|
||||
def __init__(self, *args):
|
||||
win32serviceutil.ServiceFramework.__init__(self, *args)
|
||||
@@ -44,6 +45,12 @@ class Service(win32serviceutil.ServiceFramework):
|
||||
def log(self, 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):
|
||||
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
|
||||
try:
|
||||
@@ -80,6 +87,7 @@ class Web2pyService(Service):
|
||||
|
||||
_svc_name_ = 'web2py'
|
||||
_svc_display_name_ = 'web2py Service'
|
||||
_svc_description_ = 'Web2py application framework service'
|
||||
_exe_args_ = 'options'
|
||||
server = None
|
||||
|
||||
@@ -132,17 +140,16 @@ class Web2pyService(Service):
|
||||
server_name=options.server_name,
|
||||
request_queue_size=options.request_queue_size,
|
||||
timeout=options.timeout,
|
||||
socket_timeout=options.socket_timeout,
|
||||
shutdown_timeout=options.shutdown_timeout,
|
||||
path=options.folder
|
||||
path=options.folder,
|
||||
interfaces=options.interfaces
|
||||
)
|
||||
try:
|
||||
from rewrite import load
|
||||
load()
|
||||
self.server.start()
|
||||
except:
|
||||
|
||||
# self.server.stop()
|
||||
|
||||
self.server = None
|
||||
raise
|
||||
|
||||
@@ -159,12 +166,18 @@ class Web2pyCronService(Web2pyService):
|
||||
|
||||
_svc_name_ = 'web2py_cron'
|
||||
_svc_display_name_ = 'web2py Cron Service'
|
||||
_svc_description_ = 'web2py Windows cron service for scheduled tasks'
|
||||
_exe_args_ = 'options'
|
||||
|
||||
def start(self):
|
||||
import newcron
|
||||
import global_settings
|
||||
self.log('web2py server starting')
|
||||
import logging
|
||||
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():
|
||||
return
|
||||
if len(sys.argv) == 2:
|
||||
@@ -172,25 +185,50 @@ class Web2pyCronService(Web2pyService):
|
||||
else:
|
||||
opt_mod = self._exe_args_
|
||||
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
|
||||
apps = [app.strip() for app in options.scheduler.split(
|
||||
',') if check_existent_app(options, app.strip())]
|
||||
else:
|
||||
apps = None
|
||||
self.extcron = newcron.extcron(options.folder, apps=apps)
|
||||
try:
|
||||
self.extcron.start()
|
||||
except:
|
||||
# self.server.stop()
|
||||
self.extcron = None
|
||||
raise
|
||||
|
||||
misfire_gracetime = float(options.misfire_gracetime) if 'misfire_gracetime' in dir(options) else 0.0
|
||||
logger.info('Starting Window cron service with %0.2f secs gracetime.' % misfire_gracetime)
|
||||
self._started = True
|
||||
wait_full_min = lambda: 60 - time.time() % 60
|
||||
while True:
|
||||
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):
|
||||
self.log('web2py cron stopping')
|
||||
self.log('web2py Cron service stopping')
|
||||
if not self.chdir():
|
||||
return
|
||||
if self.extcron:
|
||||
self._started = False
|
||||
self.extcron.join()
|
||||
|
||||
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
|
||||
unzip 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
|
||||
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)"
|
||||
/etc/init.d/redmine start
|
||||
start uwsgi-emperor
|
||||
|
||||
@@ -159,6 +159,7 @@ fi
|
||||
|
||||
wget http://web2py.com/examples/static/web2py_src.zip
|
||||
unzip web2py_src.zip
|
||||
mv web2py/handlers/wsgihandler.py web2py/wsgihandler.py
|
||||
chown -R apache:apache web2py
|
||||
|
||||
###
|
||||
|
||||
@@ -159,6 +159,7 @@ fi
|
||||
|
||||
wget http://web2py.com/examples/static/web2py_src.zip
|
||||
unzip web2py_src.zip
|
||||
mv web2py/handlers/wsgihandler.py web2py/wsgihandler.py
|
||||
chown -R apache:apache web2py
|
||||
|
||||
###
|
||||
|
||||
@@ -3,53 +3,53 @@
|
||||
echo 'setup-web2py-nginx-uwsgi-centos64.sh'
|
||||
echo 'Support CentOS 6.4'
|
||||
echo 'Installs Nginx 1.4.1 + uWSGI + Web2py'
|
||||
|
||||
|
||||
|
||||
|
||||
# Get Web2py Admin Password
|
||||
echo -e "Web2py Admin Password: \c "
|
||||
read PW
|
||||
|
||||
|
||||
echo -e "Set Server Name Ex: web2py.domain.com : \c "
|
||||
read SERVER_FQDN
|
||||
|
||||
|
||||
echo -e "Set Server IP: \c "
|
||||
read SERVER_IP
|
||||
|
||||
|
||||
|
||||
|
||||
echo "" >>/etc/hosts
|
||||
echo "$SERVER_IP $SERVER_FQDN" >>/etc/hosts
|
||||
|
||||
|
||||
yum update -y
|
||||
|
||||
|
||||
yum install -y http://mirror-fpt-telecom.fpt.net/fedora/epel/6/i386/epel-release-6-8.noarch.rpm
|
||||
yum clean all
|
||||
yum install -y gcc libxml2-devel python-devel python-pip PyXML unzip make sudo
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
|
||||
|
||||
pip-python install --upgrade pip
|
||||
PIPPATH=`which pip`
|
||||
$PIPPATH install --upgrade uwsgi
|
||||
|
||||
|
||||
|
||||
|
||||
# Prepare folders for uwsgi
|
||||
mkdir /etc/uwsgi
|
||||
mkdir /var/log/uwsgi
|
||||
mkdir -p /var/www/
|
||||
|
||||
|
||||
#usermod -a -G apache nginx
|
||||
mkdir -p /etc/nginx/ssl/
|
||||
|
||||
|
||||
|
||||
|
||||
cd /etc/nginx/ssl
|
||||
openssl genrsa 1024 > web2py.key && chmod 400 web2py.key
|
||||
openssl req -new -x509 -nodes -sha1 -days 1780 -key web2py.key > web2py.crt
|
||||
openssl x509 -noout -fingerprint -text < web2py.crt > web2py.info
|
||||
|
||||
|
||||
|
||||
|
||||
echo 'server {
|
||||
listen YOUR_SERVER_IP:80;
|
||||
server_name YOUR_SERVER_FQDN;
|
||||
@@ -97,13 +97,13 @@ server {
|
||||
#client_max_body_size 10m;
|
||||
###
|
||||
}
|
||||
|
||||
|
||||
}' >/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
|
||||
|
||||
|
||||
|
||||
|
||||
# Create configuration file /etc/uwsgi/web2py.ini
|
||||
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
|
||||
chmod-socket = 666
|
||||
' >/etc/uwsgi/web2py.ini
|
||||
|
||||
|
||||
|
||||
|
||||
cd /var/www/
|
||||
curl --progress -O http://web2py.com/examples/static/web2py_src.zip
|
||||
unzip web2py_src.zip && rm -rf web2py_src.zip
|
||||
# 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
|
||||
cd /var/www/web2py
|
||||
sudo -u nginx python -c "from gluon.main import save_password; save_password('$PW',443)"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Daemons /start/stop
|
||||
|
||||
|
||||
echo '#!/bin/sh
|
||||
# Autor: Nilton OS -- www.linuxpro.com.br
|
||||
#
|
||||
@@ -154,27 +154,27 @@ echo '#!/bin/sh
|
||||
# Default-Start: 3 5
|
||||
# Default-Stop: 0 1 2 6
|
||||
### END INIT INFO
|
||||
|
||||
|
||||
# Source function library.
|
||||
. /etc/rc.d/init.d/functions
|
||||
|
||||
|
||||
# Check for missing binaries (stale symlinks should not happen)
|
||||
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;
|
||||
else exit 5; fi; }
|
||||
|
||||
|
||||
UWSGI_EMPEROR_MODE=true
|
||||
UWSGI_VASSALS="/etc/uwsgi/"
|
||||
UWSGI_OPTIONS="--enable-threads --logto /var/log/uwsgi/uwsgi.log"
|
||||
lockfile=/var/lock/subsys/uwsgi
|
||||
|
||||
|
||||
UWSGI_OPTIONS="$UWSGI_OPTIONS --autoload"
|
||||
|
||||
|
||||
if [ "$UWSGI_EMPEROR_MODE" = "true" ] ; then
|
||||
UWSGI_OPTIONS="$UWSGI_OPTIONS --emperor $UWSGI_VASSALS"
|
||||
fi
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n "Starting uWSGI "
|
||||
@@ -198,19 +198,19 @@ case "$1" in
|
||||
;;
|
||||
esac
|
||||
exit 0 '> /etc/init.d/uwsgi
|
||||
|
||||
|
||||
chmod +x /etc/init.d/uwsgi
|
||||
|
||||
|
||||
/etc/init.d/uwsgi start
|
||||
/etc/init.d/nginx start
|
||||
|
||||
|
||||
/etc/init.d/iptables stop
|
||||
chkconfig --del iptables
|
||||
|
||||
|
||||
chkconfig --levels 235 uwsgi on
|
||||
chkconfig --levels 235 nginx on
|
||||
|
||||
|
||||
## you can reload uwsgi with
|
||||
#/etc/init.d/uwsgi restart
|
||||
## 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
|
||||
curl -O http://www.web2py.com/examples/static/web2py_src.zip
|
||||
unzip web2py_src.zip
|
||||
mv web2py/handlers/wsgihandler.py web2py/wsgihandler.py
|
||||
|
||||
echo "Set the ownership for web2py application to uwsgi"
|
||||
chown -R uwsgi /opt/web-apps/web2py
|
||||
|
||||
@@ -71,7 +71,7 @@ server {
|
||||
uwsgi_param UWSGI_SCHEME $scheme;
|
||||
uwsgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||
}
|
||||
|
||||
|
||||
}' >/etc/nginx/vhosts.d/web2py.conf
|
||||
|
||||
|
||||
@@ -102,8 +102,7 @@ cd /srv/www/
|
||||
wget http://web2py.com/examples/static/web2py_src.zip
|
||||
unzip web2py_src.zip
|
||||
rm web2py_src.zip
|
||||
# Download latest version of sessions2trash.py
|
||||
wget http://web2py.googlecode.com/hg/scripts/sessions2trash.py -O /srv/www/web2py/scripts/sessions2trash.py
|
||||
mv web2py/handlers/wsgihandler.py web2py/wsgihandler.py
|
||||
chown -R nginx:www web2py
|
||||
cd /srv/www/web2py
|
||||
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)
|
||||
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;
|
||||
else exit 5; fi; }
|
||||
|
||||
@@ -150,13 +149,13 @@ UWSGI_OPTIONS="$UWSGI_OPTIONS --autoload"
|
||||
|
||||
if [ "$UWSGI_EMPEROR_MODE" = "true" ] ; then
|
||||
UWSGI_OPTIONS="$UWSGI_OPTIONS --emperor $UWSGI_VASSALS"
|
||||
fi
|
||||
|
||||
. /etc/rc.status
|
||||
|
||||
rc_reset
|
||||
|
||||
case "$1" in
|
||||
fi
|
||||
|
||||
. /etc/rc.status
|
||||
|
||||
rc_reset
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n "Starting uWSGI "
|
||||
/sbin/startproc $UWSGI_BIN $UWSGI_OPTIONS
|
||||
@@ -219,7 +218,7 @@ chmod +x /etc/init.d/uwsgi
|
||||
|
||||
chkconfig --add uwsgi
|
||||
chkconfig --add nginx
|
||||
|
||||
|
||||
## you can reload uwsgi with
|
||||
#/etc/init.d/uwsgi restart
|
||||
## to reload web2py only (without restarting uwsgi)
|
||||
|
||||
@@ -169,9 +169,8 @@ mkdir /home/www-data
|
||||
cd /home/www-data
|
||||
wget http://web2py.com/examples/static/web2py_src.zip
|
||||
unzip web2py_src.zip
|
||||
mv web2py/handlers/wsgihandler.py web2py/wsgihandler.py
|
||||
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
|
||||
cd /home/www-data/web2py
|
||||
sudo -u www-data python -c "from gluon.main import save_password; save_password('$PW',443)"
|
||||
|
||||
@@ -59,6 +59,7 @@ cd www-data
|
||||
rm web2py_src.zip*
|
||||
wget http://web2py.com/examples/static/web2py_src.zip
|
||||
unzip web2py_src.zip
|
||||
mv web2py/handlers/wsgihandler.py web2py/wsgihandler.py
|
||||
chown -R www-data:www-data web2py
|
||||
|
||||
echo "setting up apache modules"
|
||||
|
||||
Reference in New Issue
Block a user