Merge ssh://github.com/web2py/web2py
This commit is contained in:
+6
-6
@@ -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
|
||||||
|
|||||||
@@ -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 @@
|
|||||||
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:
|
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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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"""
|
||||||
|
|||||||
@@ -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}}
|
||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>'
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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'
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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(" "),
|
addrow(form, XML(" "),
|
||||||
DIV(XML(" "),
|
DIV(XML(" "),
|
||||||
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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user