commit merge
@@ -3,6 +3,9 @@
|
||||
### DAL Improvements
|
||||
|
||||
- Support for DAL(lazy_tables=True) and db.define_table(on_define=lambda table:), thanks Jonathan
|
||||
- db(...).select(cacheable=True) make select 30% faster
|
||||
- db(...).select(cache=(cache.ram,3600)) now caches parsed data 100x faster
|
||||
- db(...).count(cache=(cache.ram,3600)) now supported
|
||||
- MongoDB support in DAL (experimental), thanks Mark Breedveld
|
||||
- geodal and spatialite, thanks Denes and Fran (experimental)
|
||||
- db.mytable._before_insert, _after_insert, _before_update, _after_update, _before_delete. _after_delete (list of callbacks)
|
||||
|
||||
@@ -29,7 +29,7 @@ update:
|
||||
wget -O gluon/contrib/simplejsonrpc.py http://rad2py.googlecode.com/hg/ide2py/simplejsonrpc.py
|
||||
echo "remember that pymysql was tweaked"
|
||||
src:
|
||||
echo 'Version 2.00.0 ('`date +%Y-%m-%d\ %H:%M:%S`') dev' > VERSION
|
||||
echo 'Version 2.00.1 ('`date +%Y-%m-%d\ %H:%M:%S`') rc4' > VERSION
|
||||
### rm -f all junk files
|
||||
make clean
|
||||
### clean up baisc apps
|
||||
|
||||
@@ -1 +1 @@
|
||||
Version 2.00.0 (2012-08-26 00:00:11) dev
|
||||
Version 2.00.1 (2012-08-28 22:14:54) rc4
|
||||
|
||||
@@ -11,6 +11,8 @@ import copy
|
||||
import gluon.contenttype
|
||||
import gluon.fileutils
|
||||
|
||||
response.subtitle = 'Database Administration (appadmin)'
|
||||
|
||||
# ## critical --- make a copy of the environment
|
||||
|
||||
global_env = copy.copy(globals())
|
||||
|
||||
@@ -23,9 +23,9 @@ except ImportError:
|
||||
GIT_MISSING = 'requires python-git module, but not installed or incompatible version'
|
||||
|
||||
from gluon.languages import (regex_language, read_possible_languages,
|
||||
read_possible_plurals, lang_sampling,
|
||||
lang_sampling,
|
||||
read_dict, write_dict, read_plural_dict,
|
||||
write_plural_dict)
|
||||
write_plural_dict, PLURAL_RULES)
|
||||
|
||||
|
||||
if DEMO_MODE and request.function in ['change_password','pack','pack_plugin','upgrade_web2py','uninstall','cleanup','compile_app','remove_compiled_app','delete','delete_plugin','create_file','upload_file','update_languages','reload_routes','git_push','git_pull']:
|
||||
@@ -949,8 +949,8 @@ def design():
|
||||
# get only existed files
|
||||
languages = sorted(all_languages)
|
||||
|
||||
plural_rules={}
|
||||
all_plurals=read_possible_plurals()
|
||||
plural_rules = {}
|
||||
all_plurals = PLURAL_RULES
|
||||
for langfile,lang in all_languages.iteritems():
|
||||
lang=lang.strip()
|
||||
match_language = regex_language.match(lang)
|
||||
@@ -961,7 +961,7 @@ def design():
|
||||
plang = lang_sampling(match_language, all_plurals.keys())
|
||||
if plang:
|
||||
plural=all_plurals[plang]
|
||||
plural_rules[langfile]=(plural[0],plang,plural[1],plural[3])
|
||||
plural_rules[langfile]=(plural[0],plang,plural[4],plural[3])
|
||||
else:
|
||||
plural_rules[langfile]=(0,lang,'plural_rules-%s.py'%lang,'')
|
||||
|
||||
|
||||
@@ -4,5 +4,127 @@
|
||||
'!langname!': 'English (US)',
|
||||
'%Y-%m-%d': '%m-%d-%Y',
|
||||
'%Y-%m-%d %H:%M:%S': '%m-%d-%Y %H:%M:%S',
|
||||
'(requires internet access)': '(requires internet access)',
|
||||
'(something like "it-it")': '(something like "it-it")',
|
||||
'About': 'About',
|
||||
'Additional code for your application': 'Additional code for your application',
|
||||
'Admin language': 'Admin language',
|
||||
'administrative interface': 'administrative interface',
|
||||
'Administrator Password:': 'Administrator Password:',
|
||||
'and rename it:': 'and rename it:',
|
||||
'Application name:': 'Application name:',
|
||||
'are not used yet': 'are not used yet',
|
||||
'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?',
|
||||
'arguments': 'arguments',
|
||||
'back': '<<back',
|
||||
'can be a git repo': 'can be a git repo',
|
||||
'Change admin password': 'Change admin password',
|
||||
'Check for upgrades': 'Check for upgrades',
|
||||
'Checking for upgrades...': 'Checking for upgrades...',
|
||||
'Clean': 'Clean',
|
||||
'code': 'code',
|
||||
'collapse/expand all': 'collapse/expand all',
|
||||
'Compile': 'Compile',
|
||||
'Controllers': 'Controllers',
|
||||
'controllers': 'controllers',
|
||||
'Create': 'Create',
|
||||
'create file with filename:': 'create file with filename:',
|
||||
'Create rules': 'Create rules',
|
||||
'created by': 'created by',
|
||||
'crontab': 'crontab',
|
||||
'currently running': 'currently running',
|
||||
'database administration': 'database administration',
|
||||
'Debug': 'Debug',
|
||||
'defines tables': 'defines tables',
|
||||
'Delete this file (you will be asked to confirm deletion)': 'Delete this file (you will be asked to confirm deletion)',
|
||||
'Deploy': 'Deploy',
|
||||
'Deploy on Google App Engine': 'Deploy on Google App Engine',
|
||||
'Deploy to OpenShift': 'Deploy to OpenShift',
|
||||
'Detailed traceback description': 'Detailed traceback description',
|
||||
'direction: ltr': 'direction: ltr',
|
||||
'Disable': 'Disable',
|
||||
'download layouts': 'download layouts',
|
||||
'download plugins': 'download plugins',
|
||||
'Edit': 'Edit',
|
||||
'Edit application': 'Edit application',
|
||||
'Error snapshot': 'Error snapshot',
|
||||
'Error ticket': 'Error ticket',
|
||||
'Errors': 'Errors',
|
||||
'Exception instance attributes': 'Exception instance attributes',
|
||||
'exposes': 'exposes',
|
||||
'extends': 'extends',
|
||||
'filter': 'filter',
|
||||
'Frames': 'Frames',
|
||||
'Get from URL:': 'Get from URL:',
|
||||
'Git Pull': 'Git Pull',
|
||||
'Git Push': 'Git Push',
|
||||
'Help': 'Help',
|
||||
'includes': 'includes',
|
||||
'inspect attributes': 'inspect attributes',
|
||||
'Install': 'Install',
|
||||
'Installed applications': 'Installed applications',
|
||||
'languages': 'languages',
|
||||
'Languages': 'Languages',
|
||||
'loading...': 'loading...',
|
||||
'locals': 'locals',
|
||||
'Login': 'Login',
|
||||
'Login to the Administrative Interface': 'Login to the Administrative Interface',
|
||||
'Logout': 'Logout',
|
||||
'Models': 'Models',
|
||||
'models': 'models',
|
||||
'modules': 'modules',
|
||||
'Modules': 'Modules',
|
||||
'New application wizard': 'New application wizard',
|
||||
'New simple application': 'New simple application',
|
||||
'Overwrite installed app': 'Overwrite installed app',
|
||||
'Pack all': 'Pack all',
|
||||
'Plugins': 'Plugins',
|
||||
'plugins': 'plugins',
|
||||
'Plural-Forms:': 'Plural-Forms:',
|
||||
'Powered by': 'Powered by',
|
||||
'Private files': 'Private files',
|
||||
'private files': 'private files',
|
||||
'Reload routes': 'Reload routes',
|
||||
'request': 'request',
|
||||
'response': 'response',
|
||||
'rules are not defined': 'rules are not defined',
|
||||
'rules:': 'rules:',
|
||||
"Run tests in this file (to run all files, you may also use the button labelled 'test')": "Run tests in this file (to run all files, you may also use the button labelled 'test')",
|
||||
'Running on %s': 'Running on %s',
|
||||
'session': 'session',
|
||||
'shell': 'shell',
|
||||
'Site': 'Site',
|
||||
'Start wizard': 'Start wizard',
|
||||
'static': 'static',
|
||||
'Static files': 'Static files',
|
||||
'Submit': 'Submit',
|
||||
'test': 'test',
|
||||
'The application logic, each URL path is mapped in one exposed function in the controller': 'The application logic, each URL path is mapped in one exposed function in the controller',
|
||||
'The data representation, define database tables and sets': 'The data representation, define database tables and sets',
|
||||
'The presentations layer, views are also known as templates': 'The presentations layer, views are also known as templates',
|
||||
'There are no models': 'There are no models',
|
||||
'There are no plugins': 'There are no plugins',
|
||||
'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app',
|
||||
'These files are served without processing, your images go here': 'These files are served without processing, your images go here',
|
||||
'Ticket ID': 'Ticket ID',
|
||||
'Ticket Missing': 'Ticket Missing',
|
||||
'To create a plugin, name a file/folder plugin_[name]': 'To create a plugin, name a file/folder plugin_[name]',
|
||||
'Traceback': 'Traceback',
|
||||
'Translation strings for the application': 'Translation strings for the application',
|
||||
'Uninstall': 'Uninstall',
|
||||
'update all languages': 'update all languages',
|
||||
'upload': 'upload',
|
||||
'Upload a package:': 'Upload a package:',
|
||||
'Upload and install packed application': 'Upload and install packed application',
|
||||
'upload file:': 'upload file:',
|
||||
'upload plugin file:': 'upload plugin file:',
|
||||
'variables': 'variables',
|
||||
'Version': 'Version',
|
||||
'Version %s.%s.%s (%s) %s': 'Version %s.%s.%s (%s) %s',
|
||||
'Versioning': 'Versioning',
|
||||
'views': 'views',
|
||||
'Views': 'Views',
|
||||
'Web Framework': 'Web Framework',
|
||||
'web2py is up to date': 'web2py is up to date',
|
||||
'web2py Recent Tweets': 'web2py Recent Tweets',
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 718 B |
@@ -1,571 +0,0 @@
|
||||
/*
|
||||
http://keith-wood.name/timeEntry.html
|
||||
Time entry for jQuery v1.4.8.
|
||||
Written by Keith Wood (kbwood{at}iinet.com.au) June 2007.
|
||||
Minor changes by Massimo Di Pierro Nov 2010 (simplified and changed behavior)
|
||||
Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
|
||||
MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
|
||||
Please attribute the author if you use it.
|
||||
|
||||
Turn an input field into an entry point for a time value.
|
||||
The time can be entered via directly typing the value,
|
||||
via the arrow keys.
|
||||
It is configurable to show 12 or 24-hour time, to show or hide seconds,
|
||||
to enforce a minimum and/or maximum time, to change the spinner image.
|
||||
|
||||
Example: jQuery('input.time').timeEntry();
|
||||
*/
|
||||
|
||||
(function(jQuery) { // Hide scope, no jQuery conflict
|
||||
|
||||
var PROP_NAME = 'timeEntry';
|
||||
|
||||
/* TimeEntry manager.
|
||||
Use the singleton instance of this class, jQuery.timeEntry, to interact with the time entry
|
||||
functionality. Settings for (groups of) fields are maintained in an instance object
|
||||
(TimeEntryInstance), allowing multiple different settings on the same page.
|
||||
*/
|
||||
|
||||
function TimeEntry() {
|
||||
this._disabledInputs = []; // List of time entry inputs that have been disabled
|
||||
this._defaults = {
|
||||
showSeconds: true, // True to show seconds as well, false for hours/minutes only
|
||||
defaultTime: null, // The time to use if none has been set, leave at null for now
|
||||
minTime: null, // The earliest selectable time, or null for no limit
|
||||
maxTime: null, // The latest selectable time, or null for no limit
|
||||
show24Hours: true, // True to use 24 hour time, false for 12 hour (AM/PM)
|
||||
ampmNames: ['am', 'pm'] // Names of morning/evening markers
|
||||
};
|
||||
jQuery.extend(this._defaults);
|
||||
}
|
||||
|
||||
jQuery.extend(TimeEntry.prototype, {
|
||||
/*
|
||||
Class name added to elements to indicate already configured with time entry.
|
||||
*/
|
||||
markerClassName: 'hasTimeEntry',
|
||||
|
||||
/* Override the default settings for all instances of the time entry.
|
||||
@param options (object) the new settings to use as defaults (anonymous object)
|
||||
@return (DateEntry) this object
|
||||
*/
|
||||
setDefaults: function(options) {
|
||||
extendRemove(this._defaults, options || {});
|
||||
return this;
|
||||
},
|
||||
|
||||
/* Attach the time entry handler to an input field.
|
||||
@param target (element) the field to attach to
|
||||
@param options (object) custom settings for this instance
|
||||
*/
|
||||
_connectTimeEntry: function(target, options) {
|
||||
var input = jQuery(target);
|
||||
if (input.hasClass(this.markerClassName)) {
|
||||
return;
|
||||
}
|
||||
var inst = {};
|
||||
inst.options = jQuery.extend({}, options);
|
||||
inst._selectedHour = 0; // The currently selected hour
|
||||
inst._selectedMinute = 0; // The currently selected minute
|
||||
inst._selectedSecond = 0; // The currently selected second
|
||||
inst._field = 0; // The selected subfield
|
||||
inst.input = jQuery(target); // The attached input field
|
||||
jQuery.data(target, PROP_NAME, inst);
|
||||
input.addClass(this.markerClassName).bind('focus.timeEntry', this._doFocus).
|
||||
bind('blur.timeEntry', this._doBlur).bind('click.timeEntry', this._doClick).
|
||||
bind('keydown.timeEntry', this._doKeyDown).bind('keypress.timeEntry', this._doKeyPress);
|
||||
// Check pastes
|
||||
if (jQuery.browser.mozilla)
|
||||
input.bind('input.timeEntry', function(event) { jQuery.timeEntry._parseTime(inst); });
|
||||
if (jQuery.browser.msie)
|
||||
input.bind('paste.timeEntry', function(event) { setTimeout(function() { jQuery.timeEntry._parseTime(inst); }, 1); });
|
||||
},
|
||||
|
||||
|
||||
/* Check whether an input field has been disabled.
|
||||
@param input (element) input field to check
|
||||
@return (boolean) true if this field has been disabled, false if it is enabled
|
||||
*/
|
||||
_isDisabledTimeEntry: function(input) {
|
||||
return jQuery.inArray(input, this._disabledInputs) > -1;
|
||||
},
|
||||
|
||||
/* Reconfigure the settings for a time entry field.
|
||||
@param input (element) input field to change
|
||||
@param options (object) new settings to add or
|
||||
(string) an individual setting name
|
||||
@param value (any) the individual setting's value
|
||||
*/
|
||||
_changeTimeEntry: function(input, options, value) {
|
||||
var inst = jQuery.data(input, PROP_NAME);
|
||||
if (inst) {
|
||||
if (typeof options == 'string') {
|
||||
var name = options;
|
||||
options = {};
|
||||
options[name] = value;
|
||||
}
|
||||
var currentTime = this._extractTime(inst);
|
||||
extendRemove(inst.options, options || {});
|
||||
if (currentTime)
|
||||
this._setTime(inst, new Date(0, 0, 0,
|
||||
currentTime[0], currentTime[1], currentTime[2]));
|
||||
}
|
||||
jQuery.data(input, PROP_NAME, inst);
|
||||
},
|
||||
|
||||
/* Remove the time entry functionality from an input.
|
||||
@param input (element) input field to affect
|
||||
*/
|
||||
_destroyTimeEntry: function(input) {
|
||||
jQueryinput = jQuery(input);
|
||||
if (!jQueryinput.hasClass(this.markerClassName)) return;
|
||||
jQueryinput.removeClass(this.markerClassName).unbind('.timeEntry');
|
||||
this._disabledInputs = jQuery.map(this._disabledInputs, function(value) { return (value == input ? null : value); }); // Delete entry
|
||||
jQueryinput.parent().replaceWith(jQueryinput);
|
||||
jQuery.removeData(input, PROP_NAME);
|
||||
},
|
||||
|
||||
/* Initialise the current time for a time entry input field.
|
||||
@param input (element) input field to update
|
||||
@param time (Date) the new time (year/month/day ignored) or null for now
|
||||
*/
|
||||
_setTimeTimeEntry: function(input, time) {
|
||||
var inst = jQuery.data(input, PROP_NAME);
|
||||
if (inst) this._setTime(inst, time ? (typeof time == 'object' ? new Date(time.getTime()) : time) : null);
|
||||
},
|
||||
|
||||
/* Retrieve the current time for a time entry input field.
|
||||
@param input (element) input field to examine
|
||||
@return (Date) current time (year/month/day zero) or null if none
|
||||
*/
|
||||
_getTimeTimeEntry: function(input) {
|
||||
var inst = jQuery.data(input, PROP_NAME);
|
||||
var currentTime = (inst ? this._extractTime(inst) : null);
|
||||
return (!currentTime ? null : new Date(0, 0, 0, currentTime[0], currentTime[1], currentTime[2]));
|
||||
},
|
||||
|
||||
/* Retrieve the millisecond offset for the current time.
|
||||
@param input (element) input field to examine
|
||||
@return (number) the time as milliseconds offset or zero if none
|
||||
*/
|
||||
_getOffsetTimeEntry: function(input) {
|
||||
var inst = jQuery.data(input, PROP_NAME);
|
||||
var currentTime = (inst ? this._extractTime(inst) : null);
|
||||
return (!currentTime ? 0 : (currentTime[0] * 3600 + currentTime[1] * 60 + currentTime[2]) * 1000);
|
||||
},
|
||||
|
||||
/* Initialise time entry.
|
||||
@param target (element) the input field or (event) the focus event
|
||||
*/
|
||||
_doFocus: function(target) {
|
||||
var input = (target.nodeName && target.nodeName.toLowerCase() == 'input' ? target : this);
|
||||
if (jQuery.timeEntry._lastInput == input || jQuery.timeEntry._isDisabledTimeEntry(input)) {
|
||||
jQuery.timeEntry._focussed = false;
|
||||
return;
|
||||
}
|
||||
var inst = jQuery.data(input, PROP_NAME);
|
||||
jQuery.timeEntry._focussed = true;
|
||||
jQuery.timeEntry._lastInput = input;
|
||||
jQuery.timeEntry._blurredInput = null;
|
||||
jQuery.data(input, PROP_NAME, inst);
|
||||
jQuery.timeEntry._parseTime(inst);
|
||||
setTimeout(function() { jQuery.timeEntry._showField(inst); }, 10);
|
||||
},
|
||||
|
||||
/* Note that the field has been exited.
|
||||
@param event (event) the blur event
|
||||
*/
|
||||
_doBlur: function(event) {
|
||||
jQuery.timeEntry._blurredInput = jQuery.timeEntry._lastInput;
|
||||
jQuery.timeEntry._lastInput = null;
|
||||
},
|
||||
|
||||
/* Select appropriate field portion on click, if already in the field.
|
||||
@param event (event) the click event
|
||||
*/
|
||||
_doClick: function(event) {
|
||||
var input = event.target;
|
||||
var inst = jQuery.data(input, PROP_NAME);
|
||||
if (!jQuery.timeEntry._focussed) {
|
||||
var fieldSize = 3;
|
||||
inst._field = 0;
|
||||
if (input.selectionStart != null) { // Use input select range
|
||||
for (var field = 0; field <= Math.max(1, inst._secondField, inst._ampmField); field++) {
|
||||
var end = (field != inst._ampmField ? (field * fieldSize) + 2 : (inst._ampmField * fieldSize) + 2);
|
||||
inst._field = field;
|
||||
if (input.selectionStart < end) break;
|
||||
}
|
||||
} else if (input.createTextRange) { // Check against bounding boxes
|
||||
var src = jQuery(event.srcElement);
|
||||
var range = input.createTextRange();
|
||||
var convert = function(value) {
|
||||
return {thin: 2, medium: 4, thick: 6}[value] || value;
|
||||
};
|
||||
var offsetX = event.clientX + document.documentElement.scrollLeft -
|
||||
(src.offset().left + parseInt(convert(src.css('border-left-width')), 10)) -
|
||||
range.offsetLeft; // Position - left edge - alignment
|
||||
for (var field = 0; field <= Math.max(1, inst._secondField, inst._ampmField); field++) {
|
||||
var end = (field != inst._ampmField ? (field * fieldSize) + 2 : (inst._ampmField * fieldSize) + 2);
|
||||
range.collapse();
|
||||
range.moveEnd('character', end);
|
||||
inst._field = field;
|
||||
if (offsetX < range.boundingWidth) break; // And compare
|
||||
}
|
||||
}
|
||||
}
|
||||
jQuery.data(input, PROP_NAME, inst);
|
||||
jQuery.timeEntry._showField(inst);
|
||||
jQuery.timeEntry._focussed = false;
|
||||
},
|
||||
|
||||
/* Handle keystrokes in the field.
|
||||
@param event (event) the keydown event
|
||||
@return (boolean) true to continue, false to stop processing
|
||||
*/
|
||||
_doKeyDown: function(event) {
|
||||
if (event.keyCode >= 48) return true;
|
||||
var inst = jQuery.data(event.target, PROP_NAME);
|
||||
|
||||
switch (event.keyCode) {
|
||||
case 9:
|
||||
var its = jQuery(':input');
|
||||
its.eq(its.index(this)+(event.shiftKey?-1:+1)).focus();
|
||||
break;
|
||||
case 37: jQuery.timeEntry._changeField(inst, -1, false); break; // Previous field on left
|
||||
case 38: jQuery.timeEntry._adjustField(inst, -1); break; // Increment time field on down
|
||||
case 16: if(!event.shiftKey) jQuery.timeEntry._changeField(inst, +1, false); break; // Next field on right
|
||||
case 39: jQuery.timeEntry._changeField(inst, +1, false); break; // Next field on right
|
||||
case 40: jQuery.timeEntry._adjustField(inst, +1); break; // Decrement time field on up
|
||||
case 32: case 46: jQuery.timeEntry._setValue(inst, ''); break; // Clear time on delete
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/* Disallow unwanted characters.
|
||||
@param event (event) the keypress event
|
||||
@return (boolean) true to continue, false to stop processing
|
||||
*/
|
||||
_doKeyPress: function(event) {
|
||||
var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
|
||||
if (chr < ' ') return true;
|
||||
var inst = jQuery.data(event.target, PROP_NAME);
|
||||
jQuery.timeEntry._handleKeyPress(inst, chr);
|
||||
return false;
|
||||
},
|
||||
|
||||
/* Get a setting value, defaulting if necessary.
|
||||
@param inst (object) the instance settings
|
||||
@param name (string) the setting name
|
||||
@return (any) the setting value
|
||||
*/
|
||||
_get: function(inst, name) {
|
||||
return (inst.options[name] != null ? inst.options[name] : jQuery.timeEntry._defaults[name]);
|
||||
},
|
||||
|
||||
/* Extract the time value from the input field, or default to now.
|
||||
@param inst (object) the instance settings
|
||||
*/
|
||||
_parseTime: function(inst) {
|
||||
var currentTime = this._extractTime(inst);
|
||||
var showSeconds = this._get(inst, 'showSeconds');
|
||||
if (currentTime) {
|
||||
inst._selectedHour = currentTime[0];
|
||||
inst._selectedMinute = currentTime[1];
|
||||
inst._selectedSecond = currentTime[2];
|
||||
}
|
||||
else {
|
||||
var now = this._constrainTime(inst);
|
||||
inst._selectedHour = now[0];
|
||||
inst._selectedMinute = now[1];
|
||||
inst._selectedSecond = (showSeconds ? now[2] : 0);
|
||||
}
|
||||
inst._secondField = (showSeconds ? 2 : -1);
|
||||
inst._ampmField = (this._get(inst, 'show24Hours') ? -1 : (showSeconds ? 3 : 2));
|
||||
inst._lastChr = '';
|
||||
inst._field = Math.max(0, Math.min(Math.max(1, inst._secondField, inst._ampmField), 0));
|
||||
if (inst.input.val() != '') this._showTime(inst);
|
||||
},
|
||||
|
||||
/* Extract the time value from a string as an array of values, or default to null.
|
||||
@param inst (object) the instance settings
|
||||
@param value (string) the time value to parse
|
||||
@return (number[3]) the time components (hours, minutes, seconds)
|
||||
or null if no value
|
||||
*/
|
||||
_extractTime: function(inst, value) {
|
||||
value = value || inst.input.val();
|
||||
var currentTime = value.split(':');
|
||||
var ampmNames = this._get(inst, 'ampmNames');
|
||||
var show24Hours = this._get(inst, 'show24Hours');
|
||||
if (currentTime.length >= 2) {
|
||||
var isAM = !show24Hours && (value.indexOf(ampmNames[0]) > -1);
|
||||
var isPM = !show24Hours && (value.indexOf(ampmNames[1]) > -1);
|
||||
var hour = parseInt(currentTime[0], 10);
|
||||
hour = (isNaN(hour) ? 0 : hour);
|
||||
hour = ((isAM || isPM) && hour == 12 ? 0 : hour) + (isPM ? 12 : 0);
|
||||
var minute = parseInt(currentTime[1], 10);
|
||||
minute = (isNaN(minute) ? 0 : minute);
|
||||
var second = (currentTime.length >= 3 ?
|
||||
parseInt(currentTime[2], 10) : 0);
|
||||
second = (isNaN(second) || !this._get(inst, 'showSeconds') ? 0 : second);
|
||||
return this._constrainTime(inst, [hour, minute, second]);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/* Constrain the given/current time to the time steps.
|
||||
@param inst (object) the instance settings
|
||||
@param fields (number[3]) the current time components (hours, minutes, seconds)
|
||||
@return (number[3]) the constrained time components (hours, minutes, seconds)
|
||||
*/
|
||||
_constrainTime: function(inst, fields) {
|
||||
var specified = (fields != null);
|
||||
if (!specified) {
|
||||
var now = this._determineTime(inst, this._get(inst, 'defaultTime')) || new Date();
|
||||
fields = [now.getHours(), now.getMinutes(), now.getSeconds()];
|
||||
}
|
||||
return fields;
|
||||
},
|
||||
|
||||
/* Set the selected time into the input field.
|
||||
@param inst (object) the instance settings
|
||||
*/
|
||||
_showTime: function(inst) {
|
||||
var show24Hours = this._get(inst, 'show24Hours');
|
||||
var currentTime = (this._formatNumber(show24Hours ? inst._selectedHour :
|
||||
((inst._selectedHour + 11) % 12) + 1) + ':' +
|
||||
this._formatNumber(inst._selectedMinute) +
|
||||
(this._get(inst, 'showSeconds') ? ':' +
|
||||
this._formatNumber(inst._selectedSecond) : '') +
|
||||
(show24Hours ? '' : this._get(inst, 'ampmNames')[(inst._selectedHour < 12 ? 0 : 1)]));
|
||||
this._setValue(inst, currentTime);
|
||||
this._showField(inst);
|
||||
},
|
||||
|
||||
/* Highlight the current time field.
|
||||
@param inst (object) the instance settings
|
||||
*/
|
||||
_showField: function(inst) {
|
||||
var input = inst.input[0];
|
||||
if (inst.input.is(':hidden') || jQuery.timeEntry._lastInput != input) return;
|
||||
var fieldSize = 3;
|
||||
var start = (inst._field == inst._ampmField ? (inst._ampmField * fieldSize) - 1 : (inst._field * fieldSize));
|
||||
var end = start + (inst._field == inst._ampmField ? 2 : 2);
|
||||
if (input.setSelectionRange) { // Mozilla
|
||||
input.setSelectionRange(start, end);
|
||||
}
|
||||
else if (input.createTextRange) { // IE
|
||||
var range = input.createTextRange();
|
||||
range.moveStart('character', start);
|
||||
range.moveEnd('character', end - inst.input.val().length);
|
||||
range.select();
|
||||
}
|
||||
if (!input.disabled) input.focus();
|
||||
},
|
||||
|
||||
/* Ensure displayed single number has a leading zero.
|
||||
@param value (number) current value
|
||||
@return (string) number with at least two digits
|
||||
*/
|
||||
_formatNumber: function(value) {
|
||||
return (value < 10 ? '0' : '') + value;
|
||||
},
|
||||
|
||||
/* Update the input field and notify listeners.
|
||||
@param inst (object) the instance settings
|
||||
@param value (string) the new value
|
||||
*/
|
||||
_setValue: function(inst, value) {
|
||||
if (value != inst.input.val()) inst.input.val(value).trigger('change');
|
||||
},
|
||||
|
||||
/* Move to previous/next field, or out of field altogether if appropriate.
|
||||
@param inst (object) the instance settings
|
||||
@param offset (number) the direction of change (-1, +1)
|
||||
@param moveOut (boolean) true if can move out of the field
|
||||
@return (boolean) true if exitting the field, false if not
|
||||
*/
|
||||
_changeField: function(inst, offset, moveOut) {
|
||||
var atFirstLast = (inst.input.val() == '' || inst._field == (offset == -1 ? 0 : Math.max(1, inst._secondField, inst._ampmField)));
|
||||
if (!atFirstLast) inst._field += offset;
|
||||
this._showField(inst);
|
||||
inst._lastChr = '';
|
||||
jQuery.data(inst.input[0], PROP_NAME, inst);
|
||||
return (atFirstLast && moveOut);
|
||||
},
|
||||
|
||||
/* Update the current field in the direction indicated.
|
||||
@param inst (object) the instance settings
|
||||
@param offset (number) the amount to change by
|
||||
*/
|
||||
_adjustField: function(inst, offset) {
|
||||
if (inst.input.val() == '') offset = 0;
|
||||
this._setTime(inst, new Date(0, 0, 0,
|
||||
inst._selectedHour + (inst._field == 0 ? offset : 0) +
|
||||
(inst._field == inst._ampmField ? offset * 12 : 0),
|
||||
inst._selectedMinute + (inst._field == 1 ? offset : 0),
|
||||
inst._selectedSecond + (inst._field == inst._secondField ? offset : 0)));
|
||||
},
|
||||
|
||||
/* Check against minimum/maximum and display time.
|
||||
@param inst (object) the instance settings
|
||||
@param time (Date) an actual time or
|
||||
(number) offset in seconds from now or
|
||||
(string) units and periods of offsets from now
|
||||
*/
|
||||
_setTime: function(inst, time) {
|
||||
time = this._determineTime(inst, time);
|
||||
var fields = this._constrainTime(inst, time ?
|
||||
[time.getHours(), time.getMinutes(), time.getSeconds()] : null);
|
||||
time = new Date(0, 0, 0, fields[0], fields[1], fields[2]);
|
||||
// Normalise to base date
|
||||
var time = this._normaliseTime(time);
|
||||
var minTime = this._normaliseTime(this._determineTime(inst, this._get(inst, 'minTime')));
|
||||
var maxTime = this._normaliseTime(this._determineTime(inst, this._get(inst, 'maxTime')));
|
||||
// Ensure it is within the bounds set
|
||||
time = (minTime && time < minTime ? minTime :
|
||||
(maxTime && time > maxTime ? maxTime : time));
|
||||
inst._selectedHour = time.getHours();
|
||||
inst._selectedMinute = time.getMinutes();
|
||||
inst._selectedSecond = time.getSeconds();
|
||||
this._showTime(inst);
|
||||
jQuery.data(inst.input[0], PROP_NAME, inst);
|
||||
},
|
||||
|
||||
/* Normalise time object to a common date.
|
||||
@param time (Date) the original time
|
||||
@return (Date) the normalised time
|
||||
*/
|
||||
_normaliseTime: function(time) {
|
||||
if (!time) return null;
|
||||
time.setFullYear(1900);
|
||||
time.setMonth(0);
|
||||
time.setDate(0);
|
||||
return time;
|
||||
},
|
||||
|
||||
/* A time may be specified as an exact value or a relative one.
|
||||
@param inst (object) the instance settings
|
||||
@param setting (Date) an actual time or
|
||||
(number) offset in seconds from now or
|
||||
(string) units and periods of offsets from now
|
||||
@return (Date) the calculated time
|
||||
*/
|
||||
_determineTime: function(inst, setting) {
|
||||
var offsetNumeric = function(offset) { // E.g. +300, -2
|
||||
var time = new Date();
|
||||
time.setTime(time.getTime() + offset * 1000);
|
||||
return time;
|
||||
};
|
||||
var offsetString = function(offset) { // E.g. '+2m', '-4h', '+3h +30m' or '12:34:56PM'
|
||||
var fields = jQuery.timeEntry._extractTime(inst, offset); // Actual time?
|
||||
var time = new Date();
|
||||
var hour = (fields ? fields[0] : time.getHours());
|
||||
var minute = (fields ? fields[1] : time.getMinutes());
|
||||
var second = (fields ? fields[2] : time.getSeconds());
|
||||
if (!fields) {
|
||||
var pattern = /([+-]?[0-9]+)\s*(s|S|m|M|h|H)?/g;
|
||||
var matches = pattern.exec(offset);
|
||||
while (matches) {
|
||||
switch (matches[2] || 's') {
|
||||
case 's' : case 'S' : second += parseInt(matches[1], 10); break;
|
||||
case 'm' : case 'M' : minute += parseInt(matches[1], 10); break;
|
||||
case 'h' : case 'H' : hour += parseInt(matches[1], 10); break;
|
||||
}
|
||||
matches = pattern.exec(offset);
|
||||
}
|
||||
}
|
||||
time = new Date(0, 0, 10, hour, minute, second, 0);
|
||||
if (/^!/.test(offset)) { // No wrapping
|
||||
if (time.getDate() > 10)
|
||||
time = new Date(0, 0, 10, 23, 59, 59);
|
||||
else if (time.getDate() < 10)
|
||||
time = new Date(0, 0, 10, 0, 0, 0);
|
||||
}
|
||||
return time;
|
||||
};
|
||||
return (setting ? (typeof setting == 'string' ? offsetString(setting) :
|
||||
(typeof setting == 'number' ? offsetNumeric(setting) : setting)) : null);
|
||||
},
|
||||
|
||||
/* Update time based on keystroke entered.
|
||||
@param inst (object) the instance settings
|
||||
@param chr (ch) the new character
|
||||
*/
|
||||
_handleKeyPress: function(inst, chr) {
|
||||
if (chr == ':') this._changeField(inst, +1, false);
|
||||
else if (chr >= '0' && chr <= '9') { // Allow direct entry of time
|
||||
var key = parseInt(chr, 10);
|
||||
var value = parseInt(inst._lastChr + chr, 10);
|
||||
var show24Hours = this._get(inst, 'show24Hours');
|
||||
var hour = (inst._field != 0 ? inst._selectedHour :
|
||||
(show24Hours ? (value < 24 ? value : key) :
|
||||
(value >= 1 && value <= 12 ? value :
|
||||
(key > 0 ? key : inst._selectedHour)) % 12 +
|
||||
(inst._selectedHour >= 12 ? 12 : 0)));
|
||||
var minute = (inst._field != 1 ? inst._selectedMinute :
|
||||
(value < 60 ? value : key));
|
||||
var second = (inst._field != inst._secondField ? inst._selectedSecond :
|
||||
(value < 60 ? value : key));
|
||||
var fields = this._constrainTime(inst, [hour, minute, second]);
|
||||
this._setTime(inst, new Date(0, 0, 0, fields[0], fields[1], fields[2]));
|
||||
inst._lastChr = chr;
|
||||
}
|
||||
else if (!this._get(inst, 'show24Hours')) { // Set am/pm based on first char of names
|
||||
chr = chr.toLowerCase();
|
||||
var ampmNames = this._get(inst, 'ampmNames');
|
||||
if ((chr == ampmNames[0].substring(0, 1).toLowerCase() && inst._selectedHour >= 12) ||
|
||||
(chr == ampmNames[1].substring(0, 1).toLowerCase() && inst._selectedHour < 12)) {
|
||||
var saveField = inst._field;
|
||||
inst._field = inst._ampmField;
|
||||
this._adjustField(inst, +1);
|
||||
inst._field = saveField;
|
||||
this._showField(inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* jQuery extend now ignores nulls!
|
||||
@param target (object) the object to update
|
||||
@param props (object) the new settings
|
||||
@return (object) the updated object
|
||||
*/
|
||||
function extendRemove(target, props) {
|
||||
jQuery.extend(target, props);
|
||||
for (var name in props) if (props[name] == null) target[name] = null;
|
||||
return target;
|
||||
}
|
||||
|
||||
// Commands that don't return a jQuery object
|
||||
var getters = ['getOffset', 'getTime', 'isDisabled'];
|
||||
|
||||
/* Attach the time entry functionality to a jQuery selection.
|
||||
@param command (string) the command to run (optional, default 'attach')
|
||||
@param options (object) the new settings to use for these countdown instances (optional)
|
||||
@return (jQuery) for chaining further calls
|
||||
*/
|
||||
jQuery.fn.timeEntry = function(options) {
|
||||
var otherArgs = Array.prototype.slice.call(arguments, 1);
|
||||
if (typeof options == 'string' && jQuery.inArray(options, getters) > -1) {
|
||||
return jQuery.timeEntry['_' + options + 'TimeEntry'].apply(jQuery.timeEntry, [this[0]].concat(otherArgs));
|
||||
}
|
||||
return this.each(function() {
|
||||
var nodeName = this.nodeName.toLowerCase();
|
||||
if (nodeName == 'input') {
|
||||
if (typeof options == 'string')
|
||||
jQuery.timeEntry['_' + options + 'TimeEntry'].apply(jQuery.timeEntry, [this].concat(otherArgs));
|
||||
else {
|
||||
// Check for settings on the control itself
|
||||
var inlineSettings = (jQuery.fn.metadata ? jQuery(this).metadata() : {});
|
||||
jQuery.timeEntry._connectTimeEntry(this, jQuery.extend(inlineSettings, options));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* Initialise the time entry functionality. */
|
||||
jQuery.timeEntry = new TimeEntry(); // Singleton instance
|
||||
|
||||
})(jQuery);
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
//--></script>
|
||||
|
||||
{{if request.function=='index':}}
|
||||
<h2>{{=T("Available databases and tables")}}</h2>
|
||||
<h2>{{=T("Available Databases and Tables")}}</h2>
|
||||
{{if not databases:}}{{=T("No databases in this application")}}{{pass}}
|
||||
<table>
|
||||
{{for db in sorted(databases):}}
|
||||
{{for table in databases[db].tables:}}
|
||||
{{qry='%s.%s.id>0'%(db,table)}}
|
||||
@@ -28,20 +29,24 @@
|
||||
{{qry=''}}
|
||||
{{pass}}
|
||||
{{pass}}
|
||||
<h3>
|
||||
{{=A("%s.%s" % (db,table),_href=URL('select',args=[db],vars=dict(query=qry)))}}
|
||||
</h3>
|
||||
[ {{=A(str(T('insert new'))+' '+table,_href=URL('insert',args=[db,table]))}} ]
|
||||
<br /><br />
|
||||
<tr>
|
||||
<th style="font-size: 1.75em;">
|
||||
{{=A("%s.%s" % (db,table),_href=URL('select',args=[db],vars=dict(query=qry)))}}
|
||||
</th>
|
||||
<td>
|
||||
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}}
|
||||
</td>
|
||||
</tr>
|
||||
{{pass}}
|
||||
</table>
|
||||
{{pass}}
|
||||
|
||||
{{elif request.function=='select':}}
|
||||
<h2>{{=XML(str(T("database %s select"))%A(request.args[0],_href=URL('index'))) }}
|
||||
<h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }}
|
||||
</h2>
|
||||
{{if table:}}
|
||||
[ {{=A(str(T('insert new %s'))%table,_href=URL('insert',args=[request.args[0],table]))}} ]<br/><br/>
|
||||
<h3>{{=T("Rows in table")}}</h3><br/>
|
||||
{{if table:}}
|
||||
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/>
|
||||
<h3>{{=T("Rows in Table")}}</h3><br/>
|
||||
{{else:}}
|
||||
<h3>{{=T("Rows selected")}}</h3><br/>
|
||||
{{pass}}
|
||||
@@ -51,8 +56,8 @@
|
||||
{{=T('"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN')}}</p>
|
||||
<br/><br/>
|
||||
<h4>{{=T("%s selected", nrows)}}</h4>
|
||||
{{if start>0:}}[ {{=A(T('previous 100 rows'),_href=URL('select',args=request.args[0],vars=dict(start=start-100)))}} ]{{pass}}
|
||||
{{if stop<nrows:}}[ {{=A(T('next 100 rows'),_href=URL('select',args=request.args[0],vars=dict(start=start+100)))}} ]{{pass}}
|
||||
{{if start>0:}}{{=A(T('previous 100 rows'),_href=URL('select',args=request.args[0],vars=dict(start=start-100)),_class="btn")}}{{pass}}
|
||||
{{if stop<nrows:}}{{=A(T('next 100 rows'),_href=URL('select',args=request.args[0],vars=dict(start=start+100)),_class="btn")}}{{pass}}
|
||||
{{if rows:}}
|
||||
<div style="overflow: auto;" width="80%">
|
||||
{{linkto=URL('update',args=request.args[0])}}
|
||||
@@ -61,35 +66,35 @@
|
||||
</div>
|
||||
{{pass}}
|
||||
<br/><br/><h3>{{=T("Import/Export")}}</h3><br/>
|
||||
[ <a href="{{=URL('csv',args=request.args[0],vars=dict(query=query))}}">{{=T("export as csv file")}}</a> ]
|
||||
<a href="{{=URL('csv',args=request.args[0],vars=dict(query=query))}}" class="btn">{{=T("export as csv file")}}</a>
|
||||
{{=formcsv or ''}}
|
||||
|
||||
{{elif request.function=='insert':}}
|
||||
<h2>{{=T("database")}} {{=A(request.args[0],_href=URL('index'))}}
|
||||
<h2>{{=T("Database")}} {{=A(request.args[0],_href=URL('index'))}}
|
||||
{{if hasattr(table,'_primarykey'):}}
|
||||
{{fieldname=table._primarykey[0]}}
|
||||
{{dbname=request.args[0]}}
|
||||
{{tablename=request.args[1]}}
|
||||
{{cond = table[fieldname].type in ['string','text'] and '!=""' or '>0'}}
|
||||
{{=T("table")}} {{=A(tablename,_href=URL('select',args=dbname,vars=dict(query='%s.%s.%s%s'%(dbname,tablename,fieldname,cond))))}}
|
||||
{{=T("Table")}} {{=A(tablename,_href=URL('select',args=dbname,vars=dict(query='%s.%s.%s%s'%(dbname,tablename,fieldname,cond))))}}
|
||||
{{else:}}
|
||||
{{=T("table")}} {{=A(request.args[1],_href=URL('select',args=request.args[0],vars=dict(query='%s.%s.id>0'%tuple(request.args[:2]))))}}
|
||||
{{=T("Table")}} {{=A(request.args[1],_href=URL('select',args=request.args[0],vars=dict(query='%s.%s.id>0'%tuple(request.args[:2]))))}}
|
||||
{{pass}}
|
||||
</h2>
|
||||
<h3>{{=T("New Record")}}</h3><br/>
|
||||
{{=form}}
|
||||
{{elif request.function=='update':}}
|
||||
<h2>{{=T("database")}} {{=A(request.args[0],_href=URL('index'))}}
|
||||
<h2>{{=T("Database")}} {{=A(request.args[0],_href=URL('index'))}}
|
||||
{{if hasattr(table,'_primarykey'):}}
|
||||
{{fieldname=request.vars.keys()[0]}}
|
||||
{{dbname=request.args[0]}}
|
||||
{{tablename=request.args[1]}}
|
||||
{{cond = table[fieldname].type in ['string','text'] and '!=""' or '>0'}}
|
||||
{{=T("table")}} {{=A(tablename,_href=URL('select',args=dbname,vars=dict(query='%s.%s.%s%s'%(dbname,tablename,fieldname,cond))))}}
|
||||
{{=T("record")}} {{=A('%s=%s'%request.vars.items()[0],_href=URL('update',args=request.args[:2],vars=request.vars))}}
|
||||
{{=T("Table")}} {{=A(tablename,_href=URL('select',args=dbname,vars=dict(query='%s.%s.%s%s'%(dbname,tablename,fieldname,cond))))}}
|
||||
{{=T("Record")}} {{=A('%s=%s'%request.vars.items()[0],_href=URL('update',args=request.args[:2],vars=request.vars))}}
|
||||
{{else:}}
|
||||
{{=T("table")}} {{=A(request.args[1],_href=URL('select',args=request.args[0],vars=dict(query='%s.%s.id>0'%tuple(request.args[:2]))))}}
|
||||
{{=T("record id")}} {{=A(request.args[2],_href=URL('update',args=request.args[:3]))}}
|
||||
{{=T("Table")}} {{=A(request.args[1],_href=URL('select',args=request.args[0],vars=dict(query='%s.%s.id>0'%tuple(request.args[:2]))))}}
|
||||
{{=T("Record id")}} {{=A(request.args[2],_href=URL('update',args=request.args[:3]))}}
|
||||
{{pass}}
|
||||
</h2>
|
||||
<h3>{{=T("Edit current record")}}</h3><br/><br/>{{=form}}
|
||||
|
||||
@@ -13,12 +13,16 @@
|
||||
<th>web2py™</th>
|
||||
<td>{{=myversion}}</td>
|
||||
</tr>
|
||||
{{if snapshot:}}
|
||||
<tr>
|
||||
<th>Python</th>
|
||||
<td>{{=snapshot.get('pyver','')}}</td>
|
||||
</tr>
|
||||
{{pass}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{if traceback or code or layer:}}
|
||||
<h3>{{=T('Traceback')}}</h3>
|
||||
<div class="inspect">
|
||||
{{=traceback}}
|
||||
@@ -130,4 +134,6 @@
|
||||
<h3>In file: {{=layer}}</h3>
|
||||
{{=CODE(code.replace('\r',''),language='python',link='/examples/global/vars/')}}
|
||||
</div>
|
||||
|
||||
{{else:}}
|
||||
<h3>{{=T('Ticket Missing')}}</h3>
|
||||
{{pass}}
|
||||
|
||||
@@ -11,6 +11,8 @@ import copy
|
||||
import gluon.contenttype
|
||||
import gluon.fileutils
|
||||
|
||||
response.subtitle = 'Database Administration (appadmin)'
|
||||
|
||||
# ## critical --- make a copy of the environment
|
||||
|
||||
global_env = copy.copy(globals())
|
||||
|
||||
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 714 B |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
@@ -1,116 +0,0 @@
|
||||
$(function(){
|
||||
// hide/show header
|
||||
$('#close-open-top a').bind('click', function() {
|
||||
if($('header:visible').length) {
|
||||
$('img', this).attr('src', 'images/open.png');
|
||||
} else {
|
||||
$('img', this).attr('src', 'images/close.png');
|
||||
}
|
||||
$('header').slideToggle('slow');
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// tabs
|
||||
$('.tab_content').hide();
|
||||
$('ul.tabs li:first').addClass('active').show();
|
||||
$('.tab_content:first').show();
|
||||
|
||||
$('ul.tabs li').click(function() {
|
||||
$('ul.tabs li').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
$('.tab_content').hide();
|
||||
var activeTab = $(this).find('a').attr('href');
|
||||
$(activeTab).fadeIn();
|
||||
return false;
|
||||
});
|
||||
|
||||
// hide/show default text when user focuses on newsletter subscribe field
|
||||
var defaultEmailTxt = $('#email-address').val();
|
||||
$('#email-address').focus(function() {
|
||||
if ($('#email-address').val() == defaultEmailTxt) {
|
||||
$('#email-address').val('');
|
||||
}
|
||||
});
|
||||
$('#email-address').blur(function() {
|
||||
if ($('#email-address').val() == '') {
|
||||
$('#email-address').val(defaultEmailTxt);
|
||||
}
|
||||
});
|
||||
|
||||
// Lightbox
|
||||
$(".gallery a[rel^='prettyPhoto']").prettyPhoto({animationSpeed:'slow',theme:'dark_rounded',slideshow:4000, autoplay_slideshow: false});
|
||||
|
||||
// Tipsy
|
||||
$('#social li a img').tipsy({delayIn: 1200, delayOut: 1200, gravity: 's'});
|
||||
|
||||
// init newsletter subscription AJAX handling
|
||||
$('#newslettersubmit').click(function() { $('#newsletterform').submit(); return false; });
|
||||
$('#newsletterform').ajaxForm({dataType: 'json',
|
||||
timeout: 2000,
|
||||
success: newsletterResponse});
|
||||
|
||||
// Twitter script config
|
||||
if ($('#tweet').length) {
|
||||
getTwitters('tweet', {
|
||||
id: 'envatowebdesign',
|
||||
count: 3,
|
||||
enableLinks: true,
|
||||
ignoreReplies: true,
|
||||
template: '"%text%" <a class="meta" href="http://twitter.com/%user_screen_name%/status/%id%">%time%</a>'});
|
||||
}
|
||||
|
||||
|
||||
// init contact form validation and AJAX handling
|
||||
if ($("#contactform").length > 0) {
|
||||
$("#contactform").validate({ rules: { name: "required",
|
||||
email: { required: true, email: true },
|
||||
message: "required"},
|
||||
messages: { name: "This field is required.",
|
||||
email: { required: "This field is required.",
|
||||
email: "Please enter a valied email address."},
|
||||
message: "This field is required."},
|
||||
submitHandler: function(form) { $(form).ajaxSubmit({dataType: 'json', success: contactFormResponse}); }
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// handle newsletter subscribe AJAX response
|
||||
function newsletterResponse(response) {
|
||||
if (response.responseStatus == 'err') {
|
||||
if (response.responseMsg == 'ajax') {
|
||||
alert('Error - this script can only be invoked via an AJAX call.');
|
||||
} else if (response.responseMsg == 'fileopen') {
|
||||
alert('Error opening $emailsFile. Please refer to documentation for help.');
|
||||
} else if (response.responseMsg == 'email') {
|
||||
alert('Please enter a valid email address.');
|
||||
} else if (response.responseMsg == 'duplicate') {
|
||||
alert('You are already subscribed to our newsletter.');
|
||||
} else if (response.responseMsg == 'filewrite') {
|
||||
alert('Error writing to $emailsFile. Please refer to documentation for help.');
|
||||
} else {
|
||||
alert('Undocumented error. Please refresh the page and try again.');
|
||||
}
|
||||
} else if (response.responseStatus == 'ok') {
|
||||
alert('Thank you for subscribing to our newsletter! We will not abuse your address.');
|
||||
} else {
|
||||
alert('Undocumented error. Please refresh the page and try again.');
|
||||
}
|
||||
} // newsletterResponse
|
||||
|
||||
// handle contact form AJAX response
|
||||
function contactFormResponse(response) {
|
||||
if (response.responseStatus == 'err') {
|
||||
if (response.responseMsg == 'ajax') {
|
||||
alert('Error - this script can only be invoked via an AJAX call.');
|
||||
} else if (response.responseMsg == 'notsent') {
|
||||
alert('We are having some mail server issues. Please refresh the page or try again later.');
|
||||
} else {
|
||||
alert('Undocumented error. Please refresh the page and try again.');
|
||||
}
|
||||
} else if (response.responseStatus == 'ok') {
|
||||
alert('Thank you for contacting us! We\'ll get back to you ASAP.');
|
||||
} else {
|
||||
alert('Undocumented error. Please refresh the page and try again.');
|
||||
}
|
||||
} // contactFormResponse
|
||||
@@ -1,69 +0,0 @@
|
||||
/* ------------------------------------------------------------------------
|
||||
Class: prettyPhoto
|
||||
Use: Lightbox clone for jQuery
|
||||
Author: Stephane Caron (http://www.no-margin-for-errors.com)
|
||||
Version: 3.0.3
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
(function($){$.prettyPhoto={version:'3.0.2'};$.fn.prettyPhoto=function(pp_settings){pp_settings=jQuery.extend({animation_speed:'fast',slideshow:false,autoplay_slideshow:false,opacity:0.80,show_title:true,allow_resize:true,default_width:500,default_height:344,counter_separator_label:'/',theme:'facebook',hideflash:false,wmode:'opaque',autoplay:true,modal:false,overlay_gallery:true,keyboard_shortcuts:true,changepicturecallback:function(){},callback:function(){},markup:'<div class="pp_pic_holder"> \
|
||||
<div class="ppt"> </div> \
|
||||
<div class="pp_top"> \
|
||||
<div class="pp_left"></div> \
|
||||
<div class="pp_middle"></div> \
|
||||
<div class="pp_right"></div> \
|
||||
</div> \
|
||||
<div class="pp_content_container"> \
|
||||
<div class="pp_left"> \
|
||||
<div class="pp_right"> \
|
||||
<div class="pp_content"> \
|
||||
<div class="pp_loaderIcon"></div> \
|
||||
<div class="pp_fade"> \
|
||||
<a href="#" class="pp_expand" title="Expand the image">Expand</a> \
|
||||
<div class="pp_hoverContainer"> \
|
||||
<a class="pp_next" href="#">next</a> \
|
||||
<a class="pp_previous" href="#">previous</a> \
|
||||
</div> \
|
||||
<div id="pp_full_res"></div> \
|
||||
<div class="pp_details clearfix"> \
|
||||
<p class="pp_description"></p> \
|
||||
<a class="pp_close" href="#">Close</a> \
|
||||
<div class="pp_nav"> \
|
||||
<a href="#" class="pp_arrow_previous">Previous</a> \
|
||||
<p class="currentTextHolder">0/0</p> \
|
||||
<a href="#" class="pp_arrow_next">Next</a> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
<div class="pp_bottom"> \
|
||||
<div class="pp_left"></div> \
|
||||
<div class="pp_middle"></div> \
|
||||
<div class="pp_right"></div> \
|
||||
</div> \
|
||||
</div> \
|
||||
<div class="pp_overlay"></div>',gallery_markup:'<div class="pp_gallery"> \
|
||||
<a href="#" class="pp_arrow_previous">Previous</a> \
|
||||
<ul> \
|
||||
{gallery} \
|
||||
</ul> \
|
||||
<a href="#" class="pp_arrow_next">Next</a> \
|
||||
</div>',image_markup:'<img id="fullResImage" src="{path}" />',flash_markup:'<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="{width}" height="{height}"><param name="wmode" value="{wmode}" /><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="{path}" /><embed src="{path}" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="{width}" height="{height}" wmode="{wmode}"></embed></object>',quicktime_markup:'<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" codebase="http://www.apple.com/qtactivex/qtplugin.cab" height="{height}" width="{width}"><param name="src" value="{path}"><param name="autoplay" value="{autoplay}"><param name="type" value="video/quicktime"><embed src="{path}" height="{height}" width="{width}" autoplay="{autoplay}" type="video/quicktime" pluginspage="http://www.apple.com/quicktime/download/"></embed></object>',iframe_markup:'<iframe src ="{path}" width="{width}" height="{height}" frameborder="no"></iframe>',inline_markup:'<div class="pp_inline clearfix">{content}</div>',custom_markup:''},pp_settings);var matchedObjects=this,percentBased=false,pp_dimensions,pp_open,pp_contentHeight,pp_contentWidth,pp_containerHeight,pp_containerWidth,windowHeight=$(window).height(),windowWidth=$(window).width(),pp_slideshow;doresize=true,scroll_pos=_get_scroll();$(window).unbind('resize.prettyphoto').bind('resize.prettyphoto',function(){_center_overlay();_resize_overlay();});if(pp_settings.keyboard_shortcuts){$(document).unbind('keydown.prettyphoto').bind('keydown.prettyphoto',function(e){if(typeof $pp_pic_holder!='undefined'){if($pp_pic_holder.is(':visible')){switch(e.keyCode){case 37:$.prettyPhoto.changePage('previous');e.preventDefault();break;case 39:$.prettyPhoto.changePage('next');e.preventDefault();break;case 27:if(!settings.modal)
|
||||
$.prettyPhoto.close();e.preventDefault();break;};};};});}
|
||||
$.prettyPhoto.initialize=function(){settings=pp_settings;if($.browser.msie&&parseInt($.browser.version)==6)settings.theme="light_square";theRel=$(this).attr('rel');galleryRegExp=/\[(?:.*)\]/;isSet=(galleryRegExp.exec(theRel))?true:false;pp_images=(isSet)?jQuery.map(matchedObjects,function(n,i){if($(n).attr('rel').indexOf(theRel)!=-1)return $(n).attr('href');}):$.makeArray($(this).attr('href'));pp_titles=(isSet)?jQuery.map(matchedObjects,function(n,i){if($(n).attr('rel').indexOf(theRel)!=-1)return($(n).find('img').attr('alt'))?$(n).find('img').attr('alt'):"";}):$.makeArray($(this).find('img').attr('alt'));pp_descriptions=(isSet)?jQuery.map(matchedObjects,function(n,i){if($(n).attr('rel').indexOf(theRel)!=-1)return($(n).attr('title'))?$(n).attr('title'):"";}):$.makeArray($(this).attr('title'));_buildOverlay(this);if(settings.allow_resize)
|
||||
$(window).bind('scroll.prettyphoto',function(){_center_overlay();});set_position=jQuery.inArray($(this).attr('href'),pp_images);$.prettyPhoto.open();return false;}
|
||||
$.prettyPhoto.open=function(event){if(typeof settings=="undefined"){settings=pp_settings;if($.browser.msie&&$.browser.version==6)settings.theme="light_square";pp_images=$.makeArray(arguments[0]);pp_titles=(arguments[1])?$.makeArray(arguments[1]):$.makeArray("");pp_descriptions=(arguments[2])?$.makeArray(arguments[2]):$.makeArray("");isSet=(pp_images.length>1)?true:false;set_position=0;_buildOverlay(event.target);}
|
||||
if($.browser.msie&&$.browser.version==6)$('select').css('visibility','hidden');if(settings.hideflash)$('object,embed').css('visibility','hidden');_checkPosition($(pp_images).size());$('.pp_loaderIcon').show();if($ppt.is(':hidden'))$ppt.css('opacity',0).show();$pp_overlay.show().fadeTo(settings.animation_speed,settings.opacity);$pp_pic_holder.find('.currentTextHolder').text((set_position+1)+settings.counter_separator_label+$(pp_images).size());$pp_pic_holder.find('.pp_description').show().html(unescape(pp_descriptions[set_position]));(settings.show_title&&pp_titles[set_position]!=""&&typeof pp_titles[set_position]!="undefined")?$ppt.html(unescape(pp_titles[set_position])):$ppt.html(' ');movie_width=(parseFloat(grab_param('width',pp_images[set_position])))?grab_param('width',pp_images[set_position]):settings.default_width.toString();movie_height=(parseFloat(grab_param('height',pp_images[set_position])))?grab_param('height',pp_images[set_position]):settings.default_height.toString();if(movie_height.indexOf('%')!=-1){movie_height=parseFloat(($(window).height()*parseFloat(movie_height)/100)-150);percentBased=true;}
|
||||
if(movie_width.indexOf('%')!=-1){movie_width=parseFloat(($(window).width()*parseFloat(movie_width)/100)-150);percentBased=true;}
|
||||
$pp_pic_holder.fadeIn(function(){imgPreloader="";switch(_getFileType(pp_images[set_position])){case'image':imgPreloader=new Image();nextImage=new Image();if(isSet&&set_position<$(pp_images).size()-1)nextImage.src=pp_images[set_position+1];prevImage=new Image();if(isSet&&pp_images[set_position-1])prevImage.src=pp_images[set_position-1];$pp_pic_holder.find('#pp_full_res')[0].innerHTML=settings.image_markup.replace(/{path}/g,pp_images[set_position]);imgPreloader.onload=function(){pp_dimensions=_fitToViewport(imgPreloader.width,imgPreloader.height);_showContent();};imgPreloader.onerror=function(){alert('Image cannot be loaded. Make sure the path is correct and image exist.');$.prettyPhoto.close();};imgPreloader.src=pp_images[set_position];break;case'youtube':pp_dimensions=_fitToViewport(movie_width,movie_height);movie='http://www.youtube.com/v/'+grab_param('v',pp_images[set_position]);if(settings.autoplay)movie+="&autoplay=1";toInject=settings.flash_markup.replace(/{width}/g,pp_dimensions['width']).replace(/{height}/g,pp_dimensions['height']).replace(/{wmode}/g,settings.wmode).replace(/{path}/g,movie);break;case'vimeo':pp_dimensions=_fitToViewport(movie_width,movie_height);movie_id=pp_images[set_position];var regExp=/http:\/\/(www\.)?vimeo.com\/(\d+)/;var match=movie_id.match(regExp);movie='http://player.vimeo.com/video/'+match[2]+'?title=0&byline=0&portrait=0';if(settings.autoplay)movie+="&autoplay=1;";vimeo_width=pp_dimensions['width']+'/embed/?moog_width='+pp_dimensions['width'];toInject=settings.iframe_markup.replace(/{width}/g,vimeo_width).replace(/{height}/g,pp_dimensions['height']).replace(/{path}/g,movie);break;case'quicktime':pp_dimensions=_fitToViewport(movie_width,movie_height);pp_dimensions['height']+=15;pp_dimensions['contentHeight']+=15;pp_dimensions['containerHeight']+=15;toInject=settings.quicktime_markup.replace(/{width}/g,pp_dimensions['width']).replace(/{height}/g,pp_dimensions['height']).replace(/{wmode}/g,settings.wmode).replace(/{path}/g,pp_images[set_position]).replace(/{autoplay}/g,settings.autoplay);break;case'flash':pp_dimensions=_fitToViewport(movie_width,movie_height);flash_vars=pp_images[set_position];flash_vars=flash_vars.substring(pp_images[set_position].indexOf('flashvars')+10,pp_images[set_position].length);filename=pp_images[set_position];filename=filename.substring(0,filename.indexOf('?'));toInject=settings.flash_markup.replace(/{width}/g,pp_dimensions['width']).replace(/{height}/g,pp_dimensions['height']).replace(/{wmode}/g,settings.wmode).replace(/{path}/g,filename+'?'+flash_vars);break;case'iframe':pp_dimensions=_fitToViewport(movie_width,movie_height);frame_url=pp_images[set_position];frame_url=frame_url.substr(0,frame_url.indexOf('iframe')-1);toInject=settings.iframe_markup.replace(/{width}/g,pp_dimensions['width']).replace(/{height}/g,pp_dimensions['height']).replace(/{path}/g,frame_url);break;case'custom':pp_dimensions=_fitToViewport(movie_width,movie_height);toInject=settings.custom_markup;break;case'inline':myClone=$(pp_images[set_position]).clone().css({'width':settings.default_width}).wrapInner('<div id="pp_full_res"><div class="pp_inline clearfix"></div></div>').appendTo($('body')).show();doresize=false;pp_dimensions=_fitToViewport($(myClone).width(),$(myClone).height());doresize=true;$(myClone).remove();toInject=settings.inline_markup.replace(/{content}/g,$(pp_images[set_position]).html());break;};if(!imgPreloader){$pp_pic_holder.find('#pp_full_res')[0].innerHTML=toInject;_showContent();};});return false;};$.prettyPhoto.changePage=function(direction){currentGalleryPage=0;if(direction=='previous'){set_position--;if(set_position<0){set_position=0;return;};}else if(direction=='next'){set_position++;if(set_position>$(pp_images).size()-1){set_position=0;}}else{set_position=direction;};if(!doresize)doresize=true;$('.pp_contract').removeClass('pp_contract').addClass('pp_expand');_hideContent(function(){$.prettyPhoto.open();});};$.prettyPhoto.changeGalleryPage=function(direction){if(direction=='next'){currentGalleryPage++;if(currentGalleryPage>totalPage){currentGalleryPage=0;};}else if(direction=='previous'){currentGalleryPage--;if(currentGalleryPage<0){currentGalleryPage=totalPage;};}else{currentGalleryPage=direction;};itemsToSlide=(currentGalleryPage==totalPage)?pp_images.length-((totalPage)*itemsPerPage):itemsPerPage;$pp_pic_holder.find('.pp_gallery li').each(function(i){$(this).animate({'left':(i*itemWidth)-((itemsToSlide*itemWidth)*currentGalleryPage)});});};$.prettyPhoto.startSlideshow=function(){if(typeof pp_slideshow=='undefined'){$pp_pic_holder.find('.pp_play').unbind('click').removeClass('pp_play').addClass('pp_pause').click(function(){$.prettyPhoto.stopSlideshow();return false;});pp_slideshow=setInterval($.prettyPhoto.startSlideshow,settings.slideshow);}else{$.prettyPhoto.changePage('next');};}
|
||||
$.prettyPhoto.stopSlideshow=function(){$pp_pic_holder.find('.pp_pause').unbind('click').removeClass('pp_pause').addClass('pp_play').click(function(){$.prettyPhoto.startSlideshow();return false;});clearInterval(pp_slideshow);pp_slideshow=undefined;}
|
||||
$.prettyPhoto.close=function(){if($pp_overlay.is(":animated"))return;$.prettyPhoto.stopSlideshow();$pp_pic_holder.stop().find('object,embed').css('visibility','hidden');$('div.pp_pic_holder,div.ppt,.pp_fade').fadeOut(settings.animation_speed,function(){$(this).remove();});$pp_overlay.fadeOut(settings.animation_speed,function(){if($.browser.msie&&$.browser.version==6)$('select').css('visibility','visible');if(settings.hideflash)$('object,embed').css('visibility','visible');$(this).remove();$(window).unbind('scroll');settings.callback();doresize=true;pp_open=false;delete settings;});};function _showContent(){$('.pp_loaderIcon').hide();$ppt.fadeTo(settings.animation_speed,1);projectedTop=scroll_pos['scrollTop']+((windowHeight/2)-(pp_dimensions['containerHeight']/2));if(projectedTop<0)projectedTop=0;$pp_pic_holder.find('.pp_content').animate({height:pp_dimensions['contentHeight'],width:pp_dimensions['contentWidth']},settings.animation_speed);$pp_pic_holder.animate({'top':projectedTop,'left':(windowWidth/2)-(pp_dimensions['containerWidth']/2),width:pp_dimensions['containerWidth']},settings.animation_speed,function(){$pp_pic_holder.find('.pp_hoverContainer,#fullResImage').height(pp_dimensions['height']).width(pp_dimensions['width']);$pp_pic_holder.find('.pp_fade').fadeIn(settings.animation_speed);if(isSet&&_getFileType(pp_images[set_position])=="image"){$pp_pic_holder.find('.pp_hoverContainer').show();}else{$pp_pic_holder.find('.pp_hoverContainer').hide();}
|
||||
if(pp_dimensions['resized']){$('a.pp_expand,a.pp_contract').show();}else{$('a.pp_expand,a.pp_contract').hide();}
|
||||
if(settings.autoplay_slideshow&&!pp_slideshow&&!pp_open)$.prettyPhoto.startSlideshow();settings.changepicturecallback();pp_open=true;});_insert_gallery();};function _hideContent(callback){$pp_pic_holder.find('#pp_full_res object,#pp_full_res embed').css('visibility','hidden');$pp_pic_holder.find('.pp_fade').fadeOut(settings.animation_speed,function(){$('.pp_loaderIcon').show();callback();});};function _checkPosition(setCount){(setCount>1)?$('.pp_nav').show():$('.pp_nav').hide();};function _fitToViewport(width,height){resized=false;_getDimensions(width,height);imageWidth=width,imageHeight=height;if(((pp_containerWidth>windowWidth)||(pp_containerHeight>windowHeight))&&doresize&&settings.allow_resize&&!percentBased){resized=true,fitting=false;while(!fitting){if((pp_containerWidth>windowWidth)){imageWidth=(windowWidth-200);imageHeight=(height/width)*imageWidth;}else if((pp_containerHeight>windowHeight)){imageHeight=(windowHeight-200);imageWidth=(width/height)*imageHeight;}else{fitting=true;};pp_containerHeight=imageHeight,pp_containerWidth=imageWidth;};_getDimensions(imageWidth,imageHeight);};return{width:Math.floor(imageWidth),height:Math.floor(imageHeight),containerHeight:Math.floor(pp_containerHeight),containerWidth:Math.floor(pp_containerWidth)+40,contentHeight:Math.floor(pp_contentHeight),contentWidth:Math.floor(pp_contentWidth),resized:resized};};function _getDimensions(width,height){width=parseFloat(width);height=parseFloat(height);$pp_details=$pp_pic_holder.find('.pp_details');$pp_details.width(width);detailsHeight=parseFloat($pp_details.css('marginTop'))+parseFloat($pp_details.css('marginBottom'));$pp_details=$pp_details.clone().appendTo($('body')).css({'position':'absolute','top':-10000});detailsHeight+=$pp_details.height();detailsHeight=(detailsHeight<=34)?36:detailsHeight;if($.browser.msie&&$.browser.version==7)detailsHeight+=8;$pp_details.remove();$pp_title=$pp_pic_holder.find('.ppt');$pp_title.width(width);titleHeight=parseFloat($pp_title.css('marginTop'))+parseFloat($pp_title.css('marginBottom'));$pp_title=$pp_title.clone().appendTo($('body')).css({'position':'absolute','top':-10000});titleHeight+=$pp_title.height();$pp_title.remove();pp_contentHeight=height+detailsHeight;pp_contentWidth=width;pp_containerHeight=pp_contentHeight+titleHeight+$pp_pic_holder.find('.pp_top').height()+$pp_pic_holder.find('.pp_bottom').height();pp_containerWidth=width;}
|
||||
function _getFileType(itemSrc){if(itemSrc.match(/youtube\.com\/watch/i)){return'youtube';}else if(itemSrc.match(/vimeo\.com/i)){return'vimeo';}else if(itemSrc.match(/\b.mov\b/i)){return'quicktime';}else if(itemSrc.match(/\b.swf\b/i)){return'flash';}else if(itemSrc.match(/\biframe=true\b/i)){return'iframe';}else if(itemSrc.match(/\bcustom=true\b/i)){return'custom';}else if(itemSrc.substr(0,1)=='#'){return'inline';}else{return'image';};};function _center_overlay(){if(doresize&&typeof $pp_pic_holder!='undefined'){scroll_pos=_get_scroll();contentHeight=$pp_pic_holder.height(),contentwidth=$pp_pic_holder.width();projectedTop=(windowHeight/2)+scroll_pos['scrollTop']-(contentHeight/2);if(projectedTop<0)projectedTop=0;$pp_pic_holder.css({'top':projectedTop,'left':(windowWidth/2)+scroll_pos['scrollLeft']-(contentwidth/2)});};};function _get_scroll(){if(self.pageYOffset){return{scrollTop:self.pageYOffset,scrollLeft:self.pageXOffset};}else if(document.documentElement&&document.documentElement.scrollTop){return{scrollTop:document.documentElement.scrollTop,scrollLeft:document.documentElement.scrollLeft};}else if(document.body){return{scrollTop:document.body.scrollTop,scrollLeft:document.body.scrollLeft};};};function _resize_overlay(){windowHeight=$(window).height(),windowWidth=$(window).width();if(typeof $pp_overlay!="undefined")$pp_overlay.height($(document).height()).width(windowWidth);};function _insert_gallery(){if(isSet&&settings.overlay_gallery&&_getFileType(pp_images[set_position])=="image"){itemWidth=52+5;navWidth=(settings.theme=="facebook")?58:38;itemsPerPage=Math.floor((pp_dimensions['containerWidth']-100-navWidth)/itemWidth);itemsPerPage=(itemsPerPage<pp_images.length)?itemsPerPage:pp_images.length;totalPage=Math.ceil(pp_images.length/itemsPerPage)-1;if(totalPage==0){navWidth=0;$pp_pic_holder.find('.pp_gallery .pp_arrow_next,.pp_gallery .pp_arrow_previous').hide();}else{$pp_pic_holder.find('.pp_gallery .pp_arrow_next,.pp_gallery .pp_arrow_previous').show();};galleryWidth=itemsPerPage*itemWidth+navWidth;$pp_pic_holder.find('.pp_gallery').width(galleryWidth).css('margin-left',-(galleryWidth/2));$pp_pic_holder.find('.pp_gallery ul').width(itemsPerPage*itemWidth).find('li.selected').removeClass('selected');goToPage=(Math.ceil((set_position+1)/itemsPerPage)<totalPage)?Math.ceil((set_position+1)/itemsPerPage):totalPage;$.prettyPhoto.changeGalleryPage(goToPage);$pp_pic_holder.find('.pp_gallery ul li:eq('+set_position+')').addClass('selected');}else{$pp_pic_holder.find('.pp_content').unbind('mouseenter mouseleave');$pp_pic_holder.find('.pp_gallery').hide();}}
|
||||
function _buildOverlay(caller){$('body').append(settings.markup);$pp_pic_holder=$('.pp_pic_holder'),$ppt=$('.ppt'),$pp_overlay=$('div.pp_overlay');if(isSet&&settings.overlay_gallery){currentGalleryPage=0;toInject="";for(var i=0;i<pp_images.length;i++){if(!pp_images[i].match(/\b(jpg|jpeg|png|gif)\b/gi)){classname='default';}else{classname='';}
|
||||
toInject+="<li class='"+classname+"'><a href='#'><img src='"+pp_images[i]+"' width='50' alt='' /></a></li>";};toInject=settings.gallery_markup.replace(/{gallery}/g,toInject);$pp_pic_holder.find('#pp_full_res').after(toInject);$pp_pic_holder.find('.pp_gallery .pp_arrow_next').click(function(){$.prettyPhoto.changeGalleryPage('next');$.prettyPhoto.stopSlideshow();return false;});$pp_pic_holder.find('.pp_gallery .pp_arrow_previous').click(function(){$.prettyPhoto.changeGalleryPage('previous');$.prettyPhoto.stopSlideshow();return false;});$pp_pic_holder.find('.pp_content').hover(function(){$pp_pic_holder.find('.pp_gallery:not(.disabled)').fadeIn();},function(){$pp_pic_holder.find('.pp_gallery:not(.disabled)').fadeOut();});itemWidth=52+5;$pp_pic_holder.find('.pp_gallery ul li').each(function(i){$(this).css({'position':'absolute','left':i*itemWidth});$(this).find('a').unbind('click').click(function(){$.prettyPhoto.changePage(i);$.prettyPhoto.stopSlideshow();return false;});});};if(settings.slideshow){$pp_pic_holder.find('.pp_nav').prepend('<a href="#" class="pp_play">Play</a>')
|
||||
$pp_pic_holder.find('.pp_nav .pp_play').click(function(){$.prettyPhoto.startSlideshow();return false;});}
|
||||
$pp_pic_holder.attr('class','pp_pic_holder '+settings.theme);$pp_overlay.css({'opacity':0,'height':$(document).height(),'width':$(window).width()}).bind('click',function(){if(!settings.modal)$.prettyPhoto.close();});$('a.pp_close').bind('click',function(){$.prettyPhoto.close();return false;});$('a.pp_expand').bind('click',function(e){if($(this).hasClass('pp_expand')){$(this).removeClass('pp_expand').addClass('pp_contract');doresize=false;}else{$(this).removeClass('pp_contract').addClass('pp_expand');doresize=true;};_hideContent(function(){$.prettyPhoto.open();});return false;});$pp_pic_holder.find('.pp_previous, .pp_nav .pp_arrow_previous').bind('click',function(){$.prettyPhoto.changePage('previous');$.prettyPhoto.stopSlideshow();return false;});$pp_pic_holder.find('.pp_next, .pp_nav .pp_arrow_next').bind('click',function(){$.prettyPhoto.changePage('next');$.prettyPhoto.stopSlideshow();return false;});_center_overlay();};return this.unbind('click.prettyphoto').bind('click.prettyphoto',$.prettyPhoto.initialize);};function grab_param(name,url){name=name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");var regexS="[\\?&]"+name+"=([^&#]*)";var regex=new RegExp(regexS);var results=regex.exec(url);return(results==null)?"":results[1];}})(jQuery);
|
||||
|
Before Width: | Height: | Size: 548 B |
|
Before Width: | Height: | Size: 545 B |
|
Before Width: | Height: | Size: 555 B |
|
Before Width: | Height: | Size: 547 B |
|
Before Width: | Height: | Size: 545 B |
|
Before Width: | Height: | Size: 536 B |
|
Before Width: | Height: | Size: 544 B |
|
Before Width: | Height: | Size: 550 B |
|
Before Width: | Height: | Size: 7.6 KiB |
@@ -10,8 +10,9 @@
|
||||
//--></script>
|
||||
|
||||
{{if request.function=='index':}}
|
||||
<h2>{{=T("Available databases and tables")}}</h2>
|
||||
<h2>{{=T("Available Databases and Tables")}}</h2>
|
||||
{{if not databases:}}{{=T("No databases in this application")}}{{pass}}
|
||||
<table>
|
||||
{{for db in sorted(databases):}}
|
||||
{{for table in databases[db].tables:}}
|
||||
{{qry='%s.%s.id>0'%(db,table)}}
|
||||
@@ -28,20 +29,24 @@
|
||||
{{qry=''}}
|
||||
{{pass}}
|
||||
{{pass}}
|
||||
<h3>
|
||||
{{=A("%s.%s" % (db,table),_href=URL('select',args=[db],vars=dict(query=qry)))}}
|
||||
</h3>
|
||||
[ {{=A(str(T('insert new'))+' '+table,_href=URL('insert',args=[db,table]))}} ]
|
||||
<br /><br />
|
||||
<tr>
|
||||
<th style="font-size: 1.75em;">
|
||||
{{=A("%s.%s" % (db,table),_href=URL('select',args=[db],vars=dict(query=qry)))}}
|
||||
</th>
|
||||
<td>
|
||||
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}}
|
||||
</td>
|
||||
</tr>
|
||||
{{pass}}
|
||||
</table>
|
||||
{{pass}}
|
||||
|
||||
{{elif request.function=='select':}}
|
||||
<h2>{{=XML(str(T("database %s select"))%A(request.args[0],_href=URL('index'))) }}
|
||||
<h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }}
|
||||
</h2>
|
||||
{{if table:}}
|
||||
[ {{=A(str(T('insert new %s'))%table,_href=URL('insert',args=[request.args[0],table]))}} ]<br/><br/>
|
||||
<h3>{{=T("Rows in table")}}</h3><br/>
|
||||
{{if table:}}
|
||||
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/>
|
||||
<h3>{{=T("Rows in Table")}}</h3><br/>
|
||||
{{else:}}
|
||||
<h3>{{=T("Rows selected")}}</h3><br/>
|
||||
{{pass}}
|
||||
@@ -51,8 +56,8 @@
|
||||
{{=T('"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN')}}</p>
|
||||
<br/><br/>
|
||||
<h4>{{=T("%s selected", nrows)}}</h4>
|
||||
{{if start>0:}}[ {{=A(T('previous 100 rows'),_href=URL('select',args=request.args[0],vars=dict(start=start-100)))}} ]{{pass}}
|
||||
{{if stop<nrows:}}[ {{=A(T('next 100 rows'),_href=URL('select',args=request.args[0],vars=dict(start=start+100)))}} ]{{pass}}
|
||||
{{if start>0:}}{{=A(T('previous 100 rows'),_href=URL('select',args=request.args[0],vars=dict(start=start-100)),_class="btn")}}{{pass}}
|
||||
{{if stop<nrows:}}{{=A(T('next 100 rows'),_href=URL('select',args=request.args[0],vars=dict(start=start+100)),_class="btn")}}{{pass}}
|
||||
{{if rows:}}
|
||||
<div style="overflow: auto;" width="80%">
|
||||
{{linkto=URL('update',args=request.args[0])}}
|
||||
@@ -61,35 +66,35 @@
|
||||
</div>
|
||||
{{pass}}
|
||||
<br/><br/><h3>{{=T("Import/Export")}}</h3><br/>
|
||||
[ <a href="{{=URL('csv',args=request.args[0],vars=dict(query=query))}}">{{=T("export as csv file")}}</a> ]
|
||||
<a href="{{=URL('csv',args=request.args[0],vars=dict(query=query))}}" class="btn">{{=T("export as csv file")}}</a>
|
||||
{{=formcsv or ''}}
|
||||
|
||||
{{elif request.function=='insert':}}
|
||||
<h2>{{=T("database")}} {{=A(request.args[0],_href=URL('index'))}}
|
||||
<h2>{{=T("Database")}} {{=A(request.args[0],_href=URL('index'))}}
|
||||
{{if hasattr(table,'_primarykey'):}}
|
||||
{{fieldname=table._primarykey[0]}}
|
||||
{{dbname=request.args[0]}}
|
||||
{{tablename=request.args[1]}}
|
||||
{{cond = table[fieldname].type in ['string','text'] and '!=""' or '>0'}}
|
||||
{{=T("table")}} {{=A(tablename,_href=URL('select',args=dbname,vars=dict(query='%s.%s.%s%s'%(dbname,tablename,fieldname,cond))))}}
|
||||
{{=T("Table")}} {{=A(tablename,_href=URL('select',args=dbname,vars=dict(query='%s.%s.%s%s'%(dbname,tablename,fieldname,cond))))}}
|
||||
{{else:}}
|
||||
{{=T("table")}} {{=A(request.args[1],_href=URL('select',args=request.args[0],vars=dict(query='%s.%s.id>0'%tuple(request.args[:2]))))}}
|
||||
{{=T("Table")}} {{=A(request.args[1],_href=URL('select',args=request.args[0],vars=dict(query='%s.%s.id>0'%tuple(request.args[:2]))))}}
|
||||
{{pass}}
|
||||
</h2>
|
||||
<h3>{{=T("New Record")}}</h3><br/>
|
||||
{{=form}}
|
||||
{{elif request.function=='update':}}
|
||||
<h2>{{=T("database")}} {{=A(request.args[0],_href=URL('index'))}}
|
||||
<h2>{{=T("Database")}} {{=A(request.args[0],_href=URL('index'))}}
|
||||
{{if hasattr(table,'_primarykey'):}}
|
||||
{{fieldname=request.vars.keys()[0]}}
|
||||
{{dbname=request.args[0]}}
|
||||
{{tablename=request.args[1]}}
|
||||
{{cond = table[fieldname].type in ['string','text'] and '!=""' or '>0'}}
|
||||
{{=T("table")}} {{=A(tablename,_href=URL('select',args=dbname,vars=dict(query='%s.%s.%s%s'%(dbname,tablename,fieldname,cond))))}}
|
||||
{{=T("record")}} {{=A('%s=%s'%request.vars.items()[0],_href=URL('update',args=request.args[:2],vars=request.vars))}}
|
||||
{{=T("Table")}} {{=A(tablename,_href=URL('select',args=dbname,vars=dict(query='%s.%s.%s%s'%(dbname,tablename,fieldname,cond))))}}
|
||||
{{=T("Record")}} {{=A('%s=%s'%request.vars.items()[0],_href=URL('update',args=request.args[:2],vars=request.vars))}}
|
||||
{{else:}}
|
||||
{{=T("table")}} {{=A(request.args[1],_href=URL('select',args=request.args[0],vars=dict(query='%s.%s.id>0'%tuple(request.args[:2]))))}}
|
||||
{{=T("record id")}} {{=A(request.args[2],_href=URL('update',args=request.args[:3]))}}
|
||||
{{=T("Table")}} {{=A(request.args[1],_href=URL('select',args=request.args[0],vars=dict(query='%s.%s.id>0'%tuple(request.args[:2]))))}}
|
||||
{{=T("Record id")}} {{=A(request.args[2],_href=URL('update',args=request.args[:3]))}}
|
||||
{{pass}}
|
||||
</h2>
|
||||
<h3>{{=T("Edit current record")}}</h3><br/><br/>{{=form}}
|
||||
|
||||
@@ -11,6 +11,8 @@ import copy
|
||||
import gluon.contenttype
|
||||
import gluon.fileutils
|
||||
|
||||
response.subtitle = 'Database Administration (appadmin)'
|
||||
|
||||
# ## critical --- make a copy of the environment
|
||||
|
||||
global_env = copy.copy(globals())
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
'%s selected': '%s označených',
|
||||
'Administrative interface': 'pro administrátorské rozhranie kliknite sem',
|
||||
'Are you sure you want to delete this object?': 'Opravdu chceš odstranit tento objekt?',
|
||||
'Available databases and tables': 'Dostupné databáze a tabuľky',
|
||||
'Available Databases and Tables': 'Dostupné databáze a tabuľky',
|
||||
'Cannot be empty': 'Nemůže být prázdné',
|
||||
'Change password': 'Změna hesla',
|
||||
'Check to delete': 'Označit ke smazání',
|
||||
@@ -68,7 +68,7 @@
|
||||
'Reset Password key': 'Nastavit registrační kľíč',
|
||||
'Retrieve username': 'Retrieve username',
|
||||
'Role': 'Role',
|
||||
'Rows in table': 'řádků v tabulce',
|
||||
'Rows in Table': 'řádků v tabulce',
|
||||
'Rows selected': 'označených řádků',
|
||||
'Stylesheet': 'CSS',
|
||||
'Submit': 'Odeslat',
|
||||
@@ -100,8 +100,8 @@
|
||||
'cache': 'cache',
|
||||
'customize me!': 'uprav mě!',
|
||||
'data uploaded': 'data nahrána',
|
||||
'database': 'databáze',
|
||||
'database %s select': 'databáze %s výber',
|
||||
'Database': 'databáze',
|
||||
'Database %s select': 'databáze %s výber',
|
||||
'db': 'db',
|
||||
'design': 'návrh',
|
||||
'done!': 'hotovo!',
|
||||
@@ -121,11 +121,11 @@
|
||||
'password': 'heslo',
|
||||
'previous 100 rows': 'předchádzajících 100 řádků',
|
||||
'profile': 'profil',
|
||||
'record': 'záznam',
|
||||
'Record': 'záznam',
|
||||
'record does not exist': 'záznam neexistuje',
|
||||
'record id': 'id záznamu',
|
||||
'Record id': 'id záznamu',
|
||||
'register': 'registrovat',
|
||||
'state': 'stav',
|
||||
'table': 'tabulka',
|
||||
'Table': 'tabulka',
|
||||
'unable to parse csv file': 'nedá sa zpracovat csv soubor',
|
||||
}
|
||||
|
||||
@@ -2,4 +2,15 @@
|
||||
{
|
||||
'!langcode!': 'en-us',
|
||||
'!langname!': 'English (US)',
|
||||
'%s %%(shop)': '%s %%(shop)',
|
||||
'%s %%(shop[0])': '%s %%(shop[0])',
|
||||
'%s %%{shop[0]}': '%s %%{shop[0]}',
|
||||
'%s %%{shop}': '%s %%{shop}',
|
||||
'%Y-%m-%d %H:%M:%S': '%Y-%m-%d %H:%M:%S',
|
||||
'@markmin\x01**Hello World**': '**Hello World**',
|
||||
'enter an integer between %(min)g and %(max)g': 'enter an integer between %(min)g and %(max)g',
|
||||
'enter date and time as %(format)s': 'enter date and time as %(format)s',
|
||||
'Hello World': 'Hello World',
|
||||
'Hello World ## comment': 'Hello World ',
|
||||
'Hello World## comment': 'Hello World',
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
'Are you sure you want to uninstall application "%s"': '¿Está seguro que desea desinstalar la aplicación "%s"',
|
||||
'Are you sure you want to uninstall application "%s"?': '¿Está seguro que desea desinstalar la aplicación "%s"?',
|
||||
'Authentication': 'Autenticación',
|
||||
'Available databases and tables': 'Bases de datos y tablas disponibles',
|
||||
'Available Databases and Tables': 'Bases de datos y tablas disponibles',
|
||||
'Cannot be empty': 'No puede estar vacío',
|
||||
'Cannot compile: there are errors in your app. Debug it, correct errors and try again.': 'No se puede compilar: hay errores en su aplicación. Depure, corrija errores y vuelva a intentarlo.',
|
||||
'Change Password': 'Cambie Contraseña',
|
||||
@@ -99,7 +99,7 @@
|
||||
'Reset Password key': 'Reset Password key',
|
||||
'Resolve Conflict file': 'archivo Resolución de Conflicto',
|
||||
'Role': 'Rol',
|
||||
'Rows in table': 'Filas en la tabla',
|
||||
'Rows in Table': 'Filas en la tabla',
|
||||
'Rows selected': 'Filas seleccionadas',
|
||||
'Saved file hash:': 'Hash del archivo guardado:',
|
||||
'Static files': 'Archivos estáticos',
|
||||
@@ -165,8 +165,8 @@
|
||||
'currently saved or': 'actualmente guardado o',
|
||||
'customize me!': 'Adaptame!',
|
||||
'data uploaded': 'datos subidos',
|
||||
'database': 'base de datos',
|
||||
'database %s select': 'selección en base de datos %s',
|
||||
'Database': 'base de datos',
|
||||
'Database %s select': 'selección en base de datos %s',
|
||||
'database administration': 'administración base de datos',
|
||||
'db': 'db',
|
||||
'defines tables': 'define tablas',
|
||||
@@ -218,9 +218,9 @@
|
||||
'pack all': 'empaquetar todo',
|
||||
'pack compiled': 'empaquete compiladas',
|
||||
'previous 100 rows': '100 filas anteriores',
|
||||
'record': 'registro',
|
||||
'Record': 'registro',
|
||||
'record does not exist': 'el registro no existe',
|
||||
'record id': 'id de registro',
|
||||
'Record id': 'id de registro',
|
||||
'register': 'registrese',
|
||||
'remove compiled': 'eliminar compiladas',
|
||||
'restore': 'restaurar',
|
||||
@@ -232,7 +232,7 @@
|
||||
'some files could not be removed': 'algunos archivos no pudieron ser removidos',
|
||||
'state': 'estado',
|
||||
'static': 'estáticos',
|
||||
'table': 'tabla',
|
||||
'Table': 'tabla',
|
||||
'test': 'probar',
|
||||
'the application logic, each URL path is mapped in one exposed function in the controller': 'la lógica de la aplicación, cada ruta URL se mapea en una función expuesta en el controlador',
|
||||
'the data representation, define database tables and sets': 'la representación de datos, define tablas y conjuntos de base de datos',
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
'Ajax Recipes': 'Recettes Ajax',
|
||||
'Are you sure you want to delete this object?': 'Êtes-vous sûr de vouloir supprimer cet objet?',
|
||||
'Authentication': 'Authentification',
|
||||
'Available databases and tables': 'Bases de données et tables disponibles',
|
||||
'Available Databases and Tables': 'Bases de données et tables disponibles',
|
||||
'Buy this book': 'Acheter ce livre',
|
||||
'Cannot be empty': 'Ne peut pas être vide',
|
||||
'Check to delete': 'Cliquez pour supprimer',
|
||||
@@ -95,7 +95,7 @@
|
||||
'Reset Password key': 'Réinitialiser le mot clé',
|
||||
'Resources': 'Ressources',
|
||||
'Role': 'Rôle',
|
||||
'Rows in table': 'Lignes du tableau',
|
||||
'Rows in Table': 'Lignes du tableau',
|
||||
'Rows selected': 'Lignes sélectionnées',
|
||||
'Semantic': 'Sémantique',
|
||||
'Services': 'Services',
|
||||
@@ -135,8 +135,8 @@
|
||||
'change password': 'changer le mot de passe',
|
||||
'customize me!': 'personnalisez-moi!',
|
||||
'data uploaded': 'données téléchargées',
|
||||
'database': 'base de données',
|
||||
'database %s select': 'base de données %s select',
|
||||
'Database': 'base de données',
|
||||
'Database %s select': 'base de données %s select',
|
||||
'db': 'db',
|
||||
'design': 'design',
|
||||
'done!': 'fait!',
|
||||
@@ -157,12 +157,12 @@
|
||||
'please input your password again': "S'il vous plaît entrer votre mot de passe",
|
||||
'previous 100 rows': '100 lignes précédentes',
|
||||
'profile': 'profile',
|
||||
'record': 'enregistrement',
|
||||
'Record': 'enregistrement',
|
||||
'record does not exist': "l'archive n'existe pas",
|
||||
'record id': "id d'enregistrement",
|
||||
'Record id': "id d'enregistrement",
|
||||
'register': "s'inscrire",
|
||||
'state': 'état',
|
||||
'table': 'tableau',
|
||||
'Table': 'tableau',
|
||||
'unable to parse csv file': "incapable d'analyser le fichier cvs",
|
||||
'value already in database or empty': 'valeur déjà dans la base ou vide',
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
'appadmin is disabled because insecure channel': "appadmin est désactivée parce que le canal n'est pas sécurisé",
|
||||
'Are you sure you want to delete this object?': 'Êtes-vous sûr de vouloir supprimer cet objet?',
|
||||
'Authentication': 'Authentification',
|
||||
'Available databases and tables': 'Bases de données et tables disponibles',
|
||||
'Available Databases and Tables': 'Bases de données et tables disponibles',
|
||||
'Buy this book': 'Acheter ce livre',
|
||||
'cache': 'cache',
|
||||
'Cannot be empty': 'Ne peut pas être vide',
|
||||
@@ -34,8 +34,8 @@
|
||||
'customize me!': 'personnalisez-moi!',
|
||||
'data uploaded': 'données téléchargées',
|
||||
'Database': 'Base de données',
|
||||
'database': 'base de données',
|
||||
'database %s select': 'base de données %s select',
|
||||
'Database': 'base de données',
|
||||
'Database %s select': 'base de données %s select',
|
||||
'db': 'db',
|
||||
'DB Model': 'Modèle DB',
|
||||
'Delete:': 'Supprimer:',
|
||||
@@ -114,9 +114,9 @@
|
||||
'Quick Examples': 'Examples Rapides',
|
||||
'Readme': 'Lisez-moi',
|
||||
'Recipes': 'Recettes',
|
||||
'record': 'enregistrement',
|
||||
'Record': 'enregistrement',
|
||||
'record does not exist': "l'archive n'existe pas",
|
||||
'record id': "id d'enregistrement",
|
||||
'Record id': "id d'enregistrement",
|
||||
'Record ID': "ID d'enregistrement",
|
||||
'Register': "S'inscrire",
|
||||
'register': "s'inscrire",
|
||||
@@ -127,7 +127,7 @@
|
||||
'Reset Password key': 'Réinitialiser le mot clé',
|
||||
'Resources': 'Ressources',
|
||||
'Role': 'Rôle',
|
||||
'Rows in table': 'Lignes du tableau',
|
||||
'Rows in Table': 'Lignes du tableau',
|
||||
'Rows selected': 'Lignes sélectionnées',
|
||||
'Semantic': 'Sémantique',
|
||||
'Services': 'Services',
|
||||
@@ -136,7 +136,7 @@
|
||||
'Submit': 'Soumettre',
|
||||
'Support': 'Support',
|
||||
'Sure you want to delete this object?': 'Êtes-vous sûr de vouloir supprimer cet objet?',
|
||||
'table': 'tableau',
|
||||
'Table': 'tableau',
|
||||
'Table name': 'Nom du tableau',
|
||||
'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La "query" est une condition comme "db.table1.champ1==\'valeur\'". Quelque chose comme "db.table1.champ1==db.table2.champ2" résulte en un JOIN SQL.',
|
||||
'The Core': 'Le noyau',
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
'%s %%{row} updated': '%s पंक्तियाँ अद्यतन',
|
||||
'%s selected': '%s चुना हुआ',
|
||||
'Administrative interface': 'प्रशासनिक इंटरफेस के लिए यहाँ क्लिक करें',
|
||||
'Available databases and tables': 'उपलब्ध डेटाबेस और तालिका',
|
||||
'Available Databases and Tables': 'उपलब्ध डेटाबेस और तालिका',
|
||||
'Cannot be empty': 'खाली नहीं हो सकता',
|
||||
'Change Password': 'पासवर्ड बदलें',
|
||||
'Check to delete': 'हटाने के लिए चुनें',
|
||||
@@ -43,7 +43,7 @@
|
||||
'Powered by': 'Powered by',
|
||||
'Query:': 'प्रश्न:',
|
||||
'Register': 'पंजीकृत (रजिस्टर) करना ',
|
||||
'Rows in table': 'तालिका में पंक्तियाँ ',
|
||||
'Rows in Table': 'तालिका में पंक्तियाँ ',
|
||||
'Rows selected': 'चयनित (चुने गये) पंक्तियाँ ',
|
||||
'Stylesheet': 'Stylesheet',
|
||||
'Sure you want to delete this object?': 'सुनिश्चित हैं कि आप इस वस्तु को हटाना चाहते हैं?',
|
||||
@@ -61,8 +61,8 @@
|
||||
'change password': 'change password',
|
||||
'customize me!': 'मुझे अनुकूलित (कस्टमाइज़) करें!',
|
||||
'data uploaded': 'डाटा अपलोड सम्पन्न ',
|
||||
'database': 'डेटाबेस',
|
||||
'database %s select': 'डेटाबेस %s चुनी हुई',
|
||||
'Database': 'डेटाबेस',
|
||||
'Database %s select': 'डेटाबेस %s चुनी हुई',
|
||||
'db': 'db',
|
||||
'design': 'रचना करें',
|
||||
'done!': 'हो गया!',
|
||||
@@ -77,11 +77,11 @@
|
||||
'next 100 rows': 'अगले 100 पंक्तियाँ',
|
||||
'or import from csv file': 'या csv फ़ाइल से आयात',
|
||||
'previous 100 rows': 'पिछले 100 पंक्तियाँ',
|
||||
'record': 'record',
|
||||
'Record': 'Record',
|
||||
'record does not exist': 'रिकॉर्ड मौजूद नहीं है',
|
||||
'record id': 'रिकॉर्ड पहचानकर्ता (आईडी)',
|
||||
'Record id': 'रिकॉर्ड पहचानकर्ता (आईडी)',
|
||||
'register': 'register',
|
||||
'state': 'स्थिति',
|
||||
'table': 'तालिका',
|
||||
'Table': 'तालिका',
|
||||
'unable to parse csv file': 'csv फ़ाइल पार्स करने में असमर्थ',
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
'%s %%{row} updated': '%s sorok frissítődtek',
|
||||
'%s selected': '%s kiválasztott',
|
||||
'Administrative interface': 'az adminisztrációs felületért kattints ide',
|
||||
'Available databases and tables': 'Elérhető adatbázisok és táblák',
|
||||
'Available Databases and Tables': 'Elérhető adatbázisok és táblák',
|
||||
'Cannot be empty': 'Nem lehet üres',
|
||||
'Check to delete': 'Törléshez válaszd ki',
|
||||
'Client IP': 'Client IP',
|
||||
@@ -50,7 +50,7 @@
|
||||
'Registration key': 'Registration key',
|
||||
'Reset Password key': 'Reset Password key',
|
||||
'Role': 'Role',
|
||||
'Rows in table': 'Sorok a táblában',
|
||||
'Rows in Table': 'Sorok a táblában',
|
||||
'Rows selected': 'Kiválasztott sorok',
|
||||
'Stylesheet': 'Stylesheet',
|
||||
'Sure you want to delete this object?': 'Biztos törli ezt az objektumot?',
|
||||
@@ -71,8 +71,8 @@
|
||||
'change password': 'jelszó megváltoztatása',
|
||||
'customize me!': 'változtass meg!',
|
||||
'data uploaded': 'adat feltöltve',
|
||||
'database': 'adatbázis',
|
||||
'database %s select': 'adatbázis %s kiválasztás',
|
||||
'Database': 'adatbázis',
|
||||
'Database %s select': 'adatbázis %s kiválasztás',
|
||||
'db': 'db',
|
||||
'design': 'design',
|
||||
'done!': 'kész!',
|
||||
@@ -88,11 +88,11 @@
|
||||
'next 100 rows': 'következő 100 sor',
|
||||
'or import from csv file': 'vagy betöltés csv fájlból',
|
||||
'previous 100 rows': 'előző 100 sor',
|
||||
'record': 'bejegyzés',
|
||||
'Record': 'bejegyzés',
|
||||
'record does not exist': 'bejegyzés nem létezik',
|
||||
'record id': 'bejegyzés id',
|
||||
'Record id': 'bejegyzés id',
|
||||
'register': 'regisztráció',
|
||||
'state': 'állapot',
|
||||
'table': 'tábla',
|
||||
'Table': 'tábla',
|
||||
'unable to parse csv file': 'nem lehet a csv fájlt beolvasni',
|
||||
}
|
||||
|
||||
@@ -9,99 +9,178 @@
|
||||
'%s selected': '%s selezionato',
|
||||
'%Y-%m-%d': '%d/%m/%Y',
|
||||
'%Y-%m-%d %H:%M:%S': '%d/%m/%Y %H:%M:%S',
|
||||
'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**',
|
||||
'About': 'About',
|
||||
'Access Control': 'Access Control',
|
||||
'Administrative Interface': 'Administrative Interface',
|
||||
'Administrative interface': 'Interfaccia amministrativa',
|
||||
'Ajax Recipes': 'Ajax Recipes',
|
||||
'appadmin is disabled because insecure channel': 'Amministrazione (appadmin) disabilitata: comunicazione non sicura',
|
||||
'Available databases and tables': 'Database e tabelle disponibili',
|
||||
'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?',
|
||||
'Available Databases and Tables': 'Database e tabelle disponibili',
|
||||
'Buy this book': 'Buy this book',
|
||||
'cache': 'cache',
|
||||
'Cannot be empty': 'Non può essere vuoto',
|
||||
'change password': 'Cambia password',
|
||||
'Check to delete': 'Seleziona per cancellare',
|
||||
'Clear CACHE?': 'Clear CACHE?',
|
||||
'Clear DISK': 'Clear DISK',
|
||||
'Clear RAM': 'Clear RAM',
|
||||
'Client IP': 'Client IP',
|
||||
'Community': 'Community',
|
||||
'Components and Plugins': 'Components and Plugins',
|
||||
'Controller': 'Controller',
|
||||
'Copyright': 'Copyright',
|
||||
'Created By': 'Created By',
|
||||
'Created On': 'Created On',
|
||||
'Current request': 'Richiesta (request) corrente',
|
||||
'Current response': 'Risposta (response) corrente',
|
||||
'Current session': 'Sessione (session) corrente',
|
||||
'customize me!': 'Personalizzami!',
|
||||
'data uploaded': 'dati caricati',
|
||||
'Database': 'Database',
|
||||
'database': 'database',
|
||||
'database %s select': 'database %s select',
|
||||
'Database %s select': 'Database %s select',
|
||||
'db': 'db',
|
||||
'DB Model': 'Modello di DB',
|
||||
'Delete': 'Delete',
|
||||
'Delete:': 'Cancella:',
|
||||
'Demo': 'Demo',
|
||||
'Deployment Recipes': 'Deployment Recipes',
|
||||
'Description': 'Descrizione',
|
||||
'design': 'progetta',
|
||||
'DISK': 'DISK',
|
||||
'Disk Cleared': 'Disk Cleared',
|
||||
'Documentation': 'Documentazione',
|
||||
"Don't know what to do?": "Don't know what to do?",
|
||||
'done!': 'fatto!',
|
||||
'Download': 'Download',
|
||||
'E-mail': 'E-mail',
|
||||
'Edit': 'Modifica',
|
||||
'Edit current record': 'Modifica record corrente',
|
||||
'edit profile': 'modifica profilo',
|
||||
'Edit This App': 'Modifica questa applicazione',
|
||||
'Email and SMS': 'Email and SMS',
|
||||
'enter an integer between %(min)g and %(max)g': 'enter an integer between %(min)g and %(max)g',
|
||||
'Errors': 'Errors',
|
||||
'export as csv file': 'esporta come file CSV',
|
||||
'FAQ': 'FAQ',
|
||||
'First name': 'Nome',
|
||||
'Forms and Validators': 'Forms and Validators',
|
||||
'Free Applications': 'Free Applications',
|
||||
'Group %(group_id)s created': 'Group %(group_id)s created',
|
||||
'Group ID': 'ID Gruppo',
|
||||
'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s',
|
||||
'Groups': 'Groups',
|
||||
'hello': 'hello',
|
||||
'hello world': 'salve mondo',
|
||||
'Hello World': 'Salve Mondo',
|
||||
'Hello World in a flash!': 'Salve Mondo in un flash!',
|
||||
'Home': 'Home',
|
||||
'How did you get here?': 'How did you get here?',
|
||||
'import': 'import',
|
||||
'Import/Export': 'Importa/Esporta',
|
||||
'Index': 'Indice',
|
||||
'insert new': 'inserisci nuovo',
|
||||
'insert new %s': 'inserisci nuovo %s',
|
||||
'Internal State': 'Stato interno',
|
||||
'Introduction': 'Introduction',
|
||||
'Invalid email': 'Email non valida',
|
||||
'Invalid login': 'Invalid login',
|
||||
'Invalid Query': 'Richiesta (query) non valida',
|
||||
'invalid request': 'richiesta non valida',
|
||||
'Is Active': 'Is Active',
|
||||
'Last name': 'Cognome',
|
||||
'Layout': 'Layout',
|
||||
'Layout Plugins': 'Layout Plugins',
|
||||
'Layouts': 'Layouts',
|
||||
'Live Chat': 'Live Chat',
|
||||
'Logged in': 'Logged in',
|
||||
'login': 'accesso',
|
||||
'Login': 'Login',
|
||||
'logout': 'uscita',
|
||||
'Logout': 'Logout',
|
||||
'Lost Password': 'Lost Password',
|
||||
'Lost password?': 'Lost password?',
|
||||
'lost password?': 'dimenticato la password?',
|
||||
'Main Menu': 'Menu principale',
|
||||
'Manage Cache': 'Manage Cache',
|
||||
'Menu Model': 'Menu Modelli',
|
||||
'Modified By': 'Modified By',
|
||||
'Modified On': 'Modified On',
|
||||
'My Sites': 'My Sites',
|
||||
'Name': 'Nome',
|
||||
'New Record': 'Nuovo elemento (record)',
|
||||
'new record inserted': 'nuovo record inserito',
|
||||
'next 100 rows': 'prossime 100 righe',
|
||||
'No databases in this application': 'Nessun database presente in questa applicazione',
|
||||
'not authorized': 'non autorizzato',
|
||||
'Object or table name': 'Object or table name',
|
||||
'Online examples': 'Vedere gli esempi',
|
||||
'or import from csv file': 'oppure importa da file CSV',
|
||||
'Origin': 'Origine',
|
||||
'Other Plugins': 'Other Plugins',
|
||||
'Other Recipes': 'Other Recipes',
|
||||
'Overview': 'Overview',
|
||||
'Password': 'Password',
|
||||
"Password fields don't match": "Password fields don't match",
|
||||
'please input your password again': 'please input your password again',
|
||||
'Plugins': 'Plugins',
|
||||
'Powered by': 'Powered by',
|
||||
'Preface': 'Preface',
|
||||
'previous 100 rows': '100 righe precedenti',
|
||||
'Profile': 'Profile',
|
||||
'Python': 'Python',
|
||||
'Query:': 'Richiesta (query):',
|
||||
'record': 'record',
|
||||
'Quick Examples': 'Quick Examples',
|
||||
'RAM': 'RAM',
|
||||
'Ram Cleared': 'Ram Cleared',
|
||||
'Recipes': 'Recipes',
|
||||
'Record': 'Record',
|
||||
'record does not exist': 'il record non esiste',
|
||||
'record id': 'record id',
|
||||
'Record ID': 'Record ID',
|
||||
'Record id': 'Record id',
|
||||
'Register': 'Register',
|
||||
'register': 'registrazione',
|
||||
'Registration identifier': 'Registration identifier',
|
||||
'Registration key': 'Chiave di Registazione',
|
||||
'Registration successful': 'Registration successful',
|
||||
'Remember me (for 30 days)': 'Remember me (for 30 days)',
|
||||
'Reset Password key': 'Resetta chiave Password ',
|
||||
'Role': 'Ruolo',
|
||||
'Rows in table': 'Righe nella tabella',
|
||||
'Rows in Table': 'Righe nella tabella',
|
||||
'Rows selected': 'Righe selezionate',
|
||||
'Semantic': 'Semantic',
|
||||
'Services': 'Services',
|
||||
'state': 'stato',
|
||||
'Stylesheet': 'Foglio di stile (stylesheet)',
|
||||
'submit': 'submit',
|
||||
'Submit': 'Submit',
|
||||
'Support': 'Support',
|
||||
'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?',
|
||||
'table': 'tabella',
|
||||
'Table': 'tabella',
|
||||
'Table name': 'Nome tabella',
|
||||
'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La richiesta (query) è una condizione come ad esempio "db.tabella1.campo1==\'valore\'". Una condizione come "db.tabella1.campo1==db.tabella2.campo2" produce un "JOIN" SQL.',
|
||||
'The Core': 'The Core',
|
||||
'The output of the file is a dictionary that was rendered by the view %s': 'L\'output del file è un "dictionary" che è stato visualizzato dalla vista %s',
|
||||
'The Views': 'The Views',
|
||||
'This App': 'This App',
|
||||
'This is a copy of the scaffolding application': "Questa è una copia dell'applicazione di base (scaffold)",
|
||||
'Timestamp': 'Ora (timestamp)',
|
||||
'Twitter': 'Twitter',
|
||||
'unable to parse csv file': 'non riesco a decodificare questo file CSV',
|
||||
'Update': 'Update',
|
||||
'Update:': 'Aggiorna:',
|
||||
'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Per costruire richieste (query) più complesse si usano (...)&(...) come "e" (AND), (...)|(...) come "o" (OR), e ~(...) come negazione (NOT).',
|
||||
'User %(id)s Logged-in': 'User %(id)s Logged-in',
|
||||
'User %(id)s Registered': 'User %(id)s Registered',
|
||||
'User ID': 'ID Utente',
|
||||
'Verify Password': 'Verify Password',
|
||||
'Videos': 'Videos',
|
||||
'View': 'Vista',
|
||||
'Welcome': 'Welcome',
|
||||
'Welcome %s': 'Benvenuto %s',
|
||||
'Welcome to web2py': 'Benvenuto su web2py',
|
||||
'Welcome to web2py!': 'Welcome to web2py!',
|
||||
'Which called the function %s located in the file %s': 'che ha chiamato la funzione %s presente nel file %s',
|
||||
'You are successfully running web2py': 'Stai eseguendo web2py con successo',
|
||||
'You can modify this application and adapt it to your needs': 'Puoi modificare questa applicazione adattandola alle tue necessità',
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'LET OP: TESTEN IS NIET THREAD SAFE, PROBEER NIET GELIJKTIJDIG MEERDERE TESTS TE DOEN.',
|
||||
'ATTENTION: you cannot edit the running application!': 'LET OP: je kan de applicatie die nu draait niet editen!',
|
||||
'Authentication': 'Authenticatie',
|
||||
'Available databases and tables': 'Beschikbare databases en tabellen',
|
||||
'Available Databases and Tables': 'Beschikbare databases en tabellen',
|
||||
'Back': 'Terug',
|
||||
'Buy this book': 'Koop dit boek',
|
||||
'Cache': 'Cache',
|
||||
@@ -95,8 +95,8 @@
|
||||
'customize me!': 'pas me aan!',
|
||||
'data uploaded': 'data geupload',
|
||||
'Database': 'Database',
|
||||
'database': 'database',
|
||||
'database %s select': 'database %s select',
|
||||
'Database': 'Database',
|
||||
'Database %s select': 'Database %s select',
|
||||
'database administration': 'database administratie',
|
||||
'Date and Time': 'Datum en Tijd',
|
||||
'db': 'db',
|
||||
@@ -260,9 +260,9 @@
|
||||
'RAM Cache Keys': 'RAM Cache Keys',
|
||||
'Ram Cleared': 'Ram Geleegd',
|
||||
'Recipes': 'Recepten',
|
||||
'record': 'record',
|
||||
'Record': 'Record',
|
||||
'record does not exist': 'record bestaat niet',
|
||||
'record id': 'record id',
|
||||
'Record id': 'Record id',
|
||||
'Record ID': 'Record ID',
|
||||
'register': 'registreer',
|
||||
'Register': 'Registreer',
|
||||
@@ -277,7 +277,7 @@
|
||||
'restore': 'herstel',
|
||||
'revert': 'herstel',
|
||||
'Role': 'Rol',
|
||||
'Rows in table': 'Rijen in tabel',
|
||||
'Rows in Table': 'Rijen in tabel',
|
||||
'Rows selected': 'Rijen geselecteerd',
|
||||
'save': 'bewaar',
|
||||
'Save profile': 'Bewaar profiel',
|
||||
@@ -300,7 +300,7 @@
|
||||
'submit': 'submit',
|
||||
'Support': 'Support',
|
||||
'Sure you want to delete this object?': 'Weet je zeker dat je dit object wilt verwijderen?',
|
||||
'table': 'Tabel',
|
||||
'Table': 'Tabel',
|
||||
'Table name': 'Tabelnaam',
|
||||
'test': 'test',
|
||||
'Testing application': 'Applicatie testen',
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
'%s selected': '%s wybranych',
|
||||
'Administrative interface': 'Kliknij aby przejść do panelu administracyjnego',
|
||||
'Authentication': 'Uwierzytelnienie',
|
||||
'Available databases and tables': 'Dostępne bazy danych i tabele',
|
||||
'Available Databases and Tables': 'Dostępne bazy danych i tabele',
|
||||
'Cannot be empty': 'Nie może być puste',
|
||||
'Change Password': 'Zmień hasło',
|
||||
'Check to delete': 'Zaznacz aby usunąć',
|
||||
@@ -59,7 +59,7 @@
|
||||
'Register': 'Zarejestruj',
|
||||
'Registration key': 'Klucz rejestracji',
|
||||
'Role': 'Rola',
|
||||
'Rows in table': 'Wiersze w tabeli',
|
||||
'Rows in Table': 'Wiersze w tabeli',
|
||||
'Rows selected': 'Wybrane wiersze',
|
||||
'Stylesheet': 'Arkusz stylów',
|
||||
'Submit': 'Wyślij',
|
||||
@@ -83,8 +83,8 @@
|
||||
'change password': 'change password',
|
||||
'customize me!': 'dostosuj mnie!',
|
||||
'data uploaded': 'dane wysłane',
|
||||
'database': 'baza danych',
|
||||
'database %s select': 'wybór z bazy danych %s',
|
||||
'Database': 'baza danych',
|
||||
'Database %s select': 'wybór z bazy danych %s',
|
||||
'db': 'baza danych',
|
||||
'design': 'projektuj',
|
||||
'done!': 'zrobione!',
|
||||
@@ -99,11 +99,11 @@
|
||||
'next 100 rows': 'następne 100 wierszy',
|
||||
'or import from csv file': 'lub zaimportuj z pliku csv',
|
||||
'previous 100 rows': 'poprzednie 100 wierszy',
|
||||
'record': 'rekord',
|
||||
'Record': 'rekord',
|
||||
'record does not exist': 'rekord nie istnieje',
|
||||
'record id': 'id rekordu',
|
||||
'Record id': 'id rekordu',
|
||||
'register': 'register',
|
||||
'state': 'stan',
|
||||
'table': 'tabela',
|
||||
'Table': 'tabela',
|
||||
'unable to parse csv file': 'nie można sparsować pliku csv',
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
'Access Control': 'Access Control',
|
||||
'Administrative interface': 'Interface administrativa',
|
||||
'Ajax Recipes': 'Ajax Recipes',
|
||||
'Available databases and tables': 'Bancos de dados e tabelas disponíveis',
|
||||
'Available Databases and Tables': 'Bancos de dados e tabelas disponíveis',
|
||||
'Buy this book': 'Buy this book',
|
||||
'Cannot be empty': 'Não pode ser vazio',
|
||||
'Check to delete': 'Marque para apagar',
|
||||
@@ -79,7 +79,7 @@
|
||||
'Reset Password key': 'Reset Password key',
|
||||
'Resources': 'Resources',
|
||||
'Role': 'Role',
|
||||
'Rows in table': 'Linhas na tabela',
|
||||
'Rows in Table': 'Linhas na tabela',
|
||||
'Rows selected': 'Linhas selecionadas',
|
||||
'Semantic': 'Semantic',
|
||||
'Services': 'Services',
|
||||
@@ -115,8 +115,8 @@
|
||||
'change password': 'modificar senha',
|
||||
'customize me!': 'Personalize-me!',
|
||||
'data uploaded': 'dados enviados',
|
||||
'database': 'banco de dados',
|
||||
'database %s select': 'Selecionar banco de dados %s',
|
||||
'Database': 'banco de dados',
|
||||
'Database %s select': 'Selecionar banco de dados %s',
|
||||
'db': 'bd',
|
||||
'design': 'design',
|
||||
'done!': 'concluído!',
|
||||
@@ -132,11 +132,11 @@
|
||||
'next 100 rows': 'próximas 100 linhas',
|
||||
'or import from csv file': 'ou importar de um arquivo csv',
|
||||
'previous 100 rows': '100 linhas anteriores',
|
||||
'record': 'registro',
|
||||
'Record': 'registro',
|
||||
'record does not exist': 'registro não existe',
|
||||
'record id': 'id do registro',
|
||||
'Record id': 'id do registro',
|
||||
'register': 'Registre-se',
|
||||
'state': 'estado',
|
||||
'table': 'tabela',
|
||||
'Table': 'tabela',
|
||||
'unable to parse csv file': 'não foi possível analisar arquivo csv',
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
'Administrative interface': 'Painel administrativo',
|
||||
'Author Reference Auth User': 'Author Reference Auth User',
|
||||
'Author Reference Auth User.username': 'Author Reference Auth User.username',
|
||||
'Available databases and tables': 'bases de dados e tabelas disponíveis',
|
||||
'Available Databases and Tables': 'bases de dados e tabelas disponíveis',
|
||||
'Cannot be empty': 'não pode ser vazio',
|
||||
'Category Create': 'Category Create',
|
||||
'Category Select': 'Category Select',
|
||||
@@ -58,7 +58,7 @@
|
||||
'Powered by': 'Suportado por',
|
||||
'Query:': 'Interrogação:',
|
||||
'Replyto Reference Post': 'Replyto Reference Post',
|
||||
'Rows in table': 'Linhas numa tabela',
|
||||
'Rows in Table': 'Linhas numa tabela',
|
||||
'Rows selected': 'Linhas seleccionadas',
|
||||
'Stylesheet': 'Folha de estilo',
|
||||
'Sure you want to delete this object?': 'Tem a certeza que deseja eliminar este objecto?',
|
||||
@@ -83,8 +83,8 @@
|
||||
'create new post': 'create new post',
|
||||
'customize me!': 'Personaliza-me!',
|
||||
'data uploaded': 'informação enviada',
|
||||
'database': 'base de dados',
|
||||
'database %s select': 'selecção de base de dados %s',
|
||||
'Database': 'base de dados',
|
||||
'Database %s select': 'selecção de base de dados %s',
|
||||
'db': 'bd',
|
||||
'design': 'design',
|
||||
'done!': 'concluído!',
|
||||
@@ -102,9 +102,9 @@
|
||||
'next 100 rows': 'próximas 100 linhas',
|
||||
'or import from csv file': 'ou importe a partir de ficheiro csv',
|
||||
'previous 100 rows': '100 linhas anteriores',
|
||||
'record': 'registo',
|
||||
'Record': 'registo',
|
||||
'record does not exist': 'registo inexistente',
|
||||
'record id': 'id de registo',
|
||||
'Record id': 'id de registo',
|
||||
'register': 'register',
|
||||
'search category': 'search category',
|
||||
'search comment': 'search comment',
|
||||
@@ -116,6 +116,6 @@
|
||||
'show comment': 'show comment',
|
||||
'show post': 'show post',
|
||||
'state': 'estado',
|
||||
'table': 'tabela',
|
||||
'Table': 'tabela',
|
||||
'unable to parse csv file': 'não foi possível carregar ficheiro csv',
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENȚIE: Nu puteți efectua mai multe teste o dată deoarece lansarea în execuție a mai multor subpocese nu este sigură.',
|
||||
'ATTENTION: you cannot edit the running application!': 'ATENȚIE: nu puteți edita o aplicație în curs de execuție!',
|
||||
'Authentication': 'Autentificare',
|
||||
'Available databases and tables': 'Baze de date și tabele disponibile',
|
||||
'Available Databases and Tables': 'Baze de date și tabele disponibile',
|
||||
'Back': 'Înapoi',
|
||||
'Buy this book': 'Cumpără această carte',
|
||||
'cache': 'cache',
|
||||
@@ -92,8 +92,8 @@
|
||||
'customize me!': 'Personalizează-mă!',
|
||||
'data uploaded': 'date încărcate',
|
||||
'Database': 'Baza de date',
|
||||
'database': 'bază de date',
|
||||
'database %s select': 'selectare bază de date %s',
|
||||
'Database': 'bază de date',
|
||||
'Database %s select': 'selectare bază de date %s',
|
||||
'database administration': 'administrare bază de date',
|
||||
'Date and Time': 'Data și ora',
|
||||
'db': 'db',
|
||||
@@ -247,9 +247,9 @@
|
||||
'Quick Examples': 'Exemple rapide',
|
||||
'RAM Cache Keys': 'Chei cache RAM',
|
||||
'Recipes': 'Rețete',
|
||||
'record': 'înregistrare',
|
||||
'Record': 'înregistrare',
|
||||
'record does not exist': 'înregistrare inexistentă',
|
||||
'record id': 'id înregistrare',
|
||||
'Record id': 'id înregistrare',
|
||||
'Record ID': 'ID înregistrare',
|
||||
'register': 'înregistrare',
|
||||
'Register': 'Înregistrare',
|
||||
@@ -264,7 +264,7 @@
|
||||
'restore': 'restaurare',
|
||||
'revert': 'revenire',
|
||||
'Role': 'Rol',
|
||||
'Rows in table': 'Linii în tabel',
|
||||
'Rows in Table': 'Linii în tabel',
|
||||
'Rows selected': 'Linii selectate',
|
||||
'save': 'salvare',
|
||||
'Save profile': 'Salvează profil',
|
||||
@@ -284,7 +284,7 @@
|
||||
'Submit': 'Înregistrează',
|
||||
'Support': 'Suport',
|
||||
'Sure you want to delete this object?': 'Sigur ștergeți acest obiect?',
|
||||
'table': 'tabel',
|
||||
'Table': 'tabel',
|
||||
'Table name': 'Nume tabel',
|
||||
'test': 'test',
|
||||
'Testing application': 'Testare aplicație',
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
'Administrative interface': 'административный интерфейс',
|
||||
'Ajax Recipes': 'Ajax Recipes',
|
||||
'Are you sure you want to delete this object?': 'Вы уверены, что хотите удалить этот объект?',
|
||||
'Available databases and tables': 'Базы данных и таблицы',
|
||||
'Available Databases and Tables': 'Базы данных и таблицы',
|
||||
'Buy this book': 'Buy this book',
|
||||
'cache': 'cache',
|
||||
'Cannot be empty': 'Пустое значение недопустимо',
|
||||
@@ -45,9 +45,9 @@
|
||||
'Current session': 'Текущая сессия',
|
||||
'customize me!': 'настройте внешний вид!',
|
||||
'data uploaded': 'данные загружены',
|
||||
'database': 'база данных',
|
||||
'Database': 'база данных',
|
||||
'Database': 'Database',
|
||||
'database %s select': 'выбор базы данных %s',
|
||||
'Database %s select': 'выбор базы данных %s',
|
||||
'db': 'БД',
|
||||
'DB Model': 'DB Model',
|
||||
'Delete:': 'Удалить:',
|
||||
@@ -129,7 +129,7 @@
|
||||
'Quick Examples': 'Quick Examples',
|
||||
'Recipes': 'Recipes',
|
||||
'record does not exist': 'запись не найдена',
|
||||
'record id': 'id записи',
|
||||
'Record id': 'id записи',
|
||||
'Record ID': 'ID записи',
|
||||
'Register': 'Зарегистрироваться',
|
||||
'Registration identifier': 'Registration identifier',
|
||||
@@ -137,7 +137,7 @@
|
||||
'Remember me (for 30 days)': 'Запомнить меня (на 30 дней)',
|
||||
'Reset Password key': 'Сбросить ключ пароля',
|
||||
'Role': 'Роль',
|
||||
'Rows in table': 'Строк в таблице',
|
||||
'Rows in Table': 'Строк в таблице',
|
||||
'Rows selected': 'Выделено строк',
|
||||
'Semantic': 'Semantic',
|
||||
'Services': 'Services',
|
||||
@@ -146,7 +146,7 @@
|
||||
'Submit': 'Отправить',
|
||||
'Support': 'Support',
|
||||
'Sure you want to delete this object?': 'Подтвердите удаление объекта',
|
||||
'table': 'таблица',
|
||||
'Table': 'таблица',
|
||||
'Table name': 'Имя таблицы',
|
||||
'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': '"Запрос" - это условие вида "db.table1.field1==\'значение\'". Выражение вида "db.table1.field1==db.table2.field2" формирует SQL JOIN.',
|
||||
'The Core': 'The Core',
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
'%s %%{row} updated': '%s upravených záznamov',
|
||||
'%s selected': '%s označených',
|
||||
'Administrative interface': 'pre administrátorské rozhranie kliknite sem',
|
||||
'Available databases and tables': 'Dostupné databázy a tabuľky',
|
||||
'Available Databases and Tables': 'Dostupné databázy a tabuľky',
|
||||
'Cannot be empty': 'Nemôže byť prázdne',
|
||||
'Check to delete': 'Označiť na zmazanie',
|
||||
'Controller': 'Controller',
|
||||
@@ -56,7 +56,7 @@
|
||||
'Remember me (for 30 days)': 'Zapamätaj si ma (na 30 dní)',
|
||||
'Reset Password key': 'Nastaviť registračný kľúč',
|
||||
'Role': 'Rola',
|
||||
'Rows in table': 'riadkov v tabuľke',
|
||||
'Rows in Table': 'riadkov v tabuľke',
|
||||
'Rows selected': 'označených riadkov',
|
||||
'Stylesheet': 'Stylesheet',
|
||||
'Submit': 'Odoslať',
|
||||
@@ -85,8 +85,8 @@
|
||||
'cache': 'cache',
|
||||
'customize me!': 'prispôsob ma!',
|
||||
'data uploaded': 'údaje naplnené',
|
||||
'database': 'databáza',
|
||||
'database %s select': 'databáza %s výber',
|
||||
'Database': 'databáza',
|
||||
'Database %s select': 'databáza %s výber',
|
||||
'db': 'db',
|
||||
'design': 'návrh',
|
||||
'done!': 'hotovo!',
|
||||
@@ -102,11 +102,11 @@
|
||||
'or import from csv file': 'alebo naimportovať z csv súboru',
|
||||
'password': 'heslo',
|
||||
'previous 100 rows': 'predchádzajúcich 100 riadkov',
|
||||
'record': 'záznam',
|
||||
'Record': 'záznam',
|
||||
'record does not exist': 'záznam neexistuje',
|
||||
'record id': 'id záznamu',
|
||||
'Record id': 'id záznamu',
|
||||
'register': 'registrovať',
|
||||
'state': 'stav',
|
||||
'table': 'tabuľka',
|
||||
'Table': 'tabuľka',
|
||||
'unable to parse csv file': 'nedá sa načítať csv súbor',
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
'Ajax Recipes': 'Рецепти для Ajax',
|
||||
'appadmin is disabled because insecure channel': 'використовується незахищенний канал (HTTP). Appadmin вимкнено',
|
||||
'Are you sure you want to delete this object?': "Ви впевнені, що хочете вилучити цей об'єкт?",
|
||||
'Available databases and tables': 'Доступні бази даних та таблиці',
|
||||
'Available Databases and Tables': 'Доступні бази даних та таблиці',
|
||||
'Buy this book': 'Купити книжку',
|
||||
'cache': 'кеш',
|
||||
'Cache': 'Кеш',
|
||||
@@ -58,9 +58,9 @@
|
||||
'Current session': 'Поточна сесія (current session)',
|
||||
'customize me!': 'причепуріть мене!',
|
||||
'data uploaded': 'дані завантажено',
|
||||
'database': 'база даних',
|
||||
'Database': 'база даних',
|
||||
'Database': 'База даних',
|
||||
'database %s select': 'Вибірка з бази даних %s',
|
||||
'Database %s select': 'Вибірка з бази даних %s',
|
||||
'db': 'база даних',
|
||||
'DB Model': 'Модель БД',
|
||||
'Delete:': 'Вилучити:',
|
||||
@@ -157,10 +157,10 @@
|
||||
'RAM Cache Keys': 'Ключі ОЗП-кешу',
|
||||
'Ram Cleared': 'ОЗП-кеш очищено',
|
||||
'Recipes': 'Рецепти',
|
||||
'record': 'запис',
|
||||
'Record': 'запис',
|
||||
'Record %(id)s updated': 'Запис %(id)s змінено',
|
||||
'record does not exist': 'запису не існує',
|
||||
'record id': 'ід. запису',
|
||||
'Record id': 'ід. запису',
|
||||
'Record ID': 'Ід.запису',
|
||||
'Record Updated': 'Запис змінено',
|
||||
'Register': 'Реєстрація',
|
||||
@@ -171,7 +171,7 @@
|
||||
'Request reset password': 'Запит на зміну пароля',
|
||||
'Reset Password key': 'Ключ скидання пароля',
|
||||
'Role': 'Роль',
|
||||
'Rows in table': 'Рядки в таблиці',
|
||||
'Rows in Table': 'Рядки в таблиці',
|
||||
'Rows selected': 'Відмічено рядків',
|
||||
'Save profile': 'Зберегти параметри',
|
||||
'Semantic': 'Семантика',
|
||||
@@ -183,7 +183,7 @@
|
||||
'submit': 'submit',
|
||||
'Submit': 'Застосувати',
|
||||
'Support': 'Підтримка',
|
||||
'table': 'Таблиця',
|
||||
'Table': 'Таблиця',
|
||||
'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': '"Запит" це умова, на зразок "db.table1.field1==\'значення\'". Вираз "db.table1.field1==db.table2.field2" повертає результат об\'єднання (SQL JOIN) таблиць.',
|
||||
'The Core': 'Ядро',
|
||||
'The output of the file is a dictionary that was rendered by the view %s': 'Результат функції - словник пар (назва=значення) було відображено з допомогою відображення (view) %s',
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
'Are you sure you want to uninstall application "%s"': '確定要移除應用程式 "%s"',
|
||||
'Are you sure you want to uninstall application "%s"?': '確定要移除應用程式 "%s"',
|
||||
'Authentication': '驗證',
|
||||
'Available databases and tables': '可提供的資料庫和資料表',
|
||||
'Available Databases and Tables': '可提供的資料庫和資料表',
|
||||
'Cannot be empty': '不可空白',
|
||||
'Cannot compile: there are errors in your app. Debug it, correct errors and try again.': '無法編譯:應用程式中含有錯誤,請除錯後再試一次.',
|
||||
'Change Password': '變更密碼',
|
||||
@@ -101,7 +101,7 @@
|
||||
'Reset Password key': '重設密碼',
|
||||
'Resolve Conflict file': '解決衝突檔案',
|
||||
'Role': '角色',
|
||||
'Rows in table': '在資料表裏的資料',
|
||||
'Rows in Table': '在資料表裏的資料',
|
||||
'Rows selected': '筆資料被選擇',
|
||||
'Saved file hash:': '檔案雜湊值已紀錄:',
|
||||
'Static files': '靜態檔案',
|
||||
@@ -144,8 +144,8 @@
|
||||
'change password': '變更密碼',
|
||||
'customize me!': '請調整我!',
|
||||
'data uploaded': '資料已上傳',
|
||||
'database': '資料庫',
|
||||
'database %s select': '已選擇 %s 資料庫',
|
||||
'Database': '資料庫',
|
||||
'Database %s select': '已選擇 %s 資料庫',
|
||||
'db': 'db',
|
||||
'design': '設計',
|
||||
'done!': '完成!',
|
||||
@@ -160,11 +160,11 @@
|
||||
'next 100 rows': '往後 100 筆',
|
||||
'or import from csv file': '或是從逗號分隔檔(CSV)匯入',
|
||||
'previous 100 rows': '往前 100 筆',
|
||||
'record': '紀錄',
|
||||
'Record': '紀錄',
|
||||
'record does not exist': '紀錄不存在',
|
||||
'record id': '紀錄編號',
|
||||
'Record id': '紀錄編號',
|
||||
'register': '註冊',
|
||||
'state': '狀態',
|
||||
'table': '資料表',
|
||||
'Table': '資料表',
|
||||
'unable to parse csv file': '無法解析逗號分隔檔(csv)',
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 244 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
@@ -10,8 +10,9 @@
|
||||
//--></script>
|
||||
|
||||
{{if request.function=='index':}}
|
||||
<h2>{{=T("Available databases and tables")}}</h2>
|
||||
<h2>{{=T("Available Databases and Tables")}}</h2>
|
||||
{{if not databases:}}{{=T("No databases in this application")}}{{pass}}
|
||||
<table>
|
||||
{{for db in sorted(databases):}}
|
||||
{{for table in databases[db].tables:}}
|
||||
{{qry='%s.%s.id>0'%(db,table)}}
|
||||
@@ -28,20 +29,24 @@
|
||||
{{qry=''}}
|
||||
{{pass}}
|
||||
{{pass}}
|
||||
<h3>
|
||||
{{=A("%s.%s" % (db,table),_href=URL('select',args=[db],vars=dict(query=qry)))}}
|
||||
</h3>
|
||||
[ {{=A(str(T('insert new'))+' '+table,_href=URL('insert',args=[db,table]))}} ]
|
||||
<br /><br />
|
||||
<tr>
|
||||
<th style="font-size: 1.75em;">
|
||||
{{=A("%s.%s" % (db,table),_href=URL('select',args=[db],vars=dict(query=qry)))}}
|
||||
</th>
|
||||
<td>
|
||||
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}}
|
||||
</td>
|
||||
</tr>
|
||||
{{pass}}
|
||||
</table>
|
||||
{{pass}}
|
||||
|
||||
{{elif request.function=='select':}}
|
||||
<h2>{{=XML(str(T("database %s select"))%A(request.args[0],_href=URL('index'))) }}
|
||||
<h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }}
|
||||
</h2>
|
||||
{{if table:}}
|
||||
[ {{=A(str(T('insert new %s'))%table,_href=URL('insert',args=[request.args[0],table]))}} ]<br/><br/>
|
||||
<h3>{{=T("Rows in table")}}</h3><br/>
|
||||
{{if table:}}
|
||||
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/>
|
||||
<h3>{{=T("Rows in Table")}}</h3><br/>
|
||||
{{else:}}
|
||||
<h3>{{=T("Rows selected")}}</h3><br/>
|
||||
{{pass}}
|
||||
@@ -51,8 +56,8 @@
|
||||
{{=T('"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN')}}</p>
|
||||
<br/><br/>
|
||||
<h4>{{=T("%s selected", nrows)}}</h4>
|
||||
{{if start>0:}}[ {{=A(T('previous 100 rows'),_href=URL('select',args=request.args[0],vars=dict(start=start-100)))}} ]{{pass}}
|
||||
{{if stop<nrows:}}[ {{=A(T('next 100 rows'),_href=URL('select',args=request.args[0],vars=dict(start=start+100)))}} ]{{pass}}
|
||||
{{if start>0:}}{{=A(T('previous 100 rows'),_href=URL('select',args=request.args[0],vars=dict(start=start-100)),_class="btn")}}{{pass}}
|
||||
{{if stop<nrows:}}{{=A(T('next 100 rows'),_href=URL('select',args=request.args[0],vars=dict(start=start+100)),_class="btn")}}{{pass}}
|
||||
{{if rows:}}
|
||||
<div style="overflow: auto;" width="80%">
|
||||
{{linkto=URL('update',args=request.args[0])}}
|
||||
@@ -61,35 +66,35 @@
|
||||
</div>
|
||||
{{pass}}
|
||||
<br/><br/><h3>{{=T("Import/Export")}}</h3><br/>
|
||||
[ <a href="{{=URL('csv',args=request.args[0],vars=dict(query=query))}}">{{=T("export as csv file")}}</a> ]
|
||||
<a href="{{=URL('csv',args=request.args[0],vars=dict(query=query))}}" class="btn">{{=T("export as csv file")}}</a>
|
||||
{{=formcsv or ''}}
|
||||
|
||||
{{elif request.function=='insert':}}
|
||||
<h2>{{=T("database")}} {{=A(request.args[0],_href=URL('index'))}}
|
||||
<h2>{{=T("Database")}} {{=A(request.args[0],_href=URL('index'))}}
|
||||
{{if hasattr(table,'_primarykey'):}}
|
||||
{{fieldname=table._primarykey[0]}}
|
||||
{{dbname=request.args[0]}}
|
||||
{{tablename=request.args[1]}}
|
||||
{{cond = table[fieldname].type in ['string','text'] and '!=""' or '>0'}}
|
||||
{{=T("table")}} {{=A(tablename,_href=URL('select',args=dbname,vars=dict(query='%s.%s.%s%s'%(dbname,tablename,fieldname,cond))))}}
|
||||
{{=T("Table")}} {{=A(tablename,_href=URL('select',args=dbname,vars=dict(query='%s.%s.%s%s'%(dbname,tablename,fieldname,cond))))}}
|
||||
{{else:}}
|
||||
{{=T("table")}} {{=A(request.args[1],_href=URL('select',args=request.args[0],vars=dict(query='%s.%s.id>0'%tuple(request.args[:2]))))}}
|
||||
{{=T("Table")}} {{=A(request.args[1],_href=URL('select',args=request.args[0],vars=dict(query='%s.%s.id>0'%tuple(request.args[:2]))))}}
|
||||
{{pass}}
|
||||
</h2>
|
||||
<h3>{{=T("New Record")}}</h3><br/>
|
||||
{{=form}}
|
||||
{{elif request.function=='update':}}
|
||||
<h2>{{=T("database")}} {{=A(request.args[0],_href=URL('index'))}}
|
||||
<h2>{{=T("Database")}} {{=A(request.args[0],_href=URL('index'))}}
|
||||
{{if hasattr(table,'_primarykey'):}}
|
||||
{{fieldname=request.vars.keys()[0]}}
|
||||
{{dbname=request.args[0]}}
|
||||
{{tablename=request.args[1]}}
|
||||
{{cond = table[fieldname].type in ['string','text'] and '!=""' or '>0'}}
|
||||
{{=T("table")}} {{=A(tablename,_href=URL('select',args=dbname,vars=dict(query='%s.%s.%s%s'%(dbname,tablename,fieldname,cond))))}}
|
||||
{{=T("record")}} {{=A('%s=%s'%request.vars.items()[0],_href=URL('update',args=request.args[:2],vars=request.vars))}}
|
||||
{{=T("Table")}} {{=A(tablename,_href=URL('select',args=dbname,vars=dict(query='%s.%s.%s%s'%(dbname,tablename,fieldname,cond))))}}
|
||||
{{=T("Record")}} {{=A('%s=%s'%request.vars.items()[0],_href=URL('update',args=request.args[:2],vars=request.vars))}}
|
||||
{{else:}}
|
||||
{{=T("table")}} {{=A(request.args[1],_href=URL('select',args=request.args[0],vars=dict(query='%s.%s.id>0'%tuple(request.args[:2]))))}}
|
||||
{{=T("record id")}} {{=A(request.args[2],_href=URL('update',args=request.args[:3]))}}
|
||||
{{=T("Table")}} {{=A(request.args[1],_href=URL('select',args=request.args[0],vars=dict(query='%s.%s.id>0'%tuple(request.args[:2]))))}}
|
||||
{{=T("Record id")}} {{=A(request.args[2],_href=URL('update',args=request.args[:3]))}}
|
||||
{{pass}}
|
||||
</h2>
|
||||
<h3>{{=T("Edit current record")}}</h3><br/><br/>{{=form}}
|
||||
|
||||
@@ -298,7 +298,7 @@ class CacheOnDisk(CacheAbstract):
|
||||
try:
|
||||
storage = self._open_shelf_with_lock()
|
||||
try:
|
||||
if not storage.has_key(CacheAbstract.cache_stats_name):
|
||||
if not CacheAbstract.cache_stats_name in storage:
|
||||
storage[CacheAbstract.cache_stats_name] = {
|
||||
'hit_total': 0,
|
||||
'misses': 0,
|
||||
|
||||
@@ -50,6 +50,8 @@ is_pypy = settings.global_settings.is_pypy
|
||||
is_gae = settings.global_settings.web2py_runtime_gae
|
||||
is_jython = settings.global_settings.is_jython
|
||||
|
||||
pjoin = os.path.join
|
||||
|
||||
TEST_CODE = \
|
||||
r"""
|
||||
def _TEST():
|
||||
@@ -319,7 +321,7 @@ def local_import_aux(name, reload_force=False, app='welcome'):
|
||||
"""
|
||||
OLD IMPLEMENTATION:
|
||||
items = name.replace('/','.').split('.')
|
||||
filename, modulepath = items[-1], os.path.join(apath,'modules',*items[:-1])
|
||||
filename, modulepath = items[-1], pjoin(apath,'modules',*items[:-1])
|
||||
imp.acquire_lock()
|
||||
try:
|
||||
file=None
|
||||
@@ -348,11 +350,9 @@ def build_environment(request, response, session, store_current=True):
|
||||
"""
|
||||
Build the environment dictionary into which web2py files are executed.
|
||||
"""
|
||||
|
||||
_validators = validators
|
||||
_html = html
|
||||
environment = dict(map(lambda key: (key, getattr(_html, key)), _html.__all__))
|
||||
environment.update(map(lambda key: (key, getattr(_validators, key)), _validators.__all__))
|
||||
h,v = html,validators
|
||||
environment = dict((k,getattr(h,k)) for k in h.__all__)
|
||||
environment.update((k,getattr(v, k)) for k in v.__all__)
|
||||
if not request.env:
|
||||
request.env = Storage()
|
||||
|
||||
@@ -389,7 +389,7 @@ def build_environment(request, response, session, store_current=True):
|
||||
environment['local_import'] = \
|
||||
lambda name, reload=False, app=request.application:\
|
||||
local_import_aux(name,reload,app)
|
||||
BaseAdapter.set_folder(os.path.join(request.folder, 'databases'))
|
||||
BaseAdapter.set_folder(pjoin(request.folder, 'databases'))
|
||||
response._view_environment = copy.copy(environment)
|
||||
return environment
|
||||
|
||||
@@ -419,11 +419,11 @@ def compile_views(folder):
|
||||
Compiles all the views in the application specified by `folder`
|
||||
"""
|
||||
|
||||
path = os.path.join(folder, 'views')
|
||||
path = pjoin(folder, 'views')
|
||||
for file in listdir(path, '^[\w/\-]+(\.\w+)+$'):
|
||||
data = parse_template(file, path)
|
||||
filename = ('views/%s.py' % file).replace('/', '_').replace('\\', '_')
|
||||
filename = os.path.join(folder, 'compiled', filename)
|
||||
filename = pjoin(folder, 'compiled', filename)
|
||||
write_file(filename, data)
|
||||
save_pyc(filename)
|
||||
os.unlink(filename)
|
||||
@@ -434,10 +434,10 @@ def compile_models(folder):
|
||||
Compiles all the models in the application specified by `folder`
|
||||
"""
|
||||
|
||||
path = os.path.join(folder, 'models')
|
||||
path = pjoin(folder, 'models')
|
||||
for file in listdir(path, '.+\.py$'):
|
||||
data = read_file(os.path.join(path, file))
|
||||
filename = os.path.join(folder, 'compiled','models',file)
|
||||
data = read_file(pjoin(path, file))
|
||||
filename = pjoin(folder, 'compiled','models',file)
|
||||
mktree(filename)
|
||||
write_file(filename, data)
|
||||
save_pyc(filename)
|
||||
@@ -449,15 +449,15 @@ def compile_controllers(folder):
|
||||
Compiles all the controllers in the application specified by `folder`
|
||||
"""
|
||||
|
||||
path = os.path.join(folder, 'controllers')
|
||||
path = pjoin(folder, 'controllers')
|
||||
for file in listdir(path, '.+\.py$'):
|
||||
### why is this here? save_pyc(os.path.join(path, file))
|
||||
data = read_file(os.path.join(path,file))
|
||||
### why is this here? save_pyc(pjoin(path, file))
|
||||
data = read_file(pjoin(path,file))
|
||||
exposed = regex_expose.findall(data)
|
||||
for function in exposed:
|
||||
command = data + "\nresponse._vars=response._caller(%s)\n" % \
|
||||
function
|
||||
filename = os.path.join(folder, 'compiled', ('controllers/'
|
||||
filename = pjoin(folder, 'compiled', ('controllers/'
|
||||
+ file[:-3]).replace('/', '_')
|
||||
+ '_' + function + '.py')
|
||||
write_file(filename, command)
|
||||
@@ -474,18 +474,18 @@ def run_models_in(environment):
|
||||
folder = environment['request'].folder
|
||||
c = environment['request'].controller
|
||||
f = environment['request'].function
|
||||
cpath = os.path.join(folder, 'compiled')
|
||||
cpath = pjoin(folder, 'compiled')
|
||||
if os.path.exists(cpath):
|
||||
for model in listdir(cpath, '^models_\w+\.pyc$', 0):
|
||||
restricted(read_pyc(model), environment, layer=model)
|
||||
path = os.path.join(cpath, 'models')
|
||||
path = pjoin(cpath, 'models')
|
||||
models = listdir(path, '^\w+\.pyc$',0,sort=False)
|
||||
compiled=True
|
||||
else:
|
||||
path = os.path.join(folder, 'models')
|
||||
path = pjoin(folder, 'models')
|
||||
models = listdir(path, '^\w+\.py$',0,sort=False)
|
||||
compiled=False
|
||||
paths = (path, os.path.join(path,c), os.path.join(path,c,f))
|
||||
paths = (path, pjoin(path,c), pjoin(path,c,f))
|
||||
for model in models:
|
||||
if not os.path.split(model)[0] in paths and c!='appadmin':
|
||||
continue
|
||||
@@ -509,11 +509,11 @@ def run_controller_in(controller, function, environment):
|
||||
# if compiled should run compiled!
|
||||
|
||||
folder = environment['request'].folder
|
||||
path = os.path.join(folder, 'compiled')
|
||||
path = pjoin(folder, 'compiled')
|
||||
badc = 'invalid controller (%s/%s)' % (controller, function)
|
||||
badf = 'invalid function (%s/%s)' % (controller, function)
|
||||
if os.path.exists(path):
|
||||
filename = os.path.join(path, 'controllers_%s_%s.pyc'
|
||||
filename = pjoin(path, 'controllers_%s_%s.pyc'
|
||||
% (controller, function))
|
||||
if not os.path.exists(filename):
|
||||
raise HTTP(404,
|
||||
@@ -528,7 +528,7 @@ def run_controller_in(controller, function, environment):
|
||||
[add_path_first(path) for path in paths]
|
||||
# TESTING END
|
||||
|
||||
filename = os.path.join(folder, 'controllers/%s.py'
|
||||
filename = pjoin(folder, 'controllers/%s.py'
|
||||
% controller)
|
||||
if not os.path.exists(filename):
|
||||
raise HTTP(404,
|
||||
@@ -539,7 +539,7 @@ def run_controller_in(controller, function, environment):
|
||||
code += TEST_CODE
|
||||
restricted(code, environment, layer=filename)
|
||||
else:
|
||||
filename = os.path.join(folder, 'controllers/%s.py'
|
||||
filename = pjoin(folder, 'controllers/%s.py'
|
||||
% controller)
|
||||
if not os.path.exists(filename):
|
||||
raise HTTP(404,
|
||||
@@ -576,19 +576,20 @@ def run_view_in(environment):
|
||||
|
||||
request = environment['request']
|
||||
response = environment['response']
|
||||
view = response.view
|
||||
folder = request.folder
|
||||
path = os.path.join(folder, 'compiled')
|
||||
badv = 'invalid view (%s)' % response.view
|
||||
path = pjoin(folder, 'compiled')
|
||||
badv = 'invalid view (%s)' % view
|
||||
patterns = response.generic_patterns or []
|
||||
regex = re.compile('|'.join(map(fnmatch.translate, patterns)))
|
||||
short_action = '%(controller)s/%(function)s.%(extension)s' % request
|
||||
allow_generic = patterns and regex.search(short_action)
|
||||
if not isinstance(response.view, str):
|
||||
ccode = parse_template(response.view, os.path.join(folder, 'views'),
|
||||
if not isinstance(view, str):
|
||||
ccode = parse_template(view, pjoin(folder, 'views'),
|
||||
context=environment)
|
||||
restricted(ccode, environment, 'file stream')
|
||||
elif os.path.exists(path):
|
||||
x = response.view.replace('/', '_')
|
||||
x = view.replace('/', '_')
|
||||
files = ['views_%s.pyc' % x]
|
||||
if allow_generic:
|
||||
files.append('views_generic.%s.pyc' % request.extension)
|
||||
@@ -599,7 +600,7 @@ def run_view_in(environment):
|
||||
files.append('views_generic.pyc')
|
||||
# end backward compatibility code
|
||||
for f in files:
|
||||
filename = os.path.join(path,f)
|
||||
filename = pjoin(path,f)
|
||||
if os.path.exists(filename):
|
||||
code = read_pyc(filename)
|
||||
restricted(code, environment, layer=filename)
|
||||
@@ -608,10 +609,10 @@ def run_view_in(environment):
|
||||
rewrite.thread.routes.error_message % badv,
|
||||
web2py_error=badv)
|
||||
else:
|
||||
filename = os.path.join(folder, 'views', response.view)
|
||||
filename = pjoin(folder, 'views', view)
|
||||
if not os.path.exists(filename) and allow_generic:
|
||||
response.view = 'generic.' + request.extension
|
||||
filename = os.path.join(folder, 'views', response.view)
|
||||
view = 'generic.' + request.extension
|
||||
filename = pjoin(folder, 'views', view)
|
||||
if not os.path.exists(filename):
|
||||
raise HTTP(404,
|
||||
rewrite.thread.routes.error_message % badv,
|
||||
@@ -619,12 +620,12 @@ def run_view_in(environment):
|
||||
layer = filename
|
||||
if is_gae:
|
||||
ccode = getcfs(layer, filename,
|
||||
lambda: compile2(parse_template(response.view,
|
||||
os.path.join(folder, 'views'),
|
||||
lambda: compile2(parse_template(view,
|
||||
pjoin(folder, 'views'),
|
||||
context=environment),layer))
|
||||
else:
|
||||
ccode = parse_template(response.view,
|
||||
os.path.join(folder, 'views'),
|
||||
ccode = parse_template(view,
|
||||
pjoin(folder, 'views'),
|
||||
context=environment)
|
||||
restricted(ccode, environment, layer)
|
||||
|
||||
@@ -633,8 +634,8 @@ def remove_compiled_application(folder):
|
||||
Deletes the folder `compiled` containing the compiled application.
|
||||
"""
|
||||
try:
|
||||
shutil.rmtree(os.path.join(folder, 'compiled'))
|
||||
path = os.path.join(folder, 'controllers')
|
||||
shutil.rmtree(pjoin(folder, 'compiled'))
|
||||
path = pjoin(folder, 'controllers')
|
||||
for file in listdir(path,'.*\.pyc$',drop=False):
|
||||
os.unlink(file)
|
||||
except OSError:
|
||||
@@ -646,7 +647,7 @@ def compile_application(folder):
|
||||
Compiles all models, views, controller for the application in `folder`.
|
||||
"""
|
||||
remove_compiled_application(folder)
|
||||
os.mkdir(os.path.join(folder, 'compiled'))
|
||||
os.mkdir(pjoin(folder, 'compiled'))
|
||||
compile_models(folder)
|
||||
compile_controllers(folder)
|
||||
compile_views(folder)
|
||||
|
||||
@@ -117,7 +117,8 @@ class Request(Storage):
|
||||
user_agent_parser.detect(self.env.http_user_agent)
|
||||
user_agent = Storage(user_agent)
|
||||
for key,value in user_agent.items():
|
||||
if isinstance(value,dict): user_agent[key] = Storage(value)
|
||||
if isinstance(value,dict):
|
||||
user_agent[key] = Storage(value)
|
||||
return user_agent
|
||||
|
||||
def requires_https(self):
|
||||
@@ -222,9 +223,9 @@ class Response(Storage):
|
||||
return page
|
||||
|
||||
def include_meta(self):
|
||||
s = '\n'
|
||||
for key,value in (self.meta or {}).items():
|
||||
s += '<meta name="%s" content="%s" />\n' % (key,xmlescape(value))
|
||||
s = '\n'.join(
|
||||
'<meta name="%s" content="%s" />\n' % (k,xmlescape(v))
|
||||
for k,v in (self.meta or {}).iteritems())
|
||||
self.write(s,escape=False)
|
||||
|
||||
def include_files(self):
|
||||
@@ -297,14 +298,15 @@ class Response(Storage):
|
||||
default to the last request argument otherwise)
|
||||
"""
|
||||
|
||||
headers = self.headers
|
||||
# for attachment settings and backward compatibility
|
||||
keys = [item.lower() for item in self.headers]
|
||||
keys = [item.lower() for item in headers]
|
||||
if attachment:
|
||||
if filename is None:
|
||||
attname = ""
|
||||
else:
|
||||
attname = filename
|
||||
self.headers["Content-Disposition"] = \
|
||||
headers["Content-Disposition"] = \
|
||||
"attachment;filename=%s" % attname
|
||||
|
||||
if not request:
|
||||
@@ -313,30 +315,31 @@ class Response(Storage):
|
||||
stream_file_or_304_or_206(stream,
|
||||
chunk_size=chunk_size,
|
||||
request=request,
|
||||
headers=self.headers)
|
||||
headers=headers)
|
||||
|
||||
# ## the following is for backward compatibility
|
||||
if hasattr(stream, 'name'):
|
||||
filename = stream.name
|
||||
|
||||
if filename and not 'content-type' in keys:
|
||||
self.headers['Content-Type'] = contenttype(filename)
|
||||
headers['Content-Type'] = contenttype(filename)
|
||||
if filename and not 'content-length' in keys:
|
||||
try:
|
||||
self.headers['Content-Length'] = \
|
||||
headers['Content-Length'] = \
|
||||
os.path.getsize(filename)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
env = request.env
|
||||
# Internet Explorer < 9.0 will not allow downloads over SSL unless caching is enabled
|
||||
if request.is_https and isinstance(request.env.http_user_agent,str) and \
|
||||
not re.search(r'Opera', request.env.http_user_agent) and \
|
||||
re.search(r'MSIE [5-8][^0-9]', request.env.http_user_agent):
|
||||
self.headers['Pragma'] = 'cache'
|
||||
self.headers['Cache-Control'] = 'private'
|
||||
if request.is_https and isinstance(env.http_user_agent,str) and \
|
||||
not re.search(r'Opera', env.http_user_agent) and \
|
||||
re.search(r'MSIE [5-8][^0-9]', env.http_user_agent):
|
||||
headers['Pragma'] = 'cache'
|
||||
headers['Cache-Control'] = 'private'
|
||||
|
||||
if request and request.env.web2py_use_wsgi_file_wrapper:
|
||||
wrapped = request.env.wsgi_file_wrapper(stream, chunk_size)
|
||||
if request and env.web2py_use_wsgi_file_wrapper:
|
||||
wrapped = env.wsgi_file_wrapper(stream, chunk_size)
|
||||
else:
|
||||
wrapped = streamer(stream, chunk_size=chunk_size)
|
||||
return wrapped
|
||||
@@ -364,11 +367,13 @@ class Response(Storage):
|
||||
(filename, stream) = field.retrieve(name)
|
||||
except IOError:
|
||||
raise HTTP(404)
|
||||
self.headers['Content-Type'] = contenttype(name)
|
||||
headers = self.headers
|
||||
headers['Content-Type'] = contenttype(name)
|
||||
if attachment:
|
||||
self.headers['Content-Disposition'] = \
|
||||
"attachment; filename=%s" % filename
|
||||
return self.stream(stream, chunk_size = chunk_size, request=request)
|
||||
headers['Content-Disposition'] = \
|
||||
'attachment; filename=%s' % filename
|
||||
return self.stream(stream, chunk_size=chunk_size, request=request)
|
||||
|
||||
|
||||
def json(self, data, default=None):
|
||||
return json(data, default = default or custom_json)
|
||||
@@ -405,8 +410,15 @@ class Response(Storage):
|
||||
dbstats = [TABLE(*[TR(PRE(row[0]),'%.2fms' % (row[1]*1000)) \
|
||||
for row in i.db._timings]) \
|
||||
for i in thread.instances]
|
||||
dbtables = dict([(i.uri, {'defined': sorted(list(set(i.db.tables) -
|
||||
set(i.db._LAZY_TABLES.keys()))) or
|
||||
'[no defined tables]',
|
||||
'lazy': sorted(i.db._LAZY_TABLES.keys()) or
|
||||
'[no lazy tables]'})
|
||||
for i in thread.instances])
|
||||
else:
|
||||
dbstats = [] # if no db or on GAE
|
||||
dbtables = {}
|
||||
u = web2py_uuid()
|
||||
return DIV(
|
||||
BUTTON('design',_onclick="document.location='%s'" % admin),
|
||||
@@ -416,6 +428,8 @@ class Response(Storage):
|
||||
DIV(BEAUTIFY(current.session),_class="hidden",_id="session-%s"%u),
|
||||
BUTTON('response',_onclick="jQuery('#response-%s').slideToggle()"%u),
|
||||
DIV(BEAUTIFY(current.response),_class="hidden",_id="response-%s"%u),
|
||||
BUTTON('db tables',_onclick="jQuery('#db-tables-%s').slideToggle()"%u),
|
||||
DIV(BEAUTIFY(dbtables),_class="hidden",_id="db-tables-%s"%u),
|
||||
BUTTON('db stats',_onclick="jQuery('#db-stats-%s').slideToggle()"%u),
|
||||
DIV(BEAUTIFY(dbstats),_class="hidden",_id="db-stats-%s"%u),
|
||||
SCRIPT("jQuery('.hidden').hide()")
|
||||
@@ -452,14 +466,15 @@ class Session(Storage):
|
||||
response.session_id_name = 'session_id_%s' % masterapp.lower()
|
||||
|
||||
# Load session data from cookie
|
||||
|
||||
cookies = request.cookies
|
||||
|
||||
if cookie_key:
|
||||
response.session_cookie_key = cookie_key
|
||||
response.session_cookie_key2 = hashlib.md5(cookie_key).digest()
|
||||
cookie_name = request.application.lower()+'_session_data'
|
||||
cookie_name = masterapp.lower()+'_session_data'
|
||||
response.session_cookie_name = cookie_name
|
||||
if cookie_data in request.cookies:
|
||||
cookie_value = request.cookies[cookie_name].value
|
||||
if cookie_name in cookies:
|
||||
cookie_value = cookies[cookie_name].value
|
||||
cookie_parts = cookie_value.split(":")
|
||||
enc = cookie_parts[2]
|
||||
cipher = AES.new(cookie_key)
|
||||
@@ -477,9 +492,9 @@ class Session(Storage):
|
||||
return
|
||||
response.session_new = False
|
||||
client = request.client and request.client.replace(':', '.')
|
||||
if response.session_id_name in request.cookies:
|
||||
if response.session_id_name in cookies:
|
||||
response.session_id = \
|
||||
request.cookies[response.session_id_name].value
|
||||
cookies[response.session_id_name].value
|
||||
if regex_session_id.match(response.session_id):
|
||||
response.session_filename = \
|
||||
os.path.join(up(request.folder), masterapp,
|
||||
@@ -532,38 +547,35 @@ class Session(Storage):
|
||||
table_migrate = False
|
||||
tname = tablename + '_' + masterapp
|
||||
table = db.get(tname, None)
|
||||
Field = db.Field
|
||||
if table is None:
|
||||
table = db.define_table(
|
||||
db.define_table(
|
||||
tname,
|
||||
db.Field('locked', 'boolean', default=False),
|
||||
db.Field('client_ip', length=64),
|
||||
db.Field('created_datetime', 'datetime',
|
||||
Field('locked', 'boolean', default=False),
|
||||
Field('client_ip', length=64),
|
||||
Field('created_datetime', 'datetime',
|
||||
default=request.now),
|
||||
db.Field('modified_datetime', 'datetime'),
|
||||
db.Field('unique_key', length=64),
|
||||
db.Field('session_data', 'blob'),
|
||||
Field('modified_datetime', 'datetime'),
|
||||
Field('unique_key', length=64),
|
||||
Field('session_data', 'blob'),
|
||||
migrate=table_migrate,
|
||||
)
|
||||
table = db[tname] # to allow for lazy table
|
||||
try:
|
||||
|
||||
# Get session data out of the database
|
||||
|
||||
# Key comes from the cookie
|
||||
key = request.cookies[response.session_id_name].value
|
||||
# Get session data out of the database
|
||||
# Key comes from the cookie
|
||||
key = cookies[response.session_id_name].value
|
||||
(record_id, unique_key) = key.split(':')
|
||||
if record_id == '0':
|
||||
raise Exception, 'record_id == 0'
|
||||
# Select from database.
|
||||
# Select from database
|
||||
rows = db(table.id == record_id).select()
|
||||
|
||||
# Make sure the session data exists in the database
|
||||
# Make sure the session data exists in the database
|
||||
if len(rows) == 0 or rows[0].unique_key != unique_key:
|
||||
raise Exception, 'No record'
|
||||
|
||||
# rows[0].update_record(locked=True)
|
||||
|
||||
|
||||
# Unpickle the data
|
||||
# rows[0].update_record(locked=True)
|
||||
# Unpickle the data
|
||||
session_data = cPickle.loads(rows[0].session_data)
|
||||
self.update(session_data)
|
||||
except Exception:
|
||||
@@ -573,8 +585,9 @@ class Session(Storage):
|
||||
response._dbtable_and_field = \
|
||||
(response.session_id_name, table, record_id, unique_key)
|
||||
response.session_id = '%s:%s' % (record_id, unique_key)
|
||||
response.cookies[response.session_id_name] = response.session_id
|
||||
response.cookies[response.session_id_name]['path'] = '/'
|
||||
rcookies = response.cookies
|
||||
rcookies[response.session_id_name] = response.session_id
|
||||
rcookies[response.session_id_name]['path'] = '/'
|
||||
self.__hash = hashlib.md5(str(self)).digest()
|
||||
if self.flash:
|
||||
(response.flash, self.flash) = (self.flash, None)
|
||||
|
||||
@@ -296,7 +296,8 @@ def URL(
|
||||
if other.endswith('/'):
|
||||
other += '/' # add trailing slash to make last trailing empty arg explicit
|
||||
|
||||
if vars.has_key('_signature'): vars.pop('_signature')
|
||||
if '_signature' in vars:
|
||||
vars.pop('_signature')
|
||||
list_vars = []
|
||||
for (key, vals) in sorted(vars.items()):
|
||||
if not isinstance(vals, (list, tuple)):
|
||||
@@ -387,7 +388,7 @@ def verifyURL(request, hmac_key=None, hash_vars=True, salt=None, user_signature=
|
||||
|
||||
"""
|
||||
|
||||
if not request.get_vars.has_key('_signature'):
|
||||
if not '_signature' in request.get_vars:
|
||||
return False # no signature in the request URL
|
||||
|
||||
# check if user_signature requires
|
||||
@@ -484,15 +485,17 @@ class XmlComponent(object):
|
||||
components += [other]
|
||||
return CAT(*components)
|
||||
|
||||
def add_class(self, name):
|
||||
def add_class(self, name):
|
||||
""" add a class to _class attribute """
|
||||
classes = set(self['_class'].split())|set(name.split())
|
||||
c = self['_class']
|
||||
classes = (set(c.split()) if c else set())|set(name.split())
|
||||
self['_class'] = ' '.join(classes) if classes else None
|
||||
return self
|
||||
|
||||
def remove_class(self, name):
|
||||
""" remove a class from _class attribute """
|
||||
classes = set(self['_class'].split())-set(name.split())
|
||||
c = self['_class']
|
||||
classes = (set(c.split()) if c else set())-set(name.split())
|
||||
self['_class'] = ' '.join(classes) if classes else None
|
||||
return self
|
||||
|
||||
@@ -656,17 +659,17 @@ class DIV(XmlComponent):
|
||||
self.attributes = attributes
|
||||
self._fixup()
|
||||
# converts special attributes in components attributes
|
||||
self._postprocessing()
|
||||
self.parent = None
|
||||
for c in self.components:
|
||||
self._setnode(c)
|
||||
self._postprocessing()
|
||||
|
||||
def update(self, **kargs):
|
||||
"""
|
||||
dictionary like updating of the tag attributes
|
||||
"""
|
||||
|
||||
for (key, value) in kargs.items():
|
||||
for (key, value) in kargs.iteritems():
|
||||
self[key] = value
|
||||
return self
|
||||
|
||||
@@ -811,7 +814,9 @@ class DIV(XmlComponent):
|
||||
c.latest = self.latest
|
||||
c.session = self.session
|
||||
c.formname = self.formname
|
||||
if hideerror: c['hideerror'] = hideerror
|
||||
if hideerror and not \
|
||||
self.attributes.get('hideerror',False):
|
||||
c['hideerror'] = hideerror
|
||||
newstatus = c._traverse(status,hideerror) and newstatus
|
||||
|
||||
# for input, textarea, select, option
|
||||
@@ -1038,7 +1043,7 @@ class DIV(XmlComponent):
|
||||
tag = getattr(self,'tag').replace('/', '')
|
||||
if args and tag not in args:
|
||||
check = False
|
||||
for (key, value) in kargs.items():
|
||||
for (key, value) in kargs.iteritems():
|
||||
if key not in ['first_only', 'replace', 'find_text']:
|
||||
if isinstance(value, (str, int)):
|
||||
if self[key] != str(value):
|
||||
@@ -1109,16 +1114,15 @@ class DIV(XmlComponent):
|
||||
sibs = [s for s in self.parent.components if not s == self]
|
||||
matches = []
|
||||
first_only = False
|
||||
if kargs.has_key("first_only"):
|
||||
first_only = kargs["first_only"]
|
||||
del kargs["first_only"]
|
||||
if 'first_only' in kargs:
|
||||
first_only = kargs.pop('first_only')
|
||||
for c in sibs:
|
||||
try:
|
||||
check = True
|
||||
tag = getattr(c,'tag').replace("/","")
|
||||
if args and tag not in args:
|
||||
check = False
|
||||
for (key, value) in kargs.items():
|
||||
for (key, value) in kargs.iteritems():
|
||||
if c[key] != value:
|
||||
check = False
|
||||
if check:
|
||||
@@ -1682,14 +1686,14 @@ class INPUT(DIV):
|
||||
if name is None or name == '':
|
||||
return True
|
||||
name = str(name)
|
||||
|
||||
request_vars_get = self.request_vars.get
|
||||
if self['_type'] != 'checkbox':
|
||||
self['old_value'] = self['value'] or self['_value'] or ''
|
||||
value = self.request_vars.get(name, '')
|
||||
value = request_vars_get(name, '')
|
||||
self['value'] = value
|
||||
else:
|
||||
self['old_value'] = self['value'] or False
|
||||
value = self.request_vars.get(name)
|
||||
value = request_vars_get(name)
|
||||
if isinstance(value, (tuple, list)):
|
||||
self['value'] = self['_value'] in value
|
||||
else:
|
||||
@@ -1932,14 +1936,15 @@ class FORM(DIV):
|
||||
# check formname and formkey
|
||||
|
||||
status = True
|
||||
if self.session:
|
||||
formkey = self.session.get('_formkey[%s]' % self.formname, None)
|
||||
request_vars = self.request_vars
|
||||
if session:
|
||||
formkey = session.get('_formkey[%s]' % formname, None)
|
||||
# check if user tampering with form and void CSRF
|
||||
if formkey != self.request_vars._formkey:
|
||||
if formkey != request_vars._formkey:
|
||||
status = False
|
||||
if self.formname != self.request_vars._formname:
|
||||
if formname != request_vars._formname:
|
||||
status = False
|
||||
if status and self.session:
|
||||
if status and session:
|
||||
# check if editing a record that has been modified by the server
|
||||
if hasattr(self,'record_hash') and self.record_hash != formkey:
|
||||
status = False
|
||||
@@ -1983,10 +1988,10 @@ class FORM(DIV):
|
||||
|
||||
def hidden_fields(self):
|
||||
c = []
|
||||
attr = self.attributes.get('hidden',{})
|
||||
if 'hidden' in self.attributes:
|
||||
for (key, value) in self.attributes.get('hidden',{}).items():
|
||||
c.append(INPUT(_type='hidden', _name=key, _value=value))
|
||||
|
||||
c = [INPUT(_type='hidden', _name=key, _value=value)
|
||||
for (key, value) in attr.iteritems()]
|
||||
if hasattr(self, 'formkey') and self.formkey:
|
||||
c.append(INPUT(_type='hidden', _name='_formkey',
|
||||
_value=self.formkey))
|
||||
@@ -2055,7 +2060,7 @@ class FORM(DIV):
|
||||
onsuccess(self)
|
||||
if next:
|
||||
if self.vars:
|
||||
for key,value in self.vars.items():
|
||||
for key,value in self.vars.iteritems():
|
||||
next = next.replace('[%s]' % key,
|
||||
urllib.quote(str(value)))
|
||||
if not next.startswith('/'):
|
||||
@@ -2116,11 +2121,11 @@ class FORM(DIV):
|
||||
inputs = [INPUT(_type='button',
|
||||
_value=name,
|
||||
_onclick=FORM.REDIRECT_JS % link) \
|
||||
for name,link in buttons.items()]
|
||||
for name,link in buttons.iteritems()]
|
||||
inputs += [INPUT(_type='hidden',
|
||||
_name=name,
|
||||
_value=value)
|
||||
for name,value in hidden.items()]
|
||||
for name,value in hidden.iteritems()]
|
||||
form = FORM(INPUT(_type='submit',_value=text),*inputs)
|
||||
form.process()
|
||||
return form
|
||||
@@ -2268,10 +2273,11 @@ class MENU(DIV):
|
||||
select = SELECT(**self.attributes)
|
||||
for item in data:
|
||||
if len(item) <= 4 or item[4] == True:
|
||||
if item[2]:
|
||||
select.append(OPTION(CAT(prefix, item[0]), _value=item[2], _selected=item[1]))
|
||||
if len(item)>3 and len(item[3]):
|
||||
self.serialize_mobile(item[3], select, prefix = CAT(prefix, item[0], '/'))
|
||||
select.append(OPTION(CAT(prefix, item[0]),
|
||||
_value=item[2], _selected=item[1]))
|
||||
if len(item)>3 and len(item[3]):
|
||||
self.serialize_mobile(
|
||||
item[3], select, prefix = CAT(prefix, item[0], '/'))
|
||||
select['_onchange'] = 'window.location=this.value'
|
||||
return select
|
||||
|
||||
@@ -2323,7 +2329,7 @@ def test():
|
||||
>>> print form.accepts({'myvar':'34'}, formname=None)
|
||||
False
|
||||
>>> print form.xml()
|
||||
<form action="" enctype="multipart/form-data" method="post"><input class="invalidinput" name="myvar" type="text" value="34" /><div class="error" id="myvar__error">invalid expression</div></form>
|
||||
<form action="" enctype="multipart/form-data" method="post"><input class="invalidinput" name="myvar" type="text" value="34" /><div class="error_wrapper"><div class="error" id="myvar__error">invalid expression</div></div></form>
|
||||
>>> print form.accepts({'myvar':'4'}, formname=None, keepvalues=True)
|
||||
True
|
||||
>>> print form.xml()
|
||||
@@ -2337,7 +2343,7 @@ def test():
|
||||
>>> print form.accepts({'myvar':'as df'}, formname=None)
|
||||
False
|
||||
>>> print form.xml()
|
||||
<form action=\"\" enctype=\"multipart/form-data\" method=\"post\"><input class=\"invalidinput\" name=\"myvar\" type=\"text\" value=\"as df\" /><div class=\"error\" id=\"myvar__error\">only alphanumeric!</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={}
|
||||
>>> form=FORM(INPUT(value=\"Hello World\", _name=\"var\", requires=IS_MATCH('^\w+$')))
|
||||
>>> if form.accepts({}, session,formname=None): print 'passed'
|
||||
|
||||
@@ -77,41 +77,45 @@ class HTTP(BaseException):
|
||||
str(cookie)[11:] for cookie in cookies.values()]
|
||||
|
||||
def to(self, responder):
|
||||
if self.status in defined_status:
|
||||
status = '%d %s' % (self.status, defined_status[self.status])
|
||||
status = self.status
|
||||
headers = self.headers
|
||||
if status in defined_status:
|
||||
status = '%d %s' % (status, defined_status[status])
|
||||
else:
|
||||
status = str(self.status) + ' '
|
||||
if not 'Content-Type' in self.headers:
|
||||
self.headers['Content-Type'] = 'text/html; charset=UTF-8'
|
||||
status = str(status) + ' '
|
||||
if not 'Content-Type' in headers:
|
||||
headers['Content-Type'] = 'text/html; charset=UTF-8'
|
||||
body = self.body
|
||||
if status[:1] == '4':
|
||||
if not body:
|
||||
body = status
|
||||
if isinstance(body, str):
|
||||
if len(body)<512 and self.headers['Content-Type'].startswith('text/html'):
|
||||
if len(body)<512 and headers['Content-Type'].startswith('text/html'):
|
||||
body += '<!-- %s //-->' % ('x'*512) ### trick IE
|
||||
self.headers['Content-Length'] = len(body)
|
||||
headers = []
|
||||
for (k, v) in self.headers.items():
|
||||
headers['Content-Length'] = len(body)
|
||||
rheaders = []
|
||||
for k, v in headers.iteritems():
|
||||
if isinstance(v, list):
|
||||
for item in v:
|
||||
headers.append((k, str(item)))
|
||||
rheaders += [(k, str(item)) for item in v]
|
||||
else:
|
||||
headers.append((k, str(v)))
|
||||
responder(status, headers)
|
||||
if hasattr(body, '__iter__') and not isinstance(self.body, str):
|
||||
rheaders.append((k, str(v)))
|
||||
responder(status, rheaders)
|
||||
if isinstance(body,str):
|
||||
return [body]
|
||||
elif hasattr(body, '__iter__'):
|
||||
return body
|
||||
return [str(body)]
|
||||
else:
|
||||
return [str(body)]
|
||||
|
||||
@property
|
||||
def message(self):
|
||||
'''
|
||||
"""
|
||||
compose a message describing this exception
|
||||
|
||||
"status defined_status [web2py_error]"
|
||||
"status defined_status [web2py_error]"
|
||||
|
||||
message elements that are not defined are omitted
|
||||
'''
|
||||
"""
|
||||
msg = '%(status)d'
|
||||
if self.status in defined_status:
|
||||
msg = '%(status)d %(defined_status)s'
|
||||
|
||||
@@ -10,7 +10,7 @@ Plural subsystem is created by Vladyslav Kozlovskyy (Ukraine)
|
||||
<dbdevelop@gmail.com>
|
||||
"""
|
||||
|
||||
from os import path as ospath, stat as ostat, sep as osep
|
||||
import os
|
||||
import re
|
||||
from utf8 import Utf8
|
||||
from cgi import escape
|
||||
@@ -20,7 +20,7 @@ import marshal
|
||||
import copy_reg
|
||||
from fileutils import abspath, listdir
|
||||
import settings
|
||||
from cfs import getcfs, cfs
|
||||
from cfs import getcfs
|
||||
from thread import allocate_lock
|
||||
from html import XML, xmlescape
|
||||
from contrib.markmin.markmin2html import render, markmin_escape
|
||||
@@ -28,10 +28,37 @@ from string import maketrans
|
||||
|
||||
__all__ = ['translator', 'findT', 'update_all_languages']
|
||||
|
||||
ospath = os.path
|
||||
ostat = os.stat
|
||||
osep = os.sep
|
||||
isdir = os.path.isdir
|
||||
is_gae = settings.global_settings.web2py_runtime_gae
|
||||
|
||||
DEFAULT_LANGUAGE = 'en'
|
||||
|
||||
# DEFAULT PLURAL-FORMS RULES:
|
||||
# language doesn't use plural forms
|
||||
DEFAULT_NPLURALS = 1
|
||||
# only one singular/plural form is used
|
||||
DEFAULT_GET_PLURAL_ID = lambda n: 0
|
||||
# word is unchangeable
|
||||
DEFAULT_CONSTRUCTOR_PLURAL_FORM = lambda word, plural_id: word
|
||||
|
||||
def safe_eval(text):
|
||||
if text.strip():
|
||||
try:
|
||||
import ast
|
||||
return ast.literal_eval(text)
|
||||
except ImportError:
|
||||
return eval(text,{},{})
|
||||
return None
|
||||
|
||||
# used as default filter in translator.M()
|
||||
markmin = lambda s: render( regex_param.sub(
|
||||
lambda m: '{' + markmin_escape(m.group('s')) + '}',
|
||||
s ), sep='br', autolinks=None, id_prefix='' )
|
||||
def markmin_aux(m):
|
||||
return '{%s}' % markmin_escape(m.group('s'))
|
||||
def markmin(s):
|
||||
return render(regex_param.sub(markmin_aux,s),
|
||||
sep='br', autolinks=None, id_prefix='')
|
||||
|
||||
NUMBERS = (int,long,float)
|
||||
|
||||
@@ -49,28 +76,24 @@ regex_param=re.compile(r'{(?P<s>.+?)}')
|
||||
regex_language = \
|
||||
re.compile('^([a-zA-Z]{2})(\-[a-zA-Z]{2})?(\-[a-zA-Z]+)?$')
|
||||
regex_langfile = re.compile('^[a-zA-Z]{2}(-[a-zA-Z]{2})?\.py$')
|
||||
regex_langinfo = re.compile("^[^'\"]*['\"]([^'\"]*)['\"]\s*:\s*['\"]([^'\"]*)['\"].*$")
|
||||
regex_backslash = re.compile(r"\\([\\{}%])")
|
||||
regex_plural = re.compile('%({.+?})')
|
||||
regex_plural_dict = re.compile('^{(?P<w>[^()[\]][^()[\]]*?)\((?P<n>[^()\[\]]+)\)}$') # %%{word(varname or number)}
|
||||
regex_plural_tuple = re.compile('^{(?P<w>[^[\]()]+)(?:\[(?P<i>\d+)\])?}$') # %%{word[index]} or %%{word}
|
||||
regex_plural_q = re.compile('^asdf$') # %%{?word?cnt}, %%{??cnt} or %%{?cnt}
|
||||
regex_plural_rules = re.compile('^plural_rules-[a-zA-Z]{2}(-[a-zA-Z]{2})?\.py$')
|
||||
|
||||
upper_fun = lambda s: unicode(s,'utf-8').upper().encode('utf-8')
|
||||
title_fun = lambda s: unicode(s,'utf-8').title().encode('utf-8')
|
||||
cap_fun = lambda s: unicode(s,'utf-8').capitalize().encode('utf-8')
|
||||
|
||||
# DEFAULT PLURAL-FORMS RULES:
|
||||
default_nplurals = 1 # language doesn't use plural forms
|
||||
default_get_plural_id = lambda n: 0 # only one singular/plural form is used
|
||||
default_construct_plural_form = lambda word, plural_id: word # word is unchangeable
|
||||
|
||||
# UTF8 helper functions
|
||||
def upper_fun(s):
|
||||
return unicode(s,'utf-8').upper().encode('utf-8')
|
||||
def title_fun(s):
|
||||
return unicode(s,'utf-8').title().encode('utf-8')
|
||||
def cap_fun(s):
|
||||
return lambda s: unicode(s,'utf-8').capitalize().encode('utf-8')
|
||||
ttab_in = maketrans("\\%{}", '\x1c\x1d\x1e\x1f')
|
||||
ttab_out = maketrans('\x1c\x1d\x1e\x1f', "\\%{}")
|
||||
|
||||
# cache of translated messages:
|
||||
# of structure:
|
||||
# global_language_cache:
|
||||
# { 'languages/xx.py':
|
||||
# ( {"def-message": "xx-message",
|
||||
# ...
|
||||
@@ -78,35 +101,38 @@ ttab_out = maketrans('\x1c\x1d\x1e\x1f', "\\%{}")
|
||||
# 'languages/yy.py': ( {dict}, lock_object )
|
||||
# ...
|
||||
# }
|
||||
tcache={}
|
||||
|
||||
global_language_cache={}
|
||||
|
||||
def get_from_cache(cache, val, fun):
|
||||
lock=cache[1]
|
||||
lang_dict, lock = cache
|
||||
lock.acquire()
|
||||
try:
|
||||
result=cache[0].get(val);
|
||||
result = lang_dict.get(val);
|
||||
finally:
|
||||
lock.release()
|
||||
if result:
|
||||
return result
|
||||
lock.acquire()
|
||||
try:
|
||||
result=cache[0].setdefault(val, fun())
|
||||
result = lang_dict.setdefault(val, fun())
|
||||
finally:
|
||||
lock.release()
|
||||
return result
|
||||
|
||||
def clear_cache(cache):
|
||||
lock=cache[1]
|
||||
def clear_cache(filename):
|
||||
cache = global_language_cache.setdefault(
|
||||
filename, ({}, allocate_lock()))
|
||||
lang_dict, lock = cache
|
||||
lock.acquire()
|
||||
try:
|
||||
cache[0].clear();
|
||||
lang_dict.clear();
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
|
||||
def lang_sampling(lang_tuple, langlist):
|
||||
""" search *lang_tuple* in *langlist*
|
||||
"""
|
||||
search *lang_tuple* in *langlist*
|
||||
|
||||
Args:
|
||||
lang_tuple (tuple of strings): ('aa'[[,'-bb'],'-cc'])
|
||||
@@ -137,32 +163,28 @@ def lang_sampling(lang_tuple, langlist):
|
||||
|
||||
|
||||
def read_dict_aux(filename):
|
||||
fp = portalocker.LockedFile(filename, 'r')
|
||||
lang_text = fp.read().replace('\r\n', '\n')
|
||||
fp.close()
|
||||
# clear cache of processed messages:
|
||||
clear_cache(tcache.setdefault(filename, ({}, allocate_lock())))
|
||||
if not lang_text.strip():
|
||||
return {}
|
||||
lang_text = portalocker.read_locked(filename).replace('\r\n', '\n')
|
||||
clear_cache(filename)
|
||||
try:
|
||||
return eval(lang_text)
|
||||
return safe_eval(lang_text) or {}
|
||||
except Exception, e:
|
||||
status='Syntax error in %s (%s)' % (filename, e)
|
||||
status = 'Syntax error in %s (%s)' % (filename, e)
|
||||
logging.error(status)
|
||||
return {'__corrupted__':status}
|
||||
|
||||
|
||||
def read_dict(filename):
|
||||
""" return dictionary with translation messages
|
||||
"""
|
||||
return dictionary with translation messages
|
||||
"""
|
||||
return getcfs('lang:'+filename, filename,
|
||||
lambda: read_dict_aux(filename))
|
||||
|
||||
|
||||
def get_lang_info(lang, langdir):
|
||||
"""retrieve lang information from *langdir*/*lang*.py file.
|
||||
Read few strings from lang.py file until keys !langname!,
|
||||
!langcode! or keys greater then '!*' were found
|
||||
"""
|
||||
retrieve lang information from *langdir*/*lang*.py file.
|
||||
Read few strings from lang.py file until keys !langname!,
|
||||
!langcode! or keys greater then '!*' were found
|
||||
|
||||
args:
|
||||
lang (str): lang-code or 'default'
|
||||
@@ -173,159 +195,78 @@ def get_lang_info(lang, langdir):
|
||||
e.g.: ('en', 'English', 1338549043.0)
|
||||
"""
|
||||
filename = ospath.join(langdir, lang+'.py')
|
||||
langcode=langname=''
|
||||
f = portalocker.LockedFile(filename, 'r')
|
||||
try:
|
||||
while not (langcode and langname):
|
||||
line = f.readline()
|
||||
if not line:
|
||||
break
|
||||
match=regex_langinfo.match(line)
|
||||
if match:
|
||||
k = match.group(1)
|
||||
if k == '!langname!':
|
||||
langname = match.group(2)
|
||||
elif k == '!langcode!':
|
||||
langcode = match.group(2)
|
||||
elif k[0:1] > '!':
|
||||
break
|
||||
finally:
|
||||
f.close()
|
||||
if not langcode:
|
||||
langcode = lang if lang != 'default' else 'en'
|
||||
return langcode, langname or langcode, ostat(filename).st_mtime
|
||||
d = read_dict(filename)
|
||||
langcode = d.get('!langcode!',DEFAULT_LANGUAGE)
|
||||
langname = d.get('!langname!',langcode)
|
||||
return (langcode, langname or langcode, ostat(filename).st_mtime)
|
||||
|
||||
def read_possible_languages_aux(langdir):
|
||||
def read_possible_languages(appdir):
|
||||
langs = {}
|
||||
# scan languages directory for langfiles:
|
||||
for langfile in [f for f in
|
||||
listdir(langdir, regex_langfile) +
|
||||
listdir(langdir, '^default\.py$')
|
||||
if osep not in f]:
|
||||
lang=langfile[:-3]
|
||||
langs[lang]=get_lang_info(lang, langdir)
|
||||
if 'default' not in langs:
|
||||
langdir = ospath.join(appdir,'languages')
|
||||
for filename in os.listdir(langdir):
|
||||
if regex_langfile.match(filename) or filename=='default.py':
|
||||
lang = filename[:-3]
|
||||
langs[lang] = get_lang_info(lang, langdir)
|
||||
if not 'en' in langs:
|
||||
# if default.py is not found, add default value:
|
||||
langs['default'] = ('en', 'English', 0)
|
||||
deflang=langs['default']
|
||||
if deflang[0] not in langs:
|
||||
# create language from default.py:
|
||||
langs[deflang[0]] = (deflang[0], deflang[1], 0)
|
||||
langs['en'] = ('en', 'English', 0)
|
||||
return langs
|
||||
|
||||
def read_possible_languages(path):
|
||||
lang_path = ospath.join(path, 'languages')
|
||||
return getcfs('langs:'+lang_path, lang_path,
|
||||
lambda: read_possible_languages_aux(lang_path))
|
||||
|
||||
|
||||
def read_plural_rules_aux(filename):
|
||||
"""retrieve plural rules from rules/*plural_rules-lang*.py file.
|
||||
def read_global_plural_rules(filename):
|
||||
"""
|
||||
retrieve plural rules from rules/*plural_rules-lang*.py file.
|
||||
|
||||
args:
|
||||
filename (str): plural_rules filename
|
||||
|
||||
returns:
|
||||
tuple(nplurals, get_plural_id, construct_plural_form)
|
||||
e.g.: (3, <function>, <function>)
|
||||
(nplurals, get_plural_id, construct_plural_form, status)
|
||||
e.g.: (3, <function>, <function>, ok)
|
||||
"""
|
||||
f = portalocker.LockedFile(filename, 'r')
|
||||
plural_py=f.read().replace('\r\n','\n')
|
||||
f.close()
|
||||
env = {}
|
||||
data = portalocker.read_locked(filename)
|
||||
try:
|
||||
exec(plural_py)
|
||||
nplurals=locals().get('nplurals', default_nplurals)
|
||||
get_plural_id=locals().get('get_plural_id', default_get_plural_id)
|
||||
construct_plural_form=locals().get('construct_plural_form',
|
||||
default_construct_plural_form)
|
||||
exec(data) in env
|
||||
status='ok'
|
||||
except Exception, e:
|
||||
nplurals=default_nplurals
|
||||
get_plural_id=default_get_plural_id
|
||||
construct_plural_form=default_construct_plural_form
|
||||
status='Syntax error in %s (%s)' % (filename, e)
|
||||
logging.error(status)
|
||||
nplurals = env.get('nplurals', DEFAULT_NPLURALS)
|
||||
get_plural_id = env.get('get_plural_id', DEFAULT_GET_PLURAL_ID)
|
||||
construct_plural_form = env.get('construct_plural_form',
|
||||
DEFAULT_CONSTRUCTOR_PLURAL_FORM)
|
||||
return (nplurals, get_plural_id, construct_plural_form, status)
|
||||
|
||||
def read_plural_rules(lang):
|
||||
filename = abspath('gluon','contrib','rules', 'plural_rules-%s.py' % lang)
|
||||
return getcfs('plural_rules-'+lang, filename,
|
||||
lambda: read_plural_rules_aux(filename))
|
||||
|
||||
pcache={}
|
||||
def read_possible_plurals():
|
||||
""" create list of all possible plural rules files
|
||||
result is cached to increase speed
|
||||
"""
|
||||
global pcache
|
||||
create list of all possible plural rules files
|
||||
result is cached to increase speed
|
||||
"""
|
||||
pdir = abspath('gluon','contrib','rules')
|
||||
plurals = {}
|
||||
# scan rules directory for plural_rules-*.py files:
|
||||
for pname in [f for f in listdir(pdir, regex_plural_rules)
|
||||
if osep not in f]:
|
||||
|
||||
lang=pname[13:-3]
|
||||
fname=ospath.join(pdir, pname)
|
||||
mtime=ostat(fname).st_mtime
|
||||
if lang in pcache and pcache[lang][2] == mtime:
|
||||
# if plural_file's mtime wasn't changed - use previous value:
|
||||
plurals[lang]=pcache[lang]
|
||||
else:
|
||||
# otherwise, reread plural_rules-file:
|
||||
if 'plural_rules-'+lang in cfs:
|
||||
n,f1,f2,status=read_plural_rules(lang)
|
||||
else:
|
||||
n,f1,f2,status=read_plural_rules_aux(fname)
|
||||
plurals[lang]=(n, pname, mtime, status)
|
||||
pcache=plurals
|
||||
return pcache
|
||||
|
||||
def get_plural_rules(languages):
|
||||
"""get plural-forms rules for language *lang*
|
||||
if rules not found - default rules will be return and lang=='unknown'
|
||||
|
||||
args:
|
||||
lang (str): the languages, for one of which the plural-forms is return
|
||||
|
||||
returns:
|
||||
tuples(lang, plural_rules-filename, nplurals,
|
||||
get_plural_id(), construct_plural_form(), status)
|
||||
"""
|
||||
if isinstance(languages, str):
|
||||
languages = [languages]
|
||||
|
||||
all_plurals=read_possible_plurals()
|
||||
for lang in languages:
|
||||
match_language = regex_language.match(lang.strip().lower())
|
||||
if match_language:
|
||||
match_language = tuple(part
|
||||
for part in match_language.groups()
|
||||
if part)
|
||||
lang = lang_sampling(match_language, all_plurals.keys())
|
||||
if lang:
|
||||
( nplurals,
|
||||
get_plural_id,
|
||||
construct_plural_form,
|
||||
status
|
||||
) = read_plural_rules(lang)
|
||||
return (lang, all_plurals[lang][1], nplurals,
|
||||
get_plural_id,
|
||||
construct_plural_form,
|
||||
status)
|
||||
return ('unknown', None, default_nplurals,
|
||||
default_get_plural_id,
|
||||
default_construct_plural_form,
|
||||
'ok')
|
||||
for pname in os.listdir(pdir):
|
||||
if not isdir(pname) and regex_plural_rules.match(pname):
|
||||
lang = pname[13:-3]
|
||||
fname = ospath.join(pdir, pname)
|
||||
n, f1, f2, status = read_global_plural_rules(fname)
|
||||
if status == 'ok':
|
||||
plurals[lang] = (lang, n, f1, f2, pname)
|
||||
plurals['default'] = ('default',
|
||||
DEFAULT_NPLURALS,
|
||||
DEFAULT_GET_PLURAL_ID,
|
||||
DEFAULT_CONSTRUCTOR_PLURAL_FORM,
|
||||
None)
|
||||
return plurals
|
||||
|
||||
PLURAL_RULES = read_possible_plurals()
|
||||
|
||||
def read_plural_dict_aux(filename):
|
||||
fp = portalocker.LockedFile(filename, 'r')
|
||||
lang_text = fp.read().replace('\r\n', '\n')
|
||||
fp.close()
|
||||
if not lang_text.strip():
|
||||
return {}
|
||||
lang_text = portalocker.read_locked(filename).replace('\r\n', '\n')
|
||||
try:
|
||||
return eval(lang_text)
|
||||
return eval(lang_text) or {}
|
||||
except Exception, e:
|
||||
status='Syntax error in %s (%s)' % (filename, e)
|
||||
logging.error(status)
|
||||
@@ -335,7 +276,6 @@ def read_plural_dict(filename):
|
||||
return getcfs('plurals:'+filename, filename,
|
||||
lambda: read_plural_dict_aux(filename))
|
||||
|
||||
|
||||
def write_plural_dict(filename, contents):
|
||||
if '__corrupted__' in contents:
|
||||
return
|
||||
@@ -466,13 +406,10 @@ class lazyT(object):
|
||||
return lazyT(self.m, symbols, self.T, self.f, self.t, self.M)
|
||||
|
||||
class translator(object):
|
||||
|
||||
"""
|
||||
this class is instantiated by gluon.compileapp.build_environment
|
||||
as the T object
|
||||
|
||||
::
|
||||
|
||||
T.force(None) # turns off translation
|
||||
T.force('fr, it') # forces web2py to translate using fr.py or it.py
|
||||
|
||||
@@ -480,27 +417,21 @@ class translator(object):
|
||||
|
||||
notice 1: there is no need to force since, by default, T uses
|
||||
http_accept_language to determine a translation file.
|
||||
|
||||
notice 2: en and en-en are considered different languages!
|
||||
|
||||
notice 3: if language xx-yy is not found force() probes other similar
|
||||
languages using such algorithm: xx-yy.py -> xx.py -> xx-yy*.py -> xx*.py
|
||||
notice 2:
|
||||
en and en-en are considered different languages!
|
||||
notice 3:
|
||||
if language xx-yy is not found force() probes other similar
|
||||
languages using such algorithm:
|
||||
xx-yy.py -> xx.py -> xx-yy*.py -> xx*.py
|
||||
"""
|
||||
|
||||
def __init__(self, request):
|
||||
global tcache
|
||||
self.request = request
|
||||
self.folder = request.folder
|
||||
dfile = ospath.join(self.folder,'languages','default.py')
|
||||
if ospath.exists(dfile):
|
||||
self.default_language_file = dfile
|
||||
self.default_t = read_dict(dfile)
|
||||
else: # languages/default.py is not found
|
||||
self.default_language_file = ospath.join(self.folder, 'languages','')
|
||||
self.default_t = {}
|
||||
self.cache = tcache.setdefault(self.default_language_file, ({}, allocate_lock()))
|
||||
self.current_languages = [self.get_possible_languages_info('default')[0]]
|
||||
self.langpath = ospath.join(self.folder,'languages')
|
||||
self.filenames = set(os.listdir(self.langpath))
|
||||
self.http_accept_language = request.env.http_accept_language
|
||||
# self.cache # filled in self.force()
|
||||
# self.accepted_language = None # filled in self.force()
|
||||
# self.language_file = None # filled in self.force()
|
||||
# self.plural_language = None # filled in self.force()
|
||||
@@ -511,12 +442,89 @@ class translator(object):
|
||||
# self.plural_file = None # filled in self.force()
|
||||
# self.plural_dict = None # filled in self.force()
|
||||
# self.plural_status = None # filled in self.force()
|
||||
self.requested_languages = self.force(self.http_accept_language)
|
||||
|
||||
self.requested_languages = \
|
||||
self.force(self.http_accept_language)
|
||||
self.lazy = True
|
||||
self.otherTs = {}
|
||||
self.filter = markmin
|
||||
self.ftag = 'markmin'
|
||||
|
||||
def get_possible_languages(self):
|
||||
return [lang[:-3] for lang in self.filenames \
|
||||
if regex_langfile.match(lang)]
|
||||
|
||||
def set_current_languages(self, *languages):
|
||||
"""
|
||||
set current AKA "default" languages
|
||||
setting one of this languages makes force() function
|
||||
turn translation off to use default language
|
||||
"""
|
||||
if len(languages) == 1 and isinstance(
|
||||
languages[0], (tuple, list)):
|
||||
languages = languages[0]
|
||||
self.current_languages = languages
|
||||
self.force(self.http_accept_language)
|
||||
|
||||
def set_plural(self, language):
|
||||
"""
|
||||
initialize plural forms subsystem
|
||||
invoked from self.force()
|
||||
"""
|
||||
lang = language[:2]
|
||||
(self.plural_language,
|
||||
self.nplurals,
|
||||
self.get_plural_id,
|
||||
self.construct_plural_form,
|
||||
self.plural_filename
|
||||
) = PLURAL_RULES.get(language,PLURAL_RULES['default'])
|
||||
for lang in (language, language[:5], language[:2]):
|
||||
filename = 'plural-%s.py' % lang
|
||||
if filename in self.filenames:
|
||||
self.plural_file = ospath.join(self.langpath,filename)
|
||||
self.plural_dict = read_plural_dict(self.plural_file)
|
||||
break
|
||||
else:
|
||||
self.plural_file = None
|
||||
self.plural_dict = {}
|
||||
|
||||
def plural(self, word, n):
|
||||
"""
|
||||
get plural form of word for number *n*
|
||||
NOTE: *word* MUST be defined in current language
|
||||
(T.accepted_language)
|
||||
|
||||
invoked from T()/M() in %%{} tag
|
||||
args:
|
||||
word (str): word in singular
|
||||
n (numeric): number plural form created for
|
||||
|
||||
returns:
|
||||
(str): word in appropriate singular/plural form
|
||||
"""
|
||||
nplurals = self.nplurals
|
||||
if int(n)==1:
|
||||
return word
|
||||
elif word:
|
||||
id = min(int(n)-1,1) # self.get_plural_id(abs(int(n)))
|
||||
# id = 0 first plural form
|
||||
# id = 1 second plural form
|
||||
# etc.
|
||||
forms = self.plural_dict.get(word, [])
|
||||
if len(forms)>=id:
|
||||
# have this plural form
|
||||
return forms[id-1]
|
||||
else:
|
||||
# guessing this plural form
|
||||
forms += ['']*(nplurals-len(forms)-1)
|
||||
form = self.construct_plural_form(word, id)
|
||||
forms[id-1] = form
|
||||
self.plural_dict[word] = forms
|
||||
if self.plural_file and not is_gae:
|
||||
write_plural_dict(self.plural_file,
|
||||
self.plural_dict)
|
||||
return form
|
||||
|
||||
def get_possible_languages_info(self, lang=None):
|
||||
"""
|
||||
return info for selected language or dictionary with all
|
||||
@@ -533,90 +541,12 @@ class translator(object):
|
||||
langname(from !langname! key),
|
||||
langfile_mtime ) }
|
||||
"""
|
||||
if lang:
|
||||
return read_possible_languages(self.folder).get(lang)
|
||||
return read_possible_languages(self.folder)
|
||||
|
||||
def get_possible_languages(self):
|
||||
""" get list of all possible languages for current applications """
|
||||
return sorted( set(lang for lang in
|
||||
read_possible_languages(self.folder).iterkeys()
|
||||
if lang != 'default')
|
||||
| set(self.current_languages))
|
||||
|
||||
def set_current_languages(self, *languages):
|
||||
"""
|
||||
set current AKA "default" languages
|
||||
setting one of this languages makes force() function
|
||||
turn translation off to use default language
|
||||
"""
|
||||
if len(languages) == 1 and isinstance(languages[0], (tuple, list)):
|
||||
languages = languages[0]
|
||||
self.current_languages = languages
|
||||
self.force(self.http_accept_language)
|
||||
|
||||
def set_plural(self, languages):
|
||||
""" initialize plural forms subsystem
|
||||
invoked from self.force()
|
||||
"""
|
||||
( self.plural_language,
|
||||
self.plural_rules_file,
|
||||
self.nplurals,
|
||||
self.get_plural_id,
|
||||
self.construct_plural_form,
|
||||
self.plural_status
|
||||
) = get_plural_rules(languages)
|
||||
|
||||
if self.plural_language == 'unknown':
|
||||
self.plural_file = None
|
||||
self.plural_dict = {}
|
||||
else:
|
||||
self.plural_file = ospath.join(self.folder,
|
||||
'languages',
|
||||
'plural-%s.py' % self.plural_language)
|
||||
if ospath.exists(self.plural_file):
|
||||
self.plural_dict = read_plural_dict(self.plural_file)
|
||||
else:
|
||||
self.plural_dict = {}
|
||||
|
||||
def plural(self, word, n):
|
||||
""" get plural form of word for number *n*
|
||||
NOTE: *word" MUST be defined in current language
|
||||
(T.accepted_language)
|
||||
|
||||
invoked from T()/M() in %%{} tag
|
||||
args:
|
||||
word (str): word in singular
|
||||
n (numeric): number plural form created for
|
||||
|
||||
returns:
|
||||
(str): word in appropriate singular/plural form
|
||||
"""
|
||||
nplurals = self.nplurals
|
||||
if word:
|
||||
id = self.get_plural_id(abs(int(n)))
|
||||
if id > 0:
|
||||
forms = self.plural_dict.get(word, [])
|
||||
if forms:
|
||||
try:
|
||||
form = forms[id-1]
|
||||
except:
|
||||
form = None
|
||||
if form: return form
|
||||
form = self.construct_plural_form(word, id)
|
||||
if len(forms) < nplurals-1:
|
||||
forms.extend('' for i in xrange(nplurals-len(forms)-1))
|
||||
forms[id-1] = form
|
||||
self.plural_dict[word] = forms
|
||||
if (self.plural_file and
|
||||
not settings.global_settings.web2py_runtime_gae):
|
||||
write_plural_dict(self.plural_file, self.plural_dict)
|
||||
return form
|
||||
return word
|
||||
info = read_possible_languages(self.folder)
|
||||
if lang: info = info.get(lang)
|
||||
return info
|
||||
|
||||
def force(self, *languages):
|
||||
"""
|
||||
|
||||
select language(s) for translation
|
||||
|
||||
if a list of languages is passed as a parameter,
|
||||
@@ -627,49 +557,41 @@ class translator(object):
|
||||
default language will be selected if none
|
||||
of them matches possible_languages.
|
||||
"""
|
||||
global tcache
|
||||
language = ''
|
||||
|
||||
if not languages or languages[0] is None:
|
||||
if isinstance(languages,str):
|
||||
languages = regex_language.findall(languages.lower())
|
||||
elif not languages or languages[0] is None:
|
||||
languages = []
|
||||
if len(languages) == 1 and isinstance(languages[0], (str, unicode)):
|
||||
languages = languages[0]
|
||||
|
||||
if languages:
|
||||
if isinstance(languages, (str, unicode)):
|
||||
parts = languages.split(';')
|
||||
languages = []
|
||||
for al in parts:
|
||||
languages.extend(al.split(','))
|
||||
|
||||
possible_languages = self.get_possible_languages()
|
||||
for lang in languages:
|
||||
match_language = regex_language.match(lang.strip().lower())
|
||||
if match_language:
|
||||
match_language = tuple(part
|
||||
for part in match_language.groups()
|
||||
if part)
|
||||
language = lang_sampling(match_language,
|
||||
self.current_languages)
|
||||
if language:
|
||||
break
|
||||
language = lang_sampling(match_language, possible_languages)
|
||||
if language:
|
||||
self.language_file = ospath.join(self.folder,
|
||||
'languages',
|
||||
language + '.py')
|
||||
if ospath.exists(self.language_file):
|
||||
self.t = read_dict(self.language_file)
|
||||
self.cache = tcache.setdefault(self.language_file,
|
||||
({},allocate_lock()))
|
||||
self.set_plural(language)
|
||||
self.accepted_language = language
|
||||
return languages
|
||||
self.accepted_language = language or self.current_languages[0]
|
||||
self.language_file = self.default_language_file
|
||||
self.cache = tcache[self.language_file]
|
||||
self.t = self.default_t
|
||||
self.set_plural(language or self.current_languages)
|
||||
for lang in languages:
|
||||
if lang+'.py' in self.filenames:
|
||||
language = lang
|
||||
langfile = language+'.py'
|
||||
break
|
||||
elif len(lang)>5 and lang[:5]+'.py' in self.filenames:
|
||||
language = lang[:5]
|
||||
langfile = language+'.py'
|
||||
break
|
||||
elif len(lang)>2 and lang[:2]+'.py' in self.filenames:
|
||||
language = lang[:2]
|
||||
langfile = language+'.py'
|
||||
break
|
||||
else:
|
||||
if 'default.py' in self.filenames:
|
||||
language = DEFAULT_LANGUAGE
|
||||
langfile = 'default.py'
|
||||
else:
|
||||
language = DEFAULT_LANGUAGE
|
||||
langfile = None
|
||||
self.accepted_language = language
|
||||
if langfile:
|
||||
self.language_file = ospath.join(self.langpath,langfile)
|
||||
self.t = read_dict(self.language_file)
|
||||
else:
|
||||
self.language_file = None
|
||||
self.t = {}
|
||||
self.cache = global_language_cache.setdefault(
|
||||
self.language_file,({},allocate_lock()))
|
||||
self.set_plural(language)
|
||||
return languages
|
||||
|
||||
def __call__(self, message, symbols={}, language=None, lazy=None):
|
||||
@@ -700,26 +622,30 @@ class translator(object):
|
||||
prefix = '@'+(ftag or 'userdef')+'\x01'
|
||||
else:
|
||||
prefix = '@'+self.ftag+'\x01'
|
||||
message = get_from_cache(self.cache, prefix+message,
|
||||
lambda: get_tr(message, prefix, filter))
|
||||
message = get_from_cache(
|
||||
self.cache, prefix+message,
|
||||
lambda: get_tr(message, prefix, filter))
|
||||
if symbols or symbols == 0 or symbols == "":
|
||||
if isinstance(symbols, dict):
|
||||
symbols.update( (key, xmlescape(value).translate(ttab_in))
|
||||
for key, value in symbols.iteritems()
|
||||
if not isinstance(value, NUMBERS) )
|
||||
symbols.update(
|
||||
(key, xmlescape(value).translate(ttab_in))
|
||||
for key, value in symbols.iteritems()
|
||||
if not isinstance(value, NUMBERS) )
|
||||
else:
|
||||
if not isinstance(symbols, tuple):
|
||||
symbols = (symbols,)
|
||||
symbols = tuple(value if isinstance(value, NUMBERS)
|
||||
else xmlescape(value).translate(ttab_in)
|
||||
for value in symbols)
|
||||
symbols = tuple(
|
||||
value if isinstance(value, NUMBERS)
|
||||
else xmlescape(value).translate(ttab_in)
|
||||
for value in symbols)
|
||||
message = self.params_substitution(message, symbols)
|
||||
return XML(message.translate(ttab_out))
|
||||
|
||||
def M(self, message, symbols={}, language=None, lazy=None, filter=None, ftag=None):
|
||||
""" get cached translated markmin-message with inserted parametes
|
||||
|
||||
if lazy==True lazyT object is returned
|
||||
def M(self, message, symbols={}, language=None,
|
||||
lazy=None, filter=None, ftag=None):
|
||||
"""
|
||||
get cached translated markmin-message with inserted parametes
|
||||
if lazy==True lazyT object is returned
|
||||
"""
|
||||
if lazy is None:
|
||||
lazy = self.lazy
|
||||
@@ -751,18 +677,20 @@ class translator(object):
|
||||
"""
|
||||
key = prefix+message
|
||||
mt = self.t.get(key, None)
|
||||
if mt is not None:
|
||||
return mt
|
||||
if not message.startswith('##') and not '\n' in message:
|
||||
tokens = message.rsplit('##', 1)
|
||||
else:
|
||||
# this allows markmin syntax in translations
|
||||
tokens = [message]
|
||||
self.t[key] = mt = self.default_t.get(key, tokens[0])
|
||||
if (self.language_file != self.default_language_file and
|
||||
not settings.global_settings.web2py_runtime_gae):
|
||||
write_dict(self.language_file, self.t)
|
||||
return regex_backslash.sub(lambda m: m.group(1).translate(ttab_in), mt)
|
||||
if mt is None:
|
||||
# we did not find a translation
|
||||
if message.find('##')>0 and not '\n' in message:
|
||||
# remove comments
|
||||
message = message.rsplit('##', 1)[0]
|
||||
# guess translation same as original
|
||||
self.t[key] = mt = message
|
||||
# update language file for later translation
|
||||
if self.language_file and not is_gae:
|
||||
write_dict(self.language_file, self.t)
|
||||
# fix backslash escaping
|
||||
mt = regex_backslash.sub(
|
||||
lambda m: m.group(1).translate(ttab_in), mt)
|
||||
return mt
|
||||
|
||||
def params_substitution(self, message, symbols):
|
||||
"""
|
||||
@@ -866,19 +794,21 @@ class translator(object):
|
||||
"""
|
||||
get cached translated message with inserted parameters(symbols)
|
||||
"""
|
||||
message = get_from_cache(self.cache, message, lambda: self.get_t(message))
|
||||
message = get_from_cache(self.cache, message,
|
||||
lambda: self.get_t(message))
|
||||
if symbols or symbols == 0 or symbols == "":
|
||||
if isinstance(symbols, dict):
|
||||
symbols.update( (key, str(value).translate(ttab_in))
|
||||
for key, value in symbols.iteritems()
|
||||
if not isinstance(value, NUMBERS) )
|
||||
symbols.update(
|
||||
(key, str(value).translate(ttab_in))
|
||||
for key, value in symbols.iteritems()
|
||||
if not isinstance(value, NUMBERS) )
|
||||
else:
|
||||
if not isinstance(symbols, tuple):
|
||||
symbols = (symbols,)
|
||||
symbols = tuple(value if isinstance(value, NUMBERS)
|
||||
else str(value).translate(ttab_in)
|
||||
for value in symbols)
|
||||
|
||||
symbols = tuple(
|
||||
value if isinstance(value, NUMBERS)
|
||||
else str(value).translate(ttab_in)
|
||||
for value in symbols)
|
||||
message = self.params_substitution(message, symbols)
|
||||
return message.translate(ttab_out)
|
||||
|
||||
@@ -892,26 +822,25 @@ def findT(path, language='en'):
|
||||
cp = ospath.join(path, 'controllers')
|
||||
vp = ospath.join(path, 'views')
|
||||
mop = ospath.join(path, 'modules')
|
||||
for file in listdir(mp, '^.+\.py$', 0) + listdir(cp, '^.+\.py$', 0)\
|
||||
+ listdir(vp, '^.+\.html$', 0) + listdir(mop, '^.+\.py$', 0):
|
||||
fp = portalocker.LockedFile(file, 'r')
|
||||
data = fp.read()
|
||||
fp.close()
|
||||
for filename in \
|
||||
listdir(mp, '^.+\.py$', 0)+listdir(cp, '^.+\.py$', 0)\
|
||||
+listdir(vp, '^.+\.html$', 0)+listdir(mop, '^.+\.py$', 0):
|
||||
data = portalocker.read_locked(filename)
|
||||
items = regex_translate.findall(data)
|
||||
for item in items:
|
||||
try:
|
||||
message = eval(item)
|
||||
if not message.startswith('#') and not '\n' in message:
|
||||
tokens = message.rsplit('##', 1)
|
||||
else:
|
||||
# this allows markmin syntax in translations
|
||||
tokens = [message]
|
||||
if len(tokens) == 2:
|
||||
message = tokens[0].strip() + '##' + tokens[1].strip()
|
||||
if message and not message in sentences:
|
||||
sentences[message] = message
|
||||
message = safe_eval(item)
|
||||
except:
|
||||
pass
|
||||
continue # silently ignore inproperly formatted strings
|
||||
if not message.startswith('#') and not '\n' in message:
|
||||
tokens = message.rsplit('##', 1)
|
||||
else:
|
||||
# this allows markmin syntax in translations
|
||||
tokens = [message]
|
||||
if len(tokens) == 2:
|
||||
message = tokens[0].strip()+'##'+tokens[1].strip()
|
||||
if message and not message in sentences:
|
||||
sentences[message] = message
|
||||
if not '!langcode!' in sentences:
|
||||
sentences['!langcode!'] = (
|
||||
'en' if language in ('default', 'en') else language)
|
||||
@@ -937,6 +866,3 @@ def update_all_languages(application_path):
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import re
|
||||
import copy
|
||||
import sys
|
||||
import time
|
||||
import thread
|
||||
import datetime
|
||||
import signal
|
||||
import socket
|
||||
@@ -29,6 +28,7 @@ import tempfile
|
||||
import random
|
||||
import string
|
||||
import urllib2
|
||||
from thread import allocate_lock
|
||||
|
||||
from fileutils import abspath, write_file, parse_version
|
||||
from settings import global_settings
|
||||
@@ -69,8 +69,11 @@ import logging.config
|
||||
import gluon.messageboxhandler
|
||||
logging.gluon = gluon
|
||||
|
||||
exists = os.path.exists
|
||||
pjoin = os.path.join
|
||||
|
||||
logpath = abspath("logging.conf")
|
||||
if os.path.exists(logpath):
|
||||
if exists(logpath):
|
||||
logging.config.fileConfig(abspath("logging.conf"))
|
||||
else:
|
||||
logging.basicConfig()
|
||||
@@ -87,10 +90,10 @@ from dal import BaseAdapter
|
||||
from settings import global_settings
|
||||
from validators import CRYPT
|
||||
from cache import Cache
|
||||
from html import URL as Url, xmlescape
|
||||
from html import URL, xmlescape
|
||||
from utils import is_valid_ip_address
|
||||
from rewrite import load, url_in, thread as rwthread, try_rewrite_on_error
|
||||
import newcron
|
||||
import rewrite
|
||||
|
||||
__all__ = ['wsgibase', 'save_password', 'appfactory', 'HttpServer']
|
||||
|
||||
@@ -103,7 +106,7 @@ requests = 0 # gc timer
|
||||
regex_client = re.compile('[\w\-:]+(\.[\w\-]+)*\.?') # ## to account for IPV6
|
||||
|
||||
try:
|
||||
version_info = open(os.path.join(global_settings.gluon_parent, 'VERSION'), 'r')
|
||||
version_info = open(pjoin(global_settings.gluon_parent, 'VERSION'), 'r')
|
||||
raw_version_string = version_info.read().strip()
|
||||
version_info.close()
|
||||
global_settings.web2py_version = parse_version(raw_version_string)
|
||||
@@ -118,7 +121,7 @@ except:
|
||||
if not global_settings.web2py_runtime_gae:
|
||||
logger.warn('unable to import Rocket')
|
||||
|
||||
rewrite.load()
|
||||
load()
|
||||
|
||||
def get_client(env):
|
||||
"""
|
||||
@@ -129,11 +132,16 @@ def get_client(env):
|
||||
"""
|
||||
g = regex_client.search(env.get('http_x_forwarded_for', ''))
|
||||
if g:
|
||||
return g.group()
|
||||
g = regex_client.search(env.get('remote_addr', ''))
|
||||
if g:
|
||||
return g.group()
|
||||
return '127.0.0.1'
|
||||
client = g.group()
|
||||
else:
|
||||
g = regex_client.search(env.get('remote_addr', ''))
|
||||
if g:
|
||||
client = g.group()
|
||||
else:
|
||||
client = '127.0.0.1'
|
||||
if not is_valid_ip_address(client):
|
||||
raise HTTP(400,"Bad Request (request.client=%s)" % client)
|
||||
return client
|
||||
|
||||
def copystream_progress(request, chunk_size= 10**5):
|
||||
"""
|
||||
@@ -253,7 +261,8 @@ def middleware_aux(request, response, *middleware_apps):
|
||||
for item in middleware_apps:
|
||||
app=item(app)
|
||||
def caller(app):
|
||||
return app(request.wsgi.environ,request.wsgi.start_response)
|
||||
wsgi = request.wsgi
|
||||
return app(wsgi.environ, wsgi.start_response)
|
||||
return lambda caller=caller, app=app: caller(app)
|
||||
return middleware
|
||||
|
||||
@@ -279,14 +288,14 @@ def parse_get_post_vars(request, environ):
|
||||
|
||||
# parse POST variables on POST, PUT, BOTH only in post_vars
|
||||
try:
|
||||
request.body = copystream_progress(request) ### stores request body
|
||||
request.body = body = copystream_progress(request)
|
||||
except IOError:
|
||||
raise HTTP(400,"Bad Request - HTTP body is incomplete")
|
||||
if (request.body and request.env.request_method in ('POST', 'PUT', 'BOTH')):
|
||||
dpost = cgi.FieldStorage(fp=request.body,environ=environ,keep_blank_values=1)
|
||||
if (body and request.env.request_method in ('POST', 'PUT', 'BOTH')):
|
||||
dpost = cgi.FieldStorage(fp=body,environ=environ,keep_blank_values=1)
|
||||
# The same detection used by FieldStorage to detect multipart POSTs
|
||||
is_multipart = dpost.type[:10] == 'multipart/'
|
||||
request.body.seek(0)
|
||||
body.seek(0)
|
||||
isle25 = sys.version_info[1] <= 5
|
||||
|
||||
def listify(a):
|
||||
@@ -357,9 +366,10 @@ def wsgibase(environ, responder):
|
||||
request = Request()
|
||||
response = Response()
|
||||
session = Session()
|
||||
request.env.web2py_path = global_settings.applications_parent
|
||||
request.env.web2py_version = web2py_version
|
||||
request.env.update(global_settings)
|
||||
env = request.env
|
||||
env.web2py_path = global_settings.applications_parent
|
||||
env.web2py_version = web2py_version
|
||||
env.update(global_settings)
|
||||
static_file = False
|
||||
try:
|
||||
try:
|
||||
@@ -373,79 +383,80 @@ def wsgibase(environ, responder):
|
||||
# serve file if static
|
||||
# ##################################################
|
||||
|
||||
if not environ.get('PATH_INFO',None) and \
|
||||
environ.get('REQUEST_URI',None):
|
||||
# for fcgi, get path_info and query_string from request_uri
|
||||
eget = environ.get
|
||||
if not eget('PATH_INFO',None) and eget('REQUEST_URI',None):
|
||||
# for fcgi, get path_info and
|
||||
# query_string from request_uri
|
||||
items = environ['REQUEST_URI'].split('?')
|
||||
environ['PATH_INFO'] = items[0]
|
||||
if len(items) > 1:
|
||||
environ['QUERY_STRING'] = items[1]
|
||||
else:
|
||||
environ['QUERY_STRING'] = ''
|
||||
if not environ.get('HTTP_HOST',None):
|
||||
environ['HTTP_HOST'] = '%s:%s' % (environ.get('SERVER_NAME'),
|
||||
environ.get('SERVER_PORT'))
|
||||
if not eget('HTTP_HOST',None):
|
||||
environ['HTTP_HOST'] = \
|
||||
eget('SERVER_NAME')+':'+eget('SERVER_PORT')
|
||||
|
||||
|
||||
(static_file, environ) = url_in(request, environ)
|
||||
|
||||
(static_file, environ) = rewrite.url_in(request, environ)
|
||||
if static_file:
|
||||
if environ.get('QUERY_STRING', '')[:10] == 'attachment':
|
||||
response.headers['Content-Disposition'] = 'attachment'
|
||||
if environ.get('QUERY_STRING','').startswith(
|
||||
'attachment'):
|
||||
response.headers['Content-Disposition'] \
|
||||
= 'attachment'
|
||||
response.stream(static_file, request=request)
|
||||
|
||||
# ##################################################
|
||||
# fill in request items
|
||||
# ##################################################
|
||||
|
||||
http_host = request.env.http_host.split(':',1)[0]
|
||||
|
||||
local_hosts = [http_host,'::1','127.0.0.1','::ffff:127.0.0.1']
|
||||
app = request.application ## must go after url_in!
|
||||
|
||||
http_host = env.http_host.split(':',1)[0]
|
||||
local_hosts = [http_host,'::1','127.0.0.1',
|
||||
'::ffff:127.0.0.1']
|
||||
if not global_settings.web2py_runtime_gae:
|
||||
local_hosts.append(socket.gethostname())
|
||||
try: local_hosts.append(socket.gethostbyname(http_host))
|
||||
except socket.gaierror: pass
|
||||
request.client = get_client(request.env)
|
||||
if not is_valid_ip_address(request.client):
|
||||
raise HTTP(400,"Bad Request (request.client=%s)" % \
|
||||
request.client)
|
||||
request.folder = abspath('applications',
|
||||
request.application) + os.sep
|
||||
x_req_with = str(request.env.http_x_requested_with).lower()
|
||||
request.ajax = x_req_with == 'xmlhttprequest'
|
||||
request.cid = request.env.http_web2py_component_element
|
||||
request.is_local = request.env.remote_addr in local_hosts
|
||||
request.is_https = request.env.wsgi_url_scheme \
|
||||
in ['https', 'HTTPS'] or request.env.https == 'on'
|
||||
|
||||
# ##################################################
|
||||
# compute a request.uuid to be used for tickets and toolbar
|
||||
# ##################################################
|
||||
|
||||
response.uuid = request.compute_uuid()
|
||||
try:
|
||||
local_hosts.append(
|
||||
socket.gethostbyname(http_host))
|
||||
except socket.gaierror:
|
||||
pass
|
||||
client = get_client(env)
|
||||
x_req_with = str(env.http_x_requested_with).lower()
|
||||
|
||||
request.update(dict(
|
||||
client = client,
|
||||
folder = abspath('applications',app) + os.sep,
|
||||
ajax = x_req_with == 'xmlhttprequest',
|
||||
cid = env.http_web2py_component_element,
|
||||
is_local = env.remote_addr in local_hosts,
|
||||
is_https = env.wsgi_url_scheme \
|
||||
in ['https', 'HTTPS'] or env.https=='on'))
|
||||
request.uuid = request.compute_uuid() # requires client
|
||||
|
||||
# ##################################################
|
||||
# access the requested application
|
||||
# ##################################################
|
||||
|
||||
if not os.path.exists(request.folder):
|
||||
if request.application == \
|
||||
rewrite.thread.routes.default_application \
|
||||
and request.application != 'welcome':
|
||||
request.application = 'welcome'
|
||||
redirect(Url(r=request))
|
||||
elif rewrite.thread.routes.error_handler:
|
||||
_handler = rewrite.thread.routes.error_handler
|
||||
redirect(Url(_handler['application'],
|
||||
if not exists(request.folder):
|
||||
if app == rwthread.routes.default_application \
|
||||
and app != 'welcome':
|
||||
redirect(URL('welcome','default','index'))
|
||||
elif rwthread.routes.error_handler:
|
||||
_handler = rwthread.routes.error_handler
|
||||
redirect(URL(_handler['application'],
|
||||
_handler['controller'],
|
||||
_handler['function'],
|
||||
args=request.application))
|
||||
args=app))
|
||||
else:
|
||||
raise HTTP(404, rewrite.thread.routes.error_message \
|
||||
raise HTTP(404, rwthread.routes.error_message \
|
||||
% 'invalid request',
|
||||
web2py_error='invalid application')
|
||||
elif not request.is_local and \
|
||||
os.path.exists(os.path.join(request.folder,'DISABLED')):
|
||||
exists(pjoin(request.folder,'DISABLED')):
|
||||
raise HTTP(503, "<html><body><h1>Temporarily down for maintenance</h1></body></html>")
|
||||
request.url = Url(r=request,
|
||||
request.url = URL(r=request,
|
||||
args=request.args,
|
||||
extension=request.raw_extension)
|
||||
|
||||
@@ -492,22 +503,23 @@ def wsgibase(environ, responder):
|
||||
# ##################################################
|
||||
# set no-cache headers
|
||||
# ##################################################
|
||||
|
||||
response.headers['Content-Type'] = \
|
||||
|
||||
headers = response.headers
|
||||
headers['Content-Type'] = \
|
||||
contenttype('.'+request.extension)
|
||||
response.headers['Cache-Control'] = \
|
||||
headers['Cache-Control'] = \
|
||||
'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
|
||||
response.headers['Expires'] = \
|
||||
headers['Expires'] = \
|
||||
time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime())
|
||||
response.headers['Pragma'] = 'no-cache'
|
||||
headers['Pragma'] = 'no-cache'
|
||||
|
||||
# ##################################################
|
||||
# run controller
|
||||
# ##################################################
|
||||
|
||||
if global_settings.debugging and request.application != "admin":
|
||||
if global_settings.debugging and app != "admin":
|
||||
import gluon.debug
|
||||
# activate the debugger and wait to reach application code
|
||||
# activate the debugger
|
||||
gluon.debug.dbg.do_debug(mainpyfile=request.folder)
|
||||
|
||||
serve_controller(request, response, session)
|
||||
@@ -549,22 +561,24 @@ def wsgibase(environ, responder):
|
||||
# ##################################################
|
||||
|
||||
if request.cid:
|
||||
if response.flash and not 'web2py-component-flash' \
|
||||
in http_response.headers:
|
||||
http_response.headers['web2py-component-flash'] = \
|
||||
rheaders
|
||||
if response.flash and \
|
||||
not 'web2py-component-flash' in rheaders:
|
||||
rheaders['web2py-component-flash'] = \
|
||||
urllib2.quote(xmlescape(response.flash)\
|
||||
.replace('\n',''))
|
||||
if response.js and not 'web2py-component-command' \
|
||||
in http_response.headers:
|
||||
http_response.headers['web2py-component-command'] = \
|
||||
if response.js and \
|
||||
not 'web2py-component-command' in readers:
|
||||
readers['web2py-component-command'] = \
|
||||
response.js.replace('\n','')
|
||||
rcookies = response.cookies
|
||||
if session._forget and \
|
||||
response.session_id_name in response.cookies:
|
||||
del response.cookies[response.session_id_name]
|
||||
del rcookies[response.session_id_name]
|
||||
elif session._secure:
|
||||
response.cookies[response.session_id_name]['secure'] = True
|
||||
rcookies[response.session_id_name]['secure'] = True
|
||||
|
||||
http_response.cookies2headers(response.cookies)
|
||||
http_response.cookies2headers(rcookies)
|
||||
ticket=None
|
||||
|
||||
except RestrictedError, e:
|
||||
@@ -583,7 +597,7 @@ def wsgibase(environ, responder):
|
||||
BaseAdapter.close_all_instances('rollback')
|
||||
|
||||
http_response = \
|
||||
HTTP(500, rewrite.thread.routes.error_message_ticket % \
|
||||
HTTP(500, rwthread.routes.error_message_ticket % \
|
||||
dict(ticket=ticket),
|
||||
web2py_error='ticket %s' % ticket)
|
||||
|
||||
@@ -606,7 +620,7 @@ def wsgibase(environ, responder):
|
||||
e = RestrictedError('Framework', '', '', locals())
|
||||
ticket = e.log(request) or 'unrecoverable'
|
||||
http_response = \
|
||||
HTTP(500, rewrite.thread.routes.error_message_ticket \
|
||||
HTTP(500, rwthread.routes.error_message_ticket \
|
||||
% dict(ticket=ticket),
|
||||
web2py_error='ticket %s' % ticket)
|
||||
|
||||
@@ -616,7 +630,7 @@ def wsgibase(environ, responder):
|
||||
response.session_file.close()
|
||||
|
||||
session._unlock(response)
|
||||
http_response, new_environ = rewrite.try_rewrite_on_error(
|
||||
http_response, new_environ = try_rewrite_on_error(
|
||||
http_response, request, environ, ticket)
|
||||
if not http_response:
|
||||
return wsgibase(new_environ,responder)
|
||||
@@ -641,7 +655,7 @@ def save_password(password, port):
|
||||
print '*********************************************************'
|
||||
elif password == '<recycle>':
|
||||
# reuse the current password if any
|
||||
if os.path.exists(password_file):
|
||||
if exists(password_file):
|
||||
return
|
||||
else:
|
||||
password = ''
|
||||
@@ -672,9 +686,9 @@ def appfactory(wsgiapp=wsgibase,
|
||||
[, profilerfilename='profiler.log']]])
|
||||
|
||||
"""
|
||||
if profilerfilename and os.path.exists(profilerfilename):
|
||||
if profilerfilename and exists(profilerfilename):
|
||||
os.unlink(profilerfilename)
|
||||
locker = thread.allocate_lock()
|
||||
locker = allocate_lock()
|
||||
|
||||
def app_with_logging(environ, responder):
|
||||
"""
|
||||
@@ -785,7 +799,7 @@ class HttpServer(object):
|
||||
os.chdir(path)
|
||||
[add_path_first(p) for p in (path, abspath('site-packages'), "")]
|
||||
custom_import_install(web2py_path)
|
||||
if os.path.exists("logging.conf"):
|
||||
if exists("logging.conf"):
|
||||
logging.config.fileConfig("logging.conf")
|
||||
|
||||
save_password(password, port)
|
||||
@@ -800,9 +814,9 @@ class HttpServer(object):
|
||||
logger.info('SSL is off')
|
||||
elif not rocket.ssl:
|
||||
logger.warning('Python "ssl" module unavailable. SSL is OFF')
|
||||
elif not os.path.exists(ssl_certificate):
|
||||
elif not exists(ssl_certificate):
|
||||
logger.warning('unable to open SSL certificate. SSL is OFF')
|
||||
elif not os.path.exists(ssl_private_key):
|
||||
elif not exists(ssl_private_key):
|
||||
logger.warning('unable to open SSL private key. SSL is OFF')
|
||||
else:
|
||||
sock_list.extend([ssl_private_key, ssl_certificate])
|
||||
@@ -848,9 +862,3 @@ class HttpServer(object):
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# portalocker.py - Cross-platform (posix/nt) API for flock-style file locking.
|
||||
# portalocker.py
|
||||
# Cross-platform (posix/nt) API for flock-style file locking.
|
||||
# Requires python 1.5.2 or better.
|
||||
|
||||
"""
|
||||
@@ -141,6 +142,17 @@ class LockedFile(object):
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def read_locked(filename):
|
||||
fp = LockedFile(filename, 'r')
|
||||
data = fp.read()
|
||||
fp.close()
|
||||
return data
|
||||
|
||||
def write_locked(filename,data):
|
||||
fp = LockedFile(filename, 'w')
|
||||
data = fp.write(data)
|
||||
fp.close()
|
||||
|
||||
if __name__=='__main__':
|
||||
f = LockedFile('test.txt',mode='wb')
|
||||
f.write('test ok')
|
||||
|
||||
@@ -88,16 +88,19 @@ class TicketStorage(Storage):
|
||||
ticket_id,
|
||||
):
|
||||
if not self.db:
|
||||
ef = self._error_file(request, ticket_id, 'rb', app)
|
||||
try:
|
||||
ef = self._error_file(request, ticket_id, 'rb', app)
|
||||
except IOError:
|
||||
return {}
|
||||
try:
|
||||
return cPickle.load(ef)
|
||||
finally:
|
||||
ef.close()
|
||||
table = self._get_table(self.db, self.tablename, app)
|
||||
rows = self.db(table.ticket_id == ticket_id).select()
|
||||
if rows:
|
||||
return cPickle.loads(rows[0].ticket_data)
|
||||
return None
|
||||
else:
|
||||
table = self._get_table(self.db, self.tablename, app)
|
||||
rows = self.db(table.ticket_id == ticket_id).select()
|
||||
return cPickle.loads(rows[0].ticket_data) if rows else {}
|
||||
|
||||
|
||||
|
||||
class RestrictedError(Exception):
|
||||
@@ -164,10 +167,10 @@ class RestrictedError(Exception):
|
||||
ticket_storage = TicketStorage(db=request.tickets_db)
|
||||
d = ticket_storage.load(request, app, ticket_id)
|
||||
|
||||
self.layer = d['layer']
|
||||
self.code = d['code']
|
||||
self.output = d['output']
|
||||
self.traceback = d['traceback']
|
||||
self.layer = d.get('layer')
|
||||
self.code = d.get('code')
|
||||
self.output = d.get('output')
|
||||
self.traceback = d.get('traceback')
|
||||
self.snapshot = d.get('snapshot')
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -27,9 +27,17 @@ from http import HTTP
|
||||
from fileutils import abspath, read_file
|
||||
from settings import global_settings
|
||||
|
||||
logger = logging.getLogger('web2py.rewrite')
|
||||
isdir = os.path.isdir
|
||||
isfile = os.path.isfile
|
||||
exists = os.path.exists
|
||||
pjoin = os.path.join
|
||||
|
||||
thread = threading.local() # thread-local storage for routing parameters
|
||||
logger = logging.getLogger('web2py.rewrite')
|
||||
thread = threading.local() # thread-local storage for routing params
|
||||
|
||||
regex_at = re.compile(r'(?<!\\)\$[a-zA-Z]\w*')
|
||||
regex_anything = re.compile(r'(?<!\\)\$anything')
|
||||
regex_redirect = re.compile(r'(\d+)->(.*)')
|
||||
|
||||
def _router_default():
|
||||
"return new copy of default base router"
|
||||
@@ -75,7 +83,7 @@ def _params_default(app=None):
|
||||
|
||||
params_apps = dict()
|
||||
params = _params_default(app=None) # regex rewrite parameters
|
||||
thread.routes = params # default to base regex rewrite parameters
|
||||
thread.routes = params # default to base regex rewrite parameters
|
||||
routers = None
|
||||
|
||||
def log_rewrite(string):
|
||||
@@ -97,13 +105,18 @@ def log_rewrite(string):
|
||||
else:
|
||||
logger.debug(string)
|
||||
|
||||
ROUTER_KEYS = set(('default_application', 'applications', 'default_controller', 'controllers',
|
||||
'default_function', 'functions', 'default_language', 'languages',
|
||||
'domain', 'domains', 'root_static', 'path_prefix',
|
||||
'exclusive_domain', 'map_hyphen', 'map_static',
|
||||
'acfe_match', 'file_match', 'args_match'))
|
||||
ROUTER_KEYS = set(
|
||||
('default_application', 'applications',
|
||||
'default_controller', 'controllers',
|
||||
'default_function', 'functions',
|
||||
'default_language', 'languages',
|
||||
'domain', 'domains', 'root_static', 'path_prefix',
|
||||
'exclusive_domain', 'map_hyphen', 'map_static',
|
||||
'acfe_match', 'file_match', 'args_match'))
|
||||
|
||||
ROUTER_BASE_KEYS = set(('applications', 'default_application', 'domains', 'path_prefix'))
|
||||
ROUTER_BASE_KEYS = set(
|
||||
('applications', 'default_application',
|
||||
'domains', 'path_prefix'))
|
||||
|
||||
# The external interface to rewrite consists of:
|
||||
#
|
||||
@@ -223,9 +236,7 @@ def try_redirect_on_error(http_object, request, ticket=None):
|
||||
(redir,status,ticket,
|
||||
urllib.quote_plus(request.env.request_uri),
|
||||
request.url)
|
||||
return HTTP(303,
|
||||
'You are being redirected <a href="%s">here</a>' % url,
|
||||
Location=url)
|
||||
return HTTP(303,'You are being redirected <a href="%s">here</a>' % url,Location=url)
|
||||
return http_object
|
||||
|
||||
|
||||
@@ -258,7 +269,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
|
||||
path = abspath(routes)
|
||||
else:
|
||||
path = abspath('applications', app, routes)
|
||||
if not os.path.exists(path):
|
||||
if not exists(path):
|
||||
return
|
||||
data = read_file(path).replace('\r\n','\n')
|
||||
|
||||
@@ -309,9 +320,11 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
|
||||
# parse the app-specific routes.py if present
|
||||
#
|
||||
all_apps = []
|
||||
for appname in [app for app in os.listdir(abspath('applications')) if not app.startswith('.')]:
|
||||
if os.path.isdir(abspath('applications', appname)) and \
|
||||
os.path.isdir(abspath('applications', appname, 'controllers')):
|
||||
apppath = abspath('applications')
|
||||
for appname in os.listdir(apppath):
|
||||
if not appname.startswith('.') and \
|
||||
isdir(abspath(apppath,appname)) and \
|
||||
isdir(abspath(apppath,appname,'controllers')):
|
||||
all_apps.append(appname)
|
||||
if routers:
|
||||
router = Storage(routers.BASE) # new copy
|
||||
@@ -321,7 +334,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
|
||||
raise SyntaxError, "BASE-only key '%s' in router '%s'" % (key, appname)
|
||||
router.update(routers[appname])
|
||||
routers[appname] = router
|
||||
if os.path.exists(abspath('applications', appname, routes)):
|
||||
if exists(abspath('applications', appname, routes)):
|
||||
load(routes, appname)
|
||||
|
||||
if routers:
|
||||
@@ -336,14 +349,9 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
|
||||
log_rewrite('URL rewrite is on. configuration in %s' % path)
|
||||
|
||||
|
||||
regex_at = re.compile(r'(?<!\\)\$[a-zA-Z]\w*')
|
||||
regex_anything = re.compile(r'(?<!\\)\$anything')
|
||||
regex_redirect = re.compile(r'(\d+)->(.*)')
|
||||
|
||||
def compile_regex(k, v):
|
||||
"""
|
||||
Preprocess and compile the regular expressions in routes_app/in/out
|
||||
|
||||
The resulting regex will match a pattern of the form:
|
||||
|
||||
[remote address]:[protocol]://[host]:[method] [path]
|
||||
@@ -380,8 +388,9 @@ def compile_regex(k, v):
|
||||
def load_routers(all_apps):
|
||||
"load-time post-processing of routers"
|
||||
|
||||
for app in routers.keys():
|
||||
# initialize apps with routers that aren't present, on behalf of unit tests
|
||||
for app in routers:
|
||||
# initialize apps with routers that aren't present,
|
||||
# on behalf of unit tests
|
||||
if app not in all_apps:
|
||||
all_apps.append(app)
|
||||
router = Storage(routers.BASE) # new copy
|
||||
@@ -420,10 +429,10 @@ def load_routers(all_apps):
|
||||
routers.BASE.domains[router.domain] = app
|
||||
if isinstance(router.controllers, str) and router.controllers == 'DEFAULT':
|
||||
router.controllers = set()
|
||||
if os.path.isdir(abspath('applications', app)):
|
||||
if isdir(abspath('applications', app)):
|
||||
cpath = abspath('applications', app, 'controllers')
|
||||
for cname in os.listdir(cpath):
|
||||
if os.path.isfile(abspath(cpath, cname)) and cname.endswith('.py'):
|
||||
if isfile(abspath(cpath, cname)) and cname.endswith('.py'):
|
||||
router.controllers.add(cname[:-3])
|
||||
if router.controllers:
|
||||
router.controllers.add('static')
|
||||
@@ -458,16 +467,20 @@ def load_routers(all_apps):
|
||||
#
|
||||
domains = dict()
|
||||
if routers.BASE.domains:
|
||||
for (domain, app) in [(d.strip(':'), a.strip('/')) for (d, a) in routers.BASE.domains.items()]:
|
||||
port = None
|
||||
for (d, a) in routers.BASE.domains.iteritems():
|
||||
(domain, app) = (d.strip(':'), a.strip('/'))
|
||||
if ':' in domain:
|
||||
(domain, port) = domain.split(':')
|
||||
ctlr = None
|
||||
fcn = None
|
||||
else:
|
||||
port = None
|
||||
if '/' in app:
|
||||
(app, ctlr) = app.split('/', 1)
|
||||
else:
|
||||
ctlr = None
|
||||
if ctlr and '/' in ctlr:
|
||||
(ctlr, fcn) = ctlr.split('/')
|
||||
else:
|
||||
fcn = None
|
||||
if app not in all_apps and app not in routers:
|
||||
raise SyntaxError, "unknown app '%s' in domains" % app
|
||||
domains[(domain, port)] = (app, ctlr, fcn)
|
||||
@@ -514,7 +527,8 @@ def regex_filter_in(e):
|
||||
query = e.get('QUERY_STRING', None)
|
||||
e['WEB2PY_ORIGINAL_URI'] = e['PATH_INFO'] + (query and ('?' + query) or '')
|
||||
if thread.routes.routes_in:
|
||||
path = regex_uri(e, thread.routes.routes_in, "routes_in", e['PATH_INFO'])
|
||||
path = regex_uri(e, thread.routes.routes_in,
|
||||
"routes_in", e['PATH_INFO'])
|
||||
rmatch = regex_redirect.match(path)
|
||||
if rmatch:
|
||||
raise HTTP(int(rmatch.group(1)),location=rmatch.group(2))
|
||||
@@ -581,6 +595,9 @@ regex_args = re.compile(r'''
|
||||
/?$) # trailing slash
|
||||
''', re.X)
|
||||
|
||||
def sluggify(key):
|
||||
return key.lower().replace('.','_')
|
||||
|
||||
def regex_url_in(request, environ):
|
||||
"rewrite and parse incoming URL"
|
||||
|
||||
@@ -594,9 +611,8 @@ def regex_url_in(request, environ):
|
||||
|
||||
if thread.routes.routes_in:
|
||||
environ = regex_filter_in(environ)
|
||||
|
||||
for (key, value) in environ.items():
|
||||
request.env[key.lower().replace('.', '_')] = value
|
||||
|
||||
request.env.update((sluggify(k),v) for k,v in environ.iteritems())
|
||||
|
||||
path = request.env.path_info.replace('\\', '/')
|
||||
|
||||
@@ -606,7 +622,7 @@ def regex_url_in(request, environ):
|
||||
|
||||
match = regex_static.match(regex_space.sub('_', path))
|
||||
if match and match.group('x'):
|
||||
static_file = os.path.join(request.env.applications_parent,
|
||||
static_file = pjoin(request.env.applications_parent,
|
||||
'applications', match.group('b'),
|
||||
'static', match.group('x'))
|
||||
return (static_file, environ)
|
||||
@@ -908,7 +924,7 @@ class MapUrlIn(object):
|
||||
'''
|
||||
if len(self.args) == 1 and self.arg0 in self.router.root_static:
|
||||
self.controller = self.request.controller = 'static'
|
||||
root_static_file = os.path.join(self.request.env.applications_parent,
|
||||
root_static_file = pjoin(self.request.env.applications_parent,
|
||||
'applications', self.application,
|
||||
self.controller, self.arg0)
|
||||
log_rewrite("route: root static=%s" % root_static_file)
|
||||
@@ -962,7 +978,8 @@ class MapUrlIn(object):
|
||||
bad_static = bad_static or name in ('', '.', '..') or not self.router._file_match.match(name)
|
||||
if bad_static:
|
||||
log_rewrite('bad static path=%s' % file)
|
||||
raise HTTP(400, thread.routes.error_message % 'invalid request',
|
||||
raise HTTP(400,
|
||||
thread.routes.error_message % 'invalid request',
|
||||
web2py_error='invalid static file')
|
||||
#
|
||||
# support language-specific static subdirectories,
|
||||
@@ -970,13 +987,13 @@ class MapUrlIn(object):
|
||||
# if language-specific file doesn't exist, try same file in static
|
||||
#
|
||||
if self.language:
|
||||
static_file = os.path.join(self.request.env.applications_parent,
|
||||
'applications', self.application,
|
||||
'static', self.language, file)
|
||||
if not self.language or not os.path.isfile(static_file):
|
||||
static_file = os.path.join(self.request.env.applications_parent,
|
||||
'applications', self.application,
|
||||
'static', file)
|
||||
static_file = pjoin(self.request.env.applications_parent,
|
||||
'applications', self.application,
|
||||
'static', self.language, file)
|
||||
if not self.language or not isfile(static_file):
|
||||
static_file = pjoin(self.request.env.applications_parent,
|
||||
'applications', self.application,
|
||||
'static', file)
|
||||
log_rewrite("route: static=%s" % static_file)
|
||||
return static_file
|
||||
|
||||
@@ -1040,12 +1057,14 @@ class MapUrlIn(object):
|
||||
uri += '.' + self.extension
|
||||
if self.language:
|
||||
uri = '/%s%s' % (self.language, uri)
|
||||
uri = '/%s%s' % (app, uri)
|
||||
uri += self.args and urllib.quote('/' + '/'.join([str(x) for x in self.args])) or ''
|
||||
uri += (self.query and ('?' + self.query) or '')
|
||||
uri = '/%s%s%s%s' % (
|
||||
app,
|
||||
uri,
|
||||
urllib.quote('/'+'/'.join(str(x) for x in self.args)) if self.args else '',
|
||||
('?' + self.query) if self.query else '')
|
||||
self.env['REQUEST_URI'] = uri
|
||||
for (key, value) in self.env.items():
|
||||
self.request.env[key.lower().replace('.', '_')] = value
|
||||
self.request.env.update(
|
||||
(sluggify(k),v) for k,v in self.env.iteritems())
|
||||
|
||||
@property
|
||||
def arg0(self):
|
||||
|
||||
@@ -1407,7 +1407,7 @@ class Worker(Thread):
|
||||
raise BadRequest
|
||||
|
||||
req = match.groupdict()
|
||||
for k,v in req.items():
|
||||
for k,v in req.iteritems():
|
||||
if not v:
|
||||
req[k] = ""
|
||||
if k == 'path':
|
||||
@@ -1694,7 +1694,8 @@ class FileSystemWorker(Worker):
|
||||
|
||||
try:
|
||||
# Get our file path
|
||||
headers = dict([(str(k.lower()), v) for k, v in self.read_headers(sock_file).items()])
|
||||
reader = self.read_headers(sock_file)
|
||||
headers = dict((k.lower(),v) for k,v in reader.iteritems())
|
||||
rpath = request.get('path', '').lstrip('/')
|
||||
filepath = os.path.join(self.root, rpath)
|
||||
filepath = os.path.abspath(filepath)
|
||||
|
||||
@@ -18,7 +18,8 @@ if not hasattr(os, 'mkdir'):
|
||||
if global_settings.db_sessions is not True:
|
||||
global_settings.db_sessions = set()
|
||||
|
||||
global_settings.gluon_parent = os.environ.get('web2py_path', os.getcwd())
|
||||
global_settings.gluon_parent = \
|
||||
os.environ.get('web2py_path', os.getcwd())
|
||||
|
||||
global_settings.applications_parent = global_settings.gluon_parent
|
||||
|
||||
@@ -26,12 +27,14 @@ global_settings.app_folders = set()
|
||||
|
||||
global_settings.debugging = False
|
||||
|
||||
global_settings.is_pypy = hasattr(platform,'python_implementation') and \
|
||||
platform.python_implementation() == 'PyPy'
|
||||
global_settings.is_pypy = \
|
||||
hasattr(platform,'python_implementation') and \
|
||||
platform.python_implementation() == 'PyPy'
|
||||
|
||||
global_settings.is_jython = 'java' in sys.platform.lower() or \
|
||||
hasattr(sys, 'JYTHON_JAR') or \
|
||||
str(sys.copyright).find('Jython') > 0
|
||||
global_settings.is_jython = \
|
||||
'java' in sys.platform.lower() or \
|
||||
hasattr(sys, 'JYTHON_JAR') or \
|
||||
str(sys.copyright).find('Jython') > 0
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ import re
|
||||
import cStringIO
|
||||
from gluon import current, redirect, A, URL, DIV, H3, UL, LI, SPAN, INPUT
|
||||
import inspect
|
||||
import settings
|
||||
is_gae = settings.global_settings.web2py_runtime_gae
|
||||
|
||||
table_field = re.compile('[\w_]+\.[\w_]+')
|
||||
widget_class = re.compile('^\w*')
|
||||
@@ -581,7 +583,7 @@ class AutocompleteWidget(object):
|
||||
def callback(self):
|
||||
if self.keyword in self.request.vars:
|
||||
field = self.fields[0]
|
||||
if settings.global_settings.web2py_runtime_gae:
|
||||
if is_gae:
|
||||
rows = self.db(field.__ge__(self.request.vars[self.keyword])&field.__lt__(self.request.vars[self.keyword]+ u'\ufffd')).select(orderby=self.orderby,limitby=self.limitby,*self.fields)
|
||||
else:
|
||||
rows = self.db(field.like(self.request.vars[self.keyword]+'%')).select(orderby=self.orderby,limitby=self.limitby,distinct=self.distinct,*self.fields)
|
||||
@@ -1834,7 +1836,7 @@ class SQLFORM(FORM):
|
||||
else:
|
||||
rows = dbset.select(left=left,orderby=orderby,*columns)
|
||||
|
||||
if exportManager.has_key(export_type):
|
||||
if export_type in exportManager:
|
||||
value = exportManager[export_type]
|
||||
clazz = value[0] if hasattr(value, '__getitem__') else value
|
||||
oExp = clazz(rows)
|
||||
|
||||
@@ -15,10 +15,10 @@ Contributors:
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import cgi
|
||||
import cStringIO
|
||||
import logging
|
||||
from re import compile, sub, escape, DOTALL
|
||||
try:
|
||||
# have web2py
|
||||
from restricted import RestrictedError
|
||||
@@ -57,6 +57,18 @@ class SuperNode(Node):
|
||||
def __repr__(self):
|
||||
return "%s->%s" % (self.name, self.value)
|
||||
|
||||
def output_aux(node,blocks):
|
||||
# If we have a block level
|
||||
# If we can override this block.
|
||||
# Override block from vars.
|
||||
# Else we take the default
|
||||
# Else its just a string
|
||||
return (blocks[node.name].output(blocks)
|
||||
if node.name in blocks else
|
||||
node.output(blocks)) \
|
||||
if isinstance(node, BlockNode) \
|
||||
else str(node)
|
||||
|
||||
class BlockNode(Node):
|
||||
"""
|
||||
Block Container.
|
||||
@@ -81,8 +93,7 @@ class BlockNode(Node):
|
||||
|
||||
def __repr__(self):
|
||||
lines = ['%sblock %s%s' % (self.left,self.name,self.right)]
|
||||
for node in self.nodes:
|
||||
lines.append(str(node))
|
||||
lines += [str(node) for node in self.nodes]
|
||||
lines.append('%send%s' % (self.left, self.right))
|
||||
return ''.join(lines)
|
||||
|
||||
@@ -90,11 +101,8 @@ class BlockNode(Node):
|
||||
"""
|
||||
Get this BlockNodes content, not including child Nodes
|
||||
"""
|
||||
lines = []
|
||||
for node in self.nodes:
|
||||
if not isinstance(node, BlockNode):
|
||||
lines.append(str(node))
|
||||
return ''.join(lines)
|
||||
return ''.join(str(node) for node in self.nodes \
|
||||
if not isinstance(node, BlockNode))
|
||||
|
||||
def append(self, node):
|
||||
"""
|
||||
@@ -122,30 +130,14 @@ class BlockNode(Node):
|
||||
else:
|
||||
raise TypeError("Invalid type; must be instance of ``BlockNode``. %s" % other)
|
||||
|
||||
|
||||
def output(self, blocks):
|
||||
"""
|
||||
Merges all nodes into a single string.
|
||||
|
||||
blocks -- Dictionary of blocks that are extending
|
||||
from this template.
|
||||
"""
|
||||
lines = []
|
||||
# Get each of our nodes
|
||||
for node in self.nodes:
|
||||
# If we have a block level node.
|
||||
if isinstance(node, BlockNode):
|
||||
# If we can override this block.
|
||||
if node.name in blocks:
|
||||
# Override block from vars.
|
||||
lines.append(blocks[node.name].output(blocks))
|
||||
# Else we take the default
|
||||
else:
|
||||
lines.append(node.output(blocks))
|
||||
# Else its just a string
|
||||
else:
|
||||
lines.append(str(node))
|
||||
# Now combine all of our lines together.
|
||||
return ''.join(lines)
|
||||
return ''.join(output_aux(node,blocks) for node in self.nodes)
|
||||
|
||||
class Content(BlockNode):
|
||||
"""
|
||||
@@ -165,29 +157,13 @@ class Content(BlockNode):
|
||||
self.pre_extend = pre_extend
|
||||
|
||||
def __str__(self):
|
||||
lines = []
|
||||
# For each of our nodes
|
||||
for node in self.nodes:
|
||||
# If it is a block node.
|
||||
if isinstance(node, BlockNode):
|
||||
# And the node has a name that corresponds with a block in us
|
||||
if node.name in self.blocks:
|
||||
# Use the overriding output.
|
||||
lines.append(self.blocks[node.name].output(self.blocks))
|
||||
else:
|
||||
# Otherwise we just use the nodes output.
|
||||
lines.append(node.output(self.blocks))
|
||||
else:
|
||||
# It is just a string, so include it.
|
||||
lines.append(str(node))
|
||||
# Merge our list together.
|
||||
return ''.join(lines)
|
||||
return ''.join(output_aux(node,self.blocks) for node in self.nodes)
|
||||
|
||||
def _insert(self, other, index = 0):
|
||||
"""
|
||||
Inserts object at index.
|
||||
"""
|
||||
if isinstance(other, str) or isinstance(other, Node):
|
||||
if isinstance(other, (str, Node)):
|
||||
self.nodes.insert(index, other)
|
||||
else:
|
||||
raise TypeError("Invalid type, must be instance of ``str`` or ``Node``.")
|
||||
@@ -201,8 +177,7 @@ class Content(BlockNode):
|
||||
if isinstance(other, (list, tuple)):
|
||||
# Must reverse so the order stays the same.
|
||||
other.reverse()
|
||||
for item in other:
|
||||
self._insert(item, index)
|
||||
(self._insert(item, index) for item in other)
|
||||
else:
|
||||
self._insert(other, index)
|
||||
|
||||
@@ -210,7 +185,7 @@ class Content(BlockNode):
|
||||
"""
|
||||
Adds a node to list. If it is a BlockNode then we assign a block for it.
|
||||
"""
|
||||
if isinstance(node, str) or isinstance(node, Node):
|
||||
if isinstance(node, (str, Node)):
|
||||
self.nodes.append(node)
|
||||
if isinstance(node, BlockNode):
|
||||
self.blocks[node.name] = node
|
||||
@@ -233,18 +208,18 @@ class Content(BlockNode):
|
||||
class TemplateParser(object):
|
||||
|
||||
default_delimiters = ('{{','}}')
|
||||
r_tag = re.compile(r'(\{\{.*?\}\})', re.DOTALL)
|
||||
r_tag = compile(r'(\{\{.*?\}\})', DOTALL)
|
||||
|
||||
r_multiline = re.compile(r'(""".*?""")|(\'\'\'.*?\'\'\')', re.DOTALL)
|
||||
r_multiline = compile(r'(""".*?""")|(\'\'\'.*?\'\'\')', DOTALL)
|
||||
|
||||
# These are used for re-indentation.
|
||||
# Indent + 1
|
||||
re_block = re.compile('^(elif |else:|except:|except |finally:).*$',
|
||||
re.DOTALL)
|
||||
re_block = compile('^(elif |else:|except:|except |finally:).*$',DOTALL)
|
||||
|
||||
# Indent - 1
|
||||
re_unblock = re.compile('^(return|continue|break|raise)( .*)?$', re.DOTALL)
|
||||
re_unblock = compile('^(return|continue|break|raise)( .*)?$', DOTALL)
|
||||
# Indent - 1
|
||||
re_pass = re.compile('^pass( .*)?$', re.DOTALL)
|
||||
re_pass = compile('^pass( .*)?$', DOTALL)
|
||||
|
||||
def __init__(self, text,
|
||||
name = "ParserContainer",
|
||||
@@ -291,13 +266,16 @@ class TemplateParser(object):
|
||||
# allow optional alternative delimiters
|
||||
self.delimiters = delimiters
|
||||
if delimiters != self.default_delimiters:
|
||||
escaped_delimiters = (re.escape(delimiters[0]),re.escape(delimiters[1]))
|
||||
self.r_tag = re.compile(r'(%s.*?%s)' % escaped_delimiters, re.DOTALL)
|
||||
elif context.has_key('response') and hasattr(context['response'],'delimiters'):
|
||||
escaped_delimiters = (escape(delimiters[0]),
|
||||
escape(delimiters[1]))
|
||||
self.r_tag = compile(r'(%s.*?%s)' % escaped_delimiters, DOTALL)
|
||||
elif hasattr(context.get('response',None),'delimiters'):
|
||||
if context['response'].delimiters != self.default_delimiters:
|
||||
escaped_delimiters = (re.escape(context['response'].delimiters[0]),
|
||||
re.escape(context['response'].delimiters[1]))
|
||||
self.r_tag = re.compile(r'(%s.*?%s)' % escaped_delimiters,re.DOTALL)
|
||||
escaped_delimiters = (
|
||||
escape(context['response'].delimiters[0]),
|
||||
escape(context['response'].delimiters[1]))
|
||||
self.r_tag = compile(r'(%s.*?%s)' % escaped_delimiters,
|
||||
DOTALL)
|
||||
|
||||
# Create a root level Content that everything will go into.
|
||||
self.content = Content(name=name)
|
||||
@@ -524,17 +502,19 @@ class TemplateParser(object):
|
||||
# the parent nodes.
|
||||
self.content.nodes = []
|
||||
|
||||
t_content = t.content
|
||||
|
||||
# Set our include, unique by filename
|
||||
t.content.blocks['__include__' + filename] = buf
|
||||
t_content.blocks['__include__' + filename] = buf
|
||||
|
||||
# Make sure our pre_extended nodes go first
|
||||
t.content.insert(pre)
|
||||
t_content.insert(pre)
|
||||
|
||||
# Then we extend our blocks
|
||||
t.content.extend(self.content)
|
||||
t_content.extend(self.content)
|
||||
|
||||
# Work off the parent node.
|
||||
self.content = t.content
|
||||
self.content = t_content
|
||||
|
||||
def parse(self, text):
|
||||
|
||||
@@ -553,69 +533,20 @@ class TemplateParser(object):
|
||||
ij = self.r_tag.split(text)
|
||||
# j = current index
|
||||
# i = current item
|
||||
stack = self.stack
|
||||
for j in range(len(ij)):
|
||||
i = ij[j]
|
||||
|
||||
if i:
|
||||
if len(self.stack) == 0:
|
||||
if not stack:
|
||||
self._raise_error('The "end" tag is unmatched, please check if you have a starting "block" tag')
|
||||
|
||||
# Our current element in the stack.
|
||||
top = self.stack[-1]
|
||||
top = stack[-1]
|
||||
|
||||
if in_tag:
|
||||
line = i
|
||||
|
||||
# If we are missing any strings!!!!
|
||||
# This usually happens with the following example
|
||||
# template code
|
||||
#
|
||||
# {{a = '}}'}}
|
||||
# or
|
||||
# {{a = '}}blahblah{{'}}
|
||||
#
|
||||
# This will fix these
|
||||
# This is commented out because the current template
|
||||
# system has this same limitation. Since this has a
|
||||
# performance hit on larger templates, I do not recommend
|
||||
# using this code on production systems. This is still here
|
||||
# for "i told you it *can* be fixed" purposes.
|
||||
#
|
||||
#
|
||||
# if line.count("'") % 2 != 0 or line.count('"') % 2 != 0:
|
||||
#
|
||||
# # Look ahead
|
||||
# la = 1
|
||||
# nextline = ij[j+la]
|
||||
#
|
||||
# # As long as we have not found our ending
|
||||
# # brackets keep going
|
||||
# while '}}' not in nextline:
|
||||
# la += 1
|
||||
# nextline += ij[j+la]
|
||||
# # clear this line, so we
|
||||
# # don't attempt to parse it
|
||||
# # this is why there is an "if i"
|
||||
# # around line 530
|
||||
# ij[j+la] = ''
|
||||
#
|
||||
# # retrieve our index.
|
||||
# index = nextline.index('}}')
|
||||
#
|
||||
# # Everything before the new brackets
|
||||
# before = nextline[:index+2]
|
||||
#
|
||||
# # Everything after
|
||||
# after = nextline[index+2:]
|
||||
#
|
||||
# # Make the next line everything after
|
||||
# # so it parses correctly, this *should* be
|
||||
# # all html
|
||||
# ij[j+1] = after
|
||||
#
|
||||
# # Add everything before to the current line
|
||||
# line += before
|
||||
|
||||
# Get rid of '{{' and '}}'
|
||||
line = line[2:-2].strip()
|
||||
|
||||
@@ -633,9 +564,9 @@ class TemplateParser(object):
|
||||
# Perform block comment escaping.
|
||||
# This performs escaping ON anything
|
||||
# in between """ and """
|
||||
line = re.sub(TemplateParser.r_multiline,
|
||||
remove_newline,
|
||||
line)
|
||||
line = sub(TemplateParser.r_multiline,
|
||||
remove_newline,
|
||||
line)
|
||||
|
||||
if line.startswith('='):
|
||||
# IE: {{=response.title}}
|
||||
@@ -672,7 +603,7 @@ class TemplateParser(object):
|
||||
self.lexers[name](parser = self,
|
||||
value = value,
|
||||
top = top,
|
||||
stack = self.stack,)
|
||||
stack = stack)
|
||||
|
||||
elif name == '=':
|
||||
# So we have a variable to insert into
|
||||
@@ -693,7 +624,7 @@ class TemplateParser(object):
|
||||
# so anything after this gets added
|
||||
# to this node. This allows us to
|
||||
# "nest" nodes.
|
||||
self.stack.append(node)
|
||||
stack.append(node)
|
||||
|
||||
elif name == 'end' and not value.startswith('='):
|
||||
# We are done with this node.
|
||||
@@ -702,7 +633,7 @@ class TemplateParser(object):
|
||||
self.blocks[top.name] = top
|
||||
|
||||
# Pop it.
|
||||
self.stack.pop()
|
||||
stack.pop()
|
||||
|
||||
elif name == 'super' and not value.startswith('='):
|
||||
# Get our correct target name
|
||||
@@ -757,17 +688,17 @@ class TemplateParser(object):
|
||||
# So we can properly put a response.write() in place.
|
||||
continuation = False
|
||||
len_parsed = 0
|
||||
for k in range(len(tokens)):
|
||||
for k, token in enumerate(tokens):
|
||||
|
||||
tokens[k] = tokens[k].strip()
|
||||
len_parsed += len(tokens[k])
|
||||
token = tokens[k] = token.strip()
|
||||
len_parsed += len(token)
|
||||
|
||||
if tokens[k].startswith('='):
|
||||
if tokens[k].endswith('\\'):
|
||||
if token.startswith('='):
|
||||
if token.endswith('\\'):
|
||||
continuation = True
|
||||
tokens[k] = "\n%s(%s" % (self.writer, tokens[k][1:].strip())
|
||||
tokens[k] = "\n%s(%s" % (self.writer, token[1:].strip())
|
||||
else:
|
||||
tokens[k] = "\n%s(%s)" % (self.writer, tokens[k][1:].strip())
|
||||
tokens[k] = "\n%s(%s)" % (self.writer, token[1:].strip())
|
||||
elif continuation:
|
||||
tokens[k] += ')'
|
||||
continuation = False
|
||||
@@ -912,7 +843,7 @@ def render(content = "hello world",
|
||||
|
||||
# Add it to the context so we can use it.
|
||||
if not 'NOESCAPE' in context:
|
||||
context['NOESCAPE'] = XML
|
||||
context['NOESCAPE'] = NOESCAPE
|
||||
|
||||
# save current response class
|
||||
if context and 'response' in context:
|
||||
|
||||
@@ -129,6 +129,26 @@ class TestFields(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(db.t.insert(a=t0), 1)
|
||||
self.assertEqual(db().select(db.t.a)[0].a, t0)
|
||||
|
||||
## Row APIs
|
||||
row = db().select(db.t.a)[0]
|
||||
self.assertEqual(db.t[1].a,t0)
|
||||
self.assertEqual(db.t['a'],db.t.a)
|
||||
self.assertEqual(db.t(1).a,t0)
|
||||
self.assertTrue(db.t(1,a=None)==None)
|
||||
self.assertFalse(db.t(1,a=t0)==None)
|
||||
self.assertEqual(row.a,t0)
|
||||
self.assertEqual(row['a'],t0)
|
||||
self.assertEqual(row['t.a'],t0)
|
||||
self.assertEqual(row('t.a'),t0)
|
||||
|
||||
## Lazy and Virtual fields
|
||||
db.t.b = Field.Virtual(lambda row: row.t.a)
|
||||
db.t.c = Field.Lazy(lambda row: row.t.a)
|
||||
row = db().select(db.t.a)[0]
|
||||
self.assertEqual(row.b,t0)
|
||||
self.assertEqual(row.c(),t0)
|
||||
|
||||
db.t.drop()
|
||||
db.define_table('t', Field('a', 'time', default='11:30'))
|
||||
t0 = datetime.time(10, 30, 55)
|
||||
@@ -369,6 +389,17 @@ class TestJoin(unittest.TestCase):
|
||||
db.t1.drop()
|
||||
db.t2.drop()
|
||||
|
||||
db.define_table('person',Field('name'))
|
||||
id = db.person.insert(name="max")
|
||||
self.assertEqual(id.name,'max')
|
||||
db.define_table('dog',Field('name'),Field('owner','reference person'))
|
||||
db.dog.insert(name='skipper',owner=1)
|
||||
row = db(db.person.id==db.dog.owner).select().first()
|
||||
self.assertEqual(row[db.person.name],'max')
|
||||
self.assertEqual(row['person.name'],'max')
|
||||
db.dog.drop()
|
||||
self.assertEqual(len(db.person._referenced_by),0)
|
||||
db.person.drop()
|
||||
|
||||
class TestMinMaxSum(unittest.TestCase):
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import languages
|
||||
import tempfile
|
||||
import threading
|
||||
import logging
|
||||
from storage import Storage
|
||||
|
||||
try:
|
||||
import multiprocessing
|
||||
@@ -53,6 +54,39 @@ try:
|
||||
for result in results:
|
||||
self.assertTrue(result)
|
||||
|
||||
|
||||
class TestTranslations(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.request = Storage()
|
||||
self.request.folder = 'applications/welcome'
|
||||
self.request.env = Storage()
|
||||
self.request.env.http_accept_language = 'en'
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_plain(self):
|
||||
T = languages.translator(self.request)
|
||||
self.assertEqual(str(T('Hello World')),
|
||||
'Hello World')
|
||||
self.assertEqual(str(T('Hello World## comment')),
|
||||
'Hello World')
|
||||
self.assertEqual(str(T('%s %%{shop}', 1)),
|
||||
'1 shop')
|
||||
self.assertEqual(str(T('%s %%{shop}', 2)),
|
||||
'2 shops')
|
||||
self.assertEqual(str(T('%s %%{shop[0]}', 1)),
|
||||
'1 shop')
|
||||
self.assertEqual(str(T('%s %%{shop[0]}', 2)),
|
||||
'2 shops')
|
||||
self.assertEqual(str(T.M('**Hello World**')),
|
||||
'<strong>Hello World</strong>')
|
||||
T.force('it')
|
||||
self.assertEqual(str(T('Hello World')),
|
||||
'Salve Mondo')
|
||||
|
||||
except ImportError:
|
||||
logging.warning("Skipped test case, no multiprocessing module.")
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ from utils import web2py_uuid
|
||||
from fileutils import read_file, check_credentials
|
||||
from gluon import *
|
||||
from gluon.contrib.autolinks import expand_one
|
||||
from gluon.dal import Row
|
||||
|
||||
import serializers
|
||||
|
||||
@@ -798,6 +799,141 @@ def addrow(form, a, b, c, style, _id, position=-1):
|
||||
|
||||
|
||||
class Auth(object):
|
||||
|
||||
default_settings = {
|
||||
'hideerror': False,
|
||||
'password_min_length': 4,
|
||||
'cas_maps': None,
|
||||
'reset_password_requires_verification': False,
|
||||
'registration_requires_verification': False,
|
||||
'registration_requires_approval': False,
|
||||
'login_after_registration': False,
|
||||
'login_after_password_change': True,
|
||||
'alternate_requires_registration': False,
|
||||
'create_user_groups': "user_%(id)s",
|
||||
'everybody_group_id': None,
|
||||
'login_captcha': None,
|
||||
'register_captcha': None,
|
||||
'retrieve_username_captcha': None,
|
||||
'retrieve_password_captcha': None,
|
||||
'captcha': None,
|
||||
'expiration': 3600, # one hour
|
||||
'long_expiration': 3600*30*24, # one month
|
||||
'remember_me_form': True,
|
||||
'allow_basic_login': False,
|
||||
'allow_basic_login_only': False,
|
||||
'on_failed_authentication': lambda x: redirect(x),
|
||||
'formstyle': 'table3cols',
|
||||
'label_separator': ': ',
|
||||
'password_field': 'password',
|
||||
'table_user_name': 'auth_user',
|
||||
'table_group_name': 'auth_group',
|
||||
'table_membership_name': 'auth_membership',
|
||||
'table_permission_name': 'auth_permission',
|
||||
'table_event_name': 'auth_event',
|
||||
'table_cas_name': 'auth_cas',
|
||||
'table_user': None,
|
||||
'table_group': None,
|
||||
'table_membership': None,
|
||||
'table_permission': None,
|
||||
'table_event': None,
|
||||
'table_cas': None,
|
||||
'showid': False,
|
||||
'login_email_validate': True,
|
||||
'login_userfield': None,
|
||||
'logout_onlogout': None,
|
||||
'register_fields': None,
|
||||
'register_verify_password': True,
|
||||
'profile_fields': None,
|
||||
'email_case_sensitive': True,
|
||||
'username_case_sensitive': True,
|
||||
}
|
||||
# ## these are messages that can be customized
|
||||
default_messages = {
|
||||
'login_button': 'Login',
|
||||
'register_button': 'Register',
|
||||
'password_reset_button': 'Request reset password',
|
||||
'password_change_button': 'Change password',
|
||||
'profile_save_button': 'Save profile',
|
||||
'submit_button': 'Submit',
|
||||
'verify_password': 'Verify Password',
|
||||
'delete_label': 'Check to delete',
|
||||
'function_disabled': 'Function disabled',
|
||||
'access_denied': 'Insufficient privileges',
|
||||
'registration_verifying': 'Registration needs verification',
|
||||
'registration_pending': 'Registration is pending approval',
|
||||
'login_disabled': 'Login disabled by administrator',
|
||||
'logged_in': 'Logged in',
|
||||
'email_sent': 'Email sent',
|
||||
'unable_to_send_email': 'Unable to send email',
|
||||
'email_verified': 'Email verified',
|
||||
'logged_out': 'Logged out',
|
||||
'registration_successful': 'Registration successful',
|
||||
'invalid_email': 'Invalid email',
|
||||
'unable_send_email': 'Unable to send email',
|
||||
'invalid_login': 'Invalid login',
|
||||
'invalid_user': 'Invalid user',
|
||||
'invalid_password': 'Invalid password',
|
||||
'is_empty': "Cannot be empty",
|
||||
'mismatched_password': "Password fields don't match",
|
||||
'verify_email': 'Click on the link %(link)s to verify your email',
|
||||
'verify_email_subject': 'Email verification',
|
||||
'username_sent': 'Your username was emailed to you',
|
||||
'new_password_sent': 'A new password was emailed to you',
|
||||
'password_changed': 'Password changed',
|
||||
'retrieve_username': 'Your username is: %(username)s',
|
||||
'retrieve_username_subject': 'Username retrieve',
|
||||
'retrieve_password': 'Your password is: %(password)s',
|
||||
'retrieve_password_subject': 'Password retrieve',
|
||||
'reset_password': \
|
||||
'Click on the link %(link)s to reset your password',
|
||||
'reset_password_subject': 'Password reset',
|
||||
'invalid_reset_password': 'Invalid reset password',
|
||||
'profile_updated': 'Profile updated',
|
||||
'new_password': 'New password',
|
||||
'old_password': 'Old password',
|
||||
'group_description': 'Group uniquely assigned to user %(id)s',
|
||||
'register_log': 'User %(id)s Registered',
|
||||
'login_log': 'User %(id)s Logged-in',
|
||||
'login_failed_log': None,
|
||||
'logout_log': 'User %(id)s Logged-out',
|
||||
'profile_log': 'User %(id)s Profile updated',
|
||||
'verify_email_log': 'User %(id)s Verification email sent',
|
||||
'retrieve_username_log': 'User %(id)s Username retrieved',
|
||||
'retrieve_password_log': 'User %(id)s Password retrieved',
|
||||
'reset_password_log': 'User %(id)s Password reset',
|
||||
'change_password_log': 'User %(id)s Password changed',
|
||||
'add_group_log': 'Group %(group_id)s created',
|
||||
'del_group_log': 'Group %(group_id)s deleted',
|
||||
'add_membership_log': None,
|
||||
'del_membership_log': None,
|
||||
'has_membership_log': None,
|
||||
'add_permission_log': None,
|
||||
'del_permission_log': None,
|
||||
'has_permission_log': None,
|
||||
'impersonate_log': 'User %(id)s is impersonating %(other_id)s',
|
||||
'label_first_name': 'First name',
|
||||
'label_last_name': 'Last name',
|
||||
'label_username': 'Username',
|
||||
'label_email': 'E-mail',
|
||||
'label_password': 'Password',
|
||||
'label_registration_key': 'Registration key',
|
||||
'label_reset_password_key': 'Reset Password key',
|
||||
'label_registration_id': 'Registration identifier',
|
||||
'label_role': 'Role',
|
||||
'label_description': 'Description',
|
||||
'label_user_id': 'User ID',
|
||||
'label_group_id': 'Group ID',
|
||||
'label_name': 'Name',
|
||||
'label_table_name': 'Object or table name',
|
||||
'label_record_id': 'Record ID',
|
||||
'label_time_stamp': 'Timestamp',
|
||||
'label_client_ip': 'Client IP',
|
||||
'label_origin': 'Origin',
|
||||
'label_remember_me': "Remember me (for 30 days)",
|
||||
'verify_password_comment': 'please input your password again',
|
||||
}
|
||||
|
||||
"""
|
||||
Class for authentication, authorization, role based access control.
|
||||
|
||||
@@ -894,10 +1030,11 @@ class Auth(object):
|
||||
open(filename,'w').write(key)
|
||||
return key
|
||||
|
||||
def url(self, f=None, args=None, vars=None):
|
||||
def url(self, f=None, args=None, vars=None, scheme=False):
|
||||
if args is None: args=[]
|
||||
if vars is None: vars={}
|
||||
return URL(c=self.settings.controller, f=f, args=args, vars=vars)
|
||||
return URL(c=self.settings.controller,
|
||||
f=f, args=args, vars=vars,scheme=scheme)
|
||||
|
||||
def here(self):
|
||||
return URL(args=current.request.args,vars=current.request.vars)
|
||||
@@ -933,221 +1070,67 @@ class Auth(object):
|
||||
else:
|
||||
self.user = None
|
||||
session.auth = None
|
||||
settings = self.settings = Settings()
|
||||
|
||||
# ## what happens after login?
|
||||
|
||||
self.next = current.request.vars._next
|
||||
if isinstance(self.next,(list,tuple)):
|
||||
self.next = self.next[0]
|
||||
|
||||
url_index = URL(controller,'index')
|
||||
url_login = URL(controller,function,args='login')
|
||||
# ## what happens after registration?
|
||||
|
||||
settings.hideerror = False
|
||||
settings.password_min_length = 4
|
||||
settings.cas_domains = [request.env.http_host]
|
||||
settings.cas_provider = cas_provider
|
||||
settings.cas_actions = {'login':'login',
|
||||
'validate':'validate',
|
||||
'servicevalidate':'serviceValidate',
|
||||
'proxyvalidate':'proxyValidate',
|
||||
'logout':'logout'}
|
||||
settings.cas_maps = None
|
||||
settings.extra_fields = {}
|
||||
settings.actions_disabled = []
|
||||
settings.reset_password_requires_verification = False
|
||||
settings.registration_requires_verification = False
|
||||
settings.registration_requires_approval = False
|
||||
settings.login_after_registration = False
|
||||
settings.alternate_requires_registration = False
|
||||
settings.create_user_groups = "user_%(id)s"
|
||||
settings.everybody_group_id = None
|
||||
|
||||
settings.controller = controller
|
||||
settings.function = function
|
||||
settings.login_url = self.url(function, args='login')
|
||||
settings.logged_url = self.url(function, args='profile')
|
||||
settings.download_url = self.url('download')
|
||||
settings.mailer = (mailer==True) and Mail() or mailer
|
||||
settings.login_captcha = None
|
||||
settings.register_captcha = None
|
||||
settings.retrieve_username_captcha = None
|
||||
settings.retrieve_password_captcha = None
|
||||
settings.captcha = None
|
||||
settings.expiration = 3600 # one hour
|
||||
settings.long_expiration = 3600*30*24 # one month
|
||||
settings.remember_me_form = True
|
||||
settings.allow_basic_login = False
|
||||
settings.allow_basic_login_only = False
|
||||
settings.on_failed_authorization = \
|
||||
self.url(function, args='not_authorized')
|
||||
|
||||
settings.on_failed_authentication = lambda x: redirect(x)
|
||||
|
||||
settings.formstyle = 'table3cols'
|
||||
settings.label_separator = ': '
|
||||
|
||||
# ## table names to be used
|
||||
|
||||
settings.password_field = 'password'
|
||||
settings.table_user_name = 'auth_user'
|
||||
settings.table_group_name = 'auth_group'
|
||||
settings.table_membership_name = 'auth_membership'
|
||||
settings.table_permission_name = 'auth_permission'
|
||||
settings.table_event_name = 'auth_event'
|
||||
settings.table_cas_name = 'auth_cas'
|
||||
|
||||
# ## if none, they will be created, unless DAL(lazy_tables=True)!!!
|
||||
|
||||
settings.table_user = None
|
||||
settings.table_group = None
|
||||
settings.table_membership = None
|
||||
settings.table_permission = None
|
||||
settings.table_event = None
|
||||
settings.table_cas = None
|
||||
|
||||
# ##
|
||||
|
||||
settings.showid = False
|
||||
|
||||
# ## these should be functions or lambdas
|
||||
|
||||
settings.login_next = self.url('index')
|
||||
settings.login_onvalidation = []
|
||||
settings.login_onaccept = []
|
||||
settings.login_methods = [self]
|
||||
settings.login_form = self
|
||||
settings.login_email_validate = True
|
||||
settings.login_userfield = None
|
||||
|
||||
settings.logout_next = self.url('index')
|
||||
settings.logout_onlogout = None
|
||||
|
||||
settings.register_next = self.url('index')
|
||||
settings.register_onvalidation = []
|
||||
settings.register_onaccept = []
|
||||
settings.register_fields = None
|
||||
settings.register_verify_password = True
|
||||
|
||||
settings.verify_email_next = self.url(function, args='login')
|
||||
settings.verify_email_onaccept = []
|
||||
|
||||
settings.profile_next = self.url('index')
|
||||
settings.profile_onvalidation = []
|
||||
settings.profile_onaccept = []
|
||||
settings.profile_fields = None
|
||||
settings.retrieve_username_next = self.url('index')
|
||||
settings.retrieve_password_next = self.url('index')
|
||||
settings.request_reset_password_next = self.url(function, args='login')
|
||||
settings.reset_password_next = self.url(function, args='login')
|
||||
|
||||
settings.change_password_next = self.url('index')
|
||||
settings.change_password_onvalidation = []
|
||||
settings.change_password_onaccept = []
|
||||
|
||||
settings.retrieve_password_onvalidation = []
|
||||
settings.reset_password_onvalidation = []
|
||||
settings.reset_password_onaccept = []
|
||||
|
||||
settings.email_case_sensitive = True
|
||||
settings.username_case_sensitive = True
|
||||
|
||||
settings.hmac_key = hmac_key
|
||||
|
||||
settings = self.settings = Settings()
|
||||
settings.update(Auth.default_settings)
|
||||
settings.update({
|
||||
'cas_domains': [request.env.http_host],
|
||||
'cas_provider': cas_provider,
|
||||
'cas_actions': {'login':'login',
|
||||
'validate':'validate',
|
||||
'servicevalidate':'serviceValidate',
|
||||
'proxyvalidate':'proxyValidate',
|
||||
'logout':'logout'},
|
||||
'extra_fields': {},
|
||||
'actions_disabled': [],
|
||||
'controller': controller,
|
||||
'function': function,
|
||||
'login_url': url_login,
|
||||
'logged_url': URL(controller, function, args='profile'),
|
||||
'download_url': URL(controller,'download'),
|
||||
'mailer': (mailer==True) and Mail() or mailer,
|
||||
'on_failed_authorization': \
|
||||
URL(controller,function, args='not_authorized'),
|
||||
'login_next': url_index,
|
||||
'login_onvalidation': [],
|
||||
'login_onaccept': [],
|
||||
'login_methods': [self],
|
||||
'login_form': self,
|
||||
'logout_next': url_index,
|
||||
'logout_onlogout': None,
|
||||
'register_next': url_index,
|
||||
'register_onvalidation': [],
|
||||
'register_onaccept': [],
|
||||
'verify_email_next': url_login,
|
||||
'verify_email_onaccept': [],
|
||||
'profile_next': url_index,
|
||||
'profile_onvalidation': [],
|
||||
'profile_onaccept': [],
|
||||
'retrieve_username_next': url_index,
|
||||
'retrieve_password_next': url_index,
|
||||
'request_reset_password_next': url_login,
|
||||
'reset_password_next': url_index,
|
||||
'change_password_next': url_index,
|
||||
'change_password_onvalidation': [],
|
||||
'change_password_onaccept': [],
|
||||
'retrieve_password_onvalidation': [],
|
||||
'reset_password_onvalidation': [],
|
||||
'reset_password_onaccept': [],
|
||||
'hmac_key': hmac_key,
|
||||
})
|
||||
settings.lock_keys = True
|
||||
|
||||
# ## these are messages that can be customized
|
||||
messages = self.messages = Messages(current.T)
|
||||
messages.login_button = 'Login'
|
||||
messages.register_button = 'Register'
|
||||
messages.password_reset_button = 'Request reset password'
|
||||
messages.password_change_button = 'Change password'
|
||||
messages.profile_save_button = 'Save profile'
|
||||
messages.submit_button = 'Submit'
|
||||
messages.verify_password = 'Verify Password'
|
||||
messages.delete_label = 'Check to delete'
|
||||
messages.function_disabled = 'Function disabled'
|
||||
messages.access_denied = 'Insufficient privileges'
|
||||
messages.registration_verifying = 'Registration needs verification'
|
||||
messages.registration_pending = 'Registration is pending approval'
|
||||
messages.login_disabled = 'Login disabled by administrator'
|
||||
messages.logged_in = 'Logged in'
|
||||
messages.email_sent = 'Email sent'
|
||||
messages.unable_to_send_email = 'Unable to send email'
|
||||
messages.email_verified = 'Email verified'
|
||||
messages.logged_out = 'Logged out'
|
||||
messages.registration_successful = 'Registration successful'
|
||||
messages.invalid_email = 'Invalid email'
|
||||
messages.unable_send_email = 'Unable to send email'
|
||||
messages.invalid_login = 'Invalid login'
|
||||
messages.invalid_user = 'Invalid user'
|
||||
messages.invalid_password = 'Invalid password'
|
||||
messages.is_empty = "Cannot be empty"
|
||||
messages.mismatched_password = "Password fields don't match"
|
||||
messages.verify_email = \
|
||||
'Click on the link ' + \
|
||||
URL('default','user',args='verify_email',scheme=True) + \
|
||||
'/%(key)s to verify your email'
|
||||
messages.verify_email_subject = 'Email verification'
|
||||
messages.username_sent = 'Your username was emailed to you'
|
||||
messages.new_password_sent = 'A new password was emailed to you'
|
||||
messages.password_changed = 'Password changed'
|
||||
messages.retrieve_username = 'Your username is: %(username)s'
|
||||
messages.retrieve_username_subject = 'Username retrieve'
|
||||
messages.retrieve_password = 'Your password is: %(password)s'
|
||||
messages.retrieve_password_subject = 'Password retrieve'
|
||||
messages.reset_password = \
|
||||
'Click on the link ' + \
|
||||
URL('default','user',args='reset_password',scheme=True) + \
|
||||
'/%(key)s to reset your password'
|
||||
messages.reset_password_subject = 'Password reset'
|
||||
messages.invalid_reset_password = 'Invalid reset password'
|
||||
messages.profile_updated = 'Profile updated'
|
||||
messages.new_password = 'New password'
|
||||
messages.old_password = 'Old password'
|
||||
messages.group_description = \
|
||||
'Group uniquely assigned to user %(id)s'
|
||||
|
||||
messages.register_log = 'User %(id)s Registered'
|
||||
messages.login_log = 'User %(id)s Logged-in'
|
||||
messages.login_failed_log = None
|
||||
messages.logout_log = 'User %(id)s Logged-out'
|
||||
messages.profile_log = 'User %(id)s Profile updated'
|
||||
messages.verify_email_log = 'User %(id)s Verification email sent'
|
||||
messages.retrieve_username_log = 'User %(id)s Username retrieved'
|
||||
messages.retrieve_password_log = 'User %(id)s Password retrieved'
|
||||
messages.reset_password_log = 'User %(id)s Password reset'
|
||||
messages.change_password_log = 'User %(id)s Password changed'
|
||||
messages.add_group_log = 'Group %(group_id)s created'
|
||||
messages.del_group_log = 'Group %(group_id)s deleted'
|
||||
messages.add_membership_log = None
|
||||
messages.del_membership_log = None
|
||||
messages.has_membership_log = None
|
||||
messages.add_permission_log = None
|
||||
messages.del_permission_log = None
|
||||
messages.has_permission_log = None
|
||||
messages.impersonate_log = 'User %(id)s is impersonating %(other_id)s'
|
||||
|
||||
messages.label_first_name = 'First name'
|
||||
messages.label_last_name = 'Last name'
|
||||
messages.label_username = 'Username'
|
||||
messages.label_email = 'E-mail'
|
||||
messages.label_password = 'Password'
|
||||
messages.label_registration_key = 'Registration key'
|
||||
messages.label_reset_password_key = 'Reset Password key'
|
||||
messages.label_registration_id = 'Registration identifier'
|
||||
messages.label_role = 'Role'
|
||||
messages.label_description = 'Description'
|
||||
messages.label_user_id = 'User ID'
|
||||
messages.label_group_id = 'Group ID'
|
||||
messages.label_name = 'Name'
|
||||
messages.label_table_name = 'Object or table name'
|
||||
messages.label_record_id = 'Record ID'
|
||||
messages.label_time_stamp = 'Timestamp'
|
||||
messages.label_client_ip = 'Client IP'
|
||||
messages.label_origin = 'Origin'
|
||||
messages.label_remember_me = "Remember me (for 30 days)"
|
||||
messages['T'] = current.T
|
||||
messages.verify_password_comment = 'please input your password again'
|
||||
messages.update(Auth.default_messages)
|
||||
messages.lock_keys = True
|
||||
|
||||
# for "remember me" option
|
||||
@@ -1336,37 +1319,39 @@ class Auth(object):
|
||||
settings = self.settings
|
||||
request = current.request
|
||||
T = current.T
|
||||
def lazy_user (auth = self): return auth.user_id
|
||||
reference_user = 'reference %s' % settings.table_user_name
|
||||
def lazy_user (auth = self):
|
||||
return auth.user_id
|
||||
def represent(id,record=None,s=settings):
|
||||
try:
|
||||
user = s.table_user(id)
|
||||
return '%(first_name)s %(last_name)s' % user
|
||||
except: return id
|
||||
self.signature = db.Table(self.db,'auth_signature',
|
||||
Field('is_active','boolean',
|
||||
default=True,
|
||||
readable=False, writable=False,
|
||||
label=T('Is Active')),
|
||||
Field('created_on','datetime',
|
||||
default=request.now,
|
||||
writable=False, readable=False,
|
||||
label=T('Created On')),
|
||||
Field('created_by',
|
||||
reference_user,
|
||||
default=lazy_user, represent=represent,
|
||||
writable=False, readable=False,
|
||||
label=T('Created By')),
|
||||
Field('modified_on','datetime',
|
||||
update=request.now,default=request.now,
|
||||
writable=False,readable=False,
|
||||
label=T('Modified On')),
|
||||
Field('modified_by',
|
||||
reference_user,represent=represent,
|
||||
default=lazy_user,update=lazy_user,
|
||||
writable=False,readable=False,
|
||||
label=T('Modified By')))
|
||||
|
||||
except:
|
||||
return id
|
||||
self.signature = db.Table(
|
||||
self.db,'auth_signature',
|
||||
Field('is_active','boolean',
|
||||
default=True,
|
||||
readable=False, writable=False,
|
||||
label=T('Is Active')),
|
||||
Field('created_on','datetime',
|
||||
default=request.now,
|
||||
writable=False, readable=False,
|
||||
label=T('Created On')),
|
||||
Field('created_by',
|
||||
reference_user,
|
||||
default=lazy_user, represent=represent,
|
||||
writable=False, readable=False,
|
||||
label=T('Created By')),
|
||||
Field('modified_on','datetime',
|
||||
update=request.now,default=request.now,
|
||||
writable=False,readable=False,
|
||||
label=T('Modified On')),
|
||||
Field('modified_by',
|
||||
reference_user,represent=represent,
|
||||
default=lazy_user,update=lazy_user,
|
||||
writable=False,readable=False,
|
||||
label=T('Modified By')))
|
||||
|
||||
def define_tables(self, username=False, signature=None,
|
||||
migrate=True, fake_migrate=False):
|
||||
@@ -1396,8 +1381,7 @@ class Auth(object):
|
||||
elif isinstance(signature,self.db.Table):
|
||||
signature_list = [signature]
|
||||
else:
|
||||
signature_list = signature
|
||||
lazy_tables, db._lazy_tables = db._lazy_tables, False
|
||||
signature_list = signature
|
||||
is_not_empty = IS_NOT_EMPTY(error_message=self.messages.is_empty)
|
||||
is_crypted = CRYPT(key=settings.hmac_key,
|
||||
min_length=settings.password_min_length)
|
||||
@@ -1600,7 +1584,6 @@ class Auth(object):
|
||||
actions=actions,
|
||||
maps=maps)
|
||||
|
||||
|
||||
def log_event(self, description, vars=None, origin='auth'):
|
||||
"""
|
||||
usage:
|
||||
@@ -1667,6 +1650,11 @@ class Auth(object):
|
||||
return user
|
||||
|
||||
def basic(self):
|
||||
"""
|
||||
perform basic login.
|
||||
reads current.request.env.http_authorization
|
||||
and returns basic_allowed,basic_accepted,user
|
||||
"""
|
||||
if not self.settings.allow_basic_login:
|
||||
return (False,False,False)
|
||||
basic = current.request.env.http_authorization
|
||||
@@ -1675,11 +1663,23 @@ class Auth(object):
|
||||
(username, password) = base64.b64decode(basic[6:]).split(':')
|
||||
return (True, True, self.login_bare(username, password))
|
||||
|
||||
def login_user(self,user):
|
||||
"""
|
||||
login the user = db.auth_user(id)
|
||||
"""
|
||||
# user=Storage(self.table_user()._filter_fields(user,id=True))
|
||||
current.session.auth = Storage(
|
||||
user = user,
|
||||
last_visit = current.request.now,
|
||||
expiration = self.settings.expiration,
|
||||
hmac_key = web2py_uuid())
|
||||
self.user = user
|
||||
self.update_groups()
|
||||
|
||||
def login_bare(self, username, password):
|
||||
"""
|
||||
logins user
|
||||
logins user as specified by usernname (or email) and password
|
||||
"""
|
||||
|
||||
request = current.request
|
||||
session = current.session
|
||||
table_user = self.table_user()
|
||||
@@ -1694,12 +1694,7 @@ class Auth(object):
|
||||
if user and user.get(passfield,False):
|
||||
password = table_user[passfield].validate(password)[0]
|
||||
if not user.registration_key and password == user[passfield]:
|
||||
user = Storage(table_user._filter_fields(user, id=True))
|
||||
session.auth = Storage(user=user, last_visit=request.now,
|
||||
expiration=self.settings.expiration,
|
||||
hmac_key = web2py_uuid())
|
||||
self.user = user
|
||||
self.update_groups()
|
||||
self.login_user(user)
|
||||
return user
|
||||
else:
|
||||
# user not in database try other login methods
|
||||
@@ -1738,15 +1733,15 @@ class Auth(object):
|
||||
renew=interactivelogin)
|
||||
service = session._cas_service
|
||||
del session._cas_service
|
||||
if request.vars.has_key('warn') and not interactivelogin:
|
||||
if 'warn' in request.vars and not interactivelogin:
|
||||
response.headers['refresh'] = "5;URL=%s"%service+"?ticket="+ticket
|
||||
return A("Continue to %s"%service,
|
||||
_href=service+"?ticket="+ticket)
|
||||
else:
|
||||
redirect(service+"?ticket="+ticket)
|
||||
if self.is_logged_in() and not request.vars.has_key('renew'):
|
||||
if self.is_logged_in() and not 'renew' in request.vars:
|
||||
return allow_access()
|
||||
elif not self.is_logged_in() and request.vars.has_key('gateway'):
|
||||
elif not self.is_logged_in() and 'gateway' in request.vars:
|
||||
redirect(service)
|
||||
def cas_onaccept(form, onaccept=onaccept):
|
||||
if not onaccept is DEFAULT: onaccept(form)
|
||||
@@ -1759,7 +1754,7 @@ class Auth(object):
|
||||
db, table = self.db, self.table_cas()
|
||||
current.response.headers['Content-Type']='text'
|
||||
ticket = request.vars.ticket
|
||||
renew = True if request.vars.has_key('renew') else False
|
||||
renew = 'renew' in request.vars
|
||||
row = table(ticket=ticket)
|
||||
success = False
|
||||
if row:
|
||||
@@ -1973,25 +1968,18 @@ class Auth(object):
|
||||
|
||||
# process authenticated users
|
||||
if user:
|
||||
user = Storage(table_user._filter_fields(user, id=True))
|
||||
|
||||
user = Row(table_user._filter_fields(user, id=True))
|
||||
# process authenticated users
|
||||
# user wants to be logged in for longer
|
||||
session.auth = Storage(
|
||||
user = user,
|
||||
last_visit = request.now,
|
||||
expiration = request.vars.get("remember",False) and \
|
||||
self.settings.long_expiration or self.settings.expiration,
|
||||
remember = request.vars.has_key("remember"),
|
||||
hmac_key = web2py_uuid()
|
||||
)
|
||||
|
||||
self.user = user
|
||||
self.login_user(user)
|
||||
session.auth.expiration = \
|
||||
request.vars.get('remember',False) and \
|
||||
self.settings.long_expiration or \
|
||||
self.settings.expiration
|
||||
session.auth.remember = 'remember' in request.vars
|
||||
self.log_event(log, user)
|
||||
session.flash = self.messages.logged_in
|
||||
|
||||
self.update_groups()
|
||||
|
||||
# how to continue
|
||||
if self.settings.login_form == self:
|
||||
if accepted_form:
|
||||
@@ -2131,11 +2119,14 @@ class Auth(object):
|
||||
if self.settings.everybody_group_id:
|
||||
self.add_membership(self.settings.everybody_group_id, form.vars.id)
|
||||
if self.settings.registration_requires_verification:
|
||||
link = self.url('user',args=('verify_email',key),scheme=True)
|
||||
|
||||
if not self.settings.mailer or \
|
||||
not self.settings.mailer.send(to=form.vars.email,
|
||||
subject=self.messages.verify_email_subject,
|
||||
message=self.messages.verify_email
|
||||
% dict(key=key)):
|
||||
not self.settings.mailer.send(
|
||||
to=form.vars.email,
|
||||
subject=self.messages.verify_email_subject,
|
||||
message=self.messages.verify_email \
|
||||
% dict(key=key,link=link)):
|
||||
self.db.rollback()
|
||||
response.flash = self.messages.unable_send_email
|
||||
return form
|
||||
@@ -2149,13 +2140,10 @@ class Auth(object):
|
||||
if not self.settings.registration_requires_verification:
|
||||
table_user[form.vars.id] = dict(registration_key='')
|
||||
session.flash = self.messages.registration_successful
|
||||
user = self.db(table_user[username] == form.vars[username]).select().first()
|
||||
user = Storage(table_user._filter_fields(user, id=True))
|
||||
session.auth = Storage(user=user, last_visit=request.now,
|
||||
expiration=self.settings.expiration,
|
||||
hmac_key = web2py_uuid())
|
||||
self.user = user
|
||||
self.update_groups()
|
||||
user = self.db(
|
||||
table_user[username] == form.vars[username]
|
||||
).select().first()
|
||||
self.login_user(user)
|
||||
session.flash = self.messages.logged_in
|
||||
self.log_event(log, form.vars)
|
||||
callback(onaccept,form)
|
||||
@@ -2192,7 +2180,7 @@ class Auth(object):
|
||||
|
||||
key = getarg(-1)
|
||||
table_user = self.table_user()
|
||||
user = self.db(table_user.registration_key == key).select().first()
|
||||
user = table_user(registration_key=key)
|
||||
if not user:
|
||||
redirect(self.settings.login_url)
|
||||
if self.settings.registration_requires_approval:
|
||||
@@ -2267,7 +2255,7 @@ class Auth(object):
|
||||
if form.accepts(request, session,
|
||||
formname='retrieve_username', dbio=False,
|
||||
onvalidation=onvalidation,hideerror=self.settings.hideerror):
|
||||
user = self.db(table_user.email == form.vars.email).select().first()
|
||||
user = table_user(email=form.vars.email)
|
||||
if not user:
|
||||
current.session.flash = \
|
||||
self.messages.invalid_email
|
||||
@@ -2345,7 +2333,7 @@ class Auth(object):
|
||||
if form.accepts(request, session,
|
||||
formname='retrieve_password', dbio=False,
|
||||
onvalidation=onvalidation,hideerror=self.settings.hideerror):
|
||||
user = self.db(table_user.email == form.vars.email).select().first()
|
||||
user = table_user(email=form.vars.email)
|
||||
if not user:
|
||||
current.session.flash = \
|
||||
self.messages.invalid_email
|
||||
@@ -2398,12 +2386,12 @@ class Auth(object):
|
||||
session = current.session
|
||||
|
||||
if next is DEFAULT:
|
||||
next = self.next or self.settings.reset_password_next
|
||||
next = self.settings.reset_password_next
|
||||
try:
|
||||
key = request.vars.key or getarg(-1)
|
||||
t0 = int(key.split('-')[0])
|
||||
if time.time()-t0 > 60*60*24: raise Exception
|
||||
user = self.db(table_user.reset_password_key == key).select().first()
|
||||
user = table_user(reset_password_key=key)
|
||||
if not user: raise Exception
|
||||
except Exception:
|
||||
session.flash = self.messages.invalid_reset_password
|
||||
@@ -2422,11 +2410,15 @@ class Auth(object):
|
||||
formstyle=self.settings.formstyle,
|
||||
separator=self.settings.label_separator
|
||||
)
|
||||
if form.accepts(request,session,hideerror=self.settings.hideerror):
|
||||
user.update_record(**{passfield:str(form.vars.new_password),
|
||||
'registration_key':'',
|
||||
'reset_password_key':''})
|
||||
if form.accepts(request,session,
|
||||
hideerror=self.settings.hideerror):
|
||||
user.update_record(
|
||||
**{passfield:str(form.vars.new_password),
|
||||
'registration_key':'',
|
||||
'reset_password_key':''})
|
||||
session.flash = self.messages.password_changed
|
||||
if self.settings.login_after_password_change:
|
||||
self.login_user(user)
|
||||
redirect(next)
|
||||
return form
|
||||
|
||||
@@ -2481,7 +2473,7 @@ class Auth(object):
|
||||
formname='reset_password', dbio=False,
|
||||
onvalidation=onvalidation,
|
||||
hideerror=self.settings.hideerror):
|
||||
user = self.db(table_user.email == form.vars.email).select().first()
|
||||
user = table_user(email=form.vars.email)
|
||||
if not user:
|
||||
session.flash = self.messages.invalid_email
|
||||
redirect(self.url(args=request.args))
|
||||
@@ -2504,11 +2496,14 @@ class Auth(object):
|
||||
|
||||
def email_reset_password(self,user):
|
||||
reset_password_key = str(int(time.time()))+'-' + web2py_uuid()
|
||||
link = self.url('user',
|
||||
args=('reset_password',reset_password_key),
|
||||
scheme=True)
|
||||
if self.settings.mailer.send(
|
||||
to=user.email,
|
||||
subject=self.messages.reset_password_subject,
|
||||
message=self.messages.reset_password % \
|
||||
dict(key=reset_password_key)):
|
||||
dict(key=reset_password_key,link=link)):
|
||||
user.update_record(reset_password_key=reset_password_key)
|
||||
return True
|
||||
return False
|
||||
@@ -2684,7 +2679,8 @@ class Auth(object):
|
||||
self.user = auth.user
|
||||
if self.settings.login_onaccept:
|
||||
form = Storage(dict(vars=self.user))
|
||||
self.settings.login_onaccept(form)
|
||||
for callback in self.settings.login_onaccept:
|
||||
callback(form)
|
||||
log = self.messages.impersonate_log
|
||||
self.log_event(log,dict(id=current_id, other_id=auth.user.id))
|
||||
elif user_id in (0, '0') and self.is_impersonating():
|
||||
@@ -3142,18 +3138,21 @@ class Auth(object):
|
||||
elif form.record and fieldname in form.record:
|
||||
new_record[fieldname]=form.record[fieldname]
|
||||
if fields:
|
||||
for key,value in fields.items():
|
||||
new_record[key] = value
|
||||
new_record.update(fields)
|
||||
id = archive_table.insert(**new_record)
|
||||
return id
|
||||
def wiki(self,slug=None,env=None,manage_permissions=False,force_prefix=''):
|
||||
|
||||
def wiki(self,slug=None,env=None,manage_permissions=False,force_prefix='', resolve=True):
|
||||
if not hasattr(self,'_wiki'):
|
||||
self._wiki = Wiki(self,
|
||||
manage_permissions=manage_permissions,
|
||||
force_prefix=force_prefix,env=env)
|
||||
else:
|
||||
self._wiki.env.update(env or {})
|
||||
return self._wiki.read(slug)['content'] if slug else self._wiki()
|
||||
# if resolve is set to True, process request as wiki call
|
||||
# resolve=False allows initial setup without wiki redirection
|
||||
if resolve:
|
||||
return self._wiki.read(slug)['content'] if slug else self._wiki()
|
||||
|
||||
class Crud(object):
|
||||
|
||||
@@ -3714,14 +3713,14 @@ def universal_caller(f, *a, **b):
|
||||
# There might be pos_args left, that are sent as named_values. Gather them as well.
|
||||
# If a argument already is populated with values we simply replaces them.
|
||||
for arg_name in pos_args[len(arg_dict):]:
|
||||
if b.has_key(arg_name):
|
||||
if arg_name in b:
|
||||
arg_dict[arg_name] = b[arg_name]
|
||||
|
||||
if len(arg_dict) >= len(pos_args):
|
||||
# All the positional arguments is found. The function may now be called.
|
||||
# However, we need to update the arg_dict with the values from the named arguments as well.
|
||||
for arg_name in named_args:
|
||||
if b.has_key(arg_name):
|
||||
if arg_name in b:
|
||||
arg_dict[arg_name] = b[arg_name]
|
||||
|
||||
return f(**arg_dict)
|
||||
@@ -4136,7 +4135,7 @@ class Service(object):
|
||||
prefix='pys',
|
||||
documentation = documentation,
|
||||
ns = True)
|
||||
for method, (function, returns, args, doc) in procedures.items():
|
||||
for method, (function, returns, args, doc) in procedures.iteritems():
|
||||
dispatcher.register_function(method, function, returns, args, doc)
|
||||
if request.env.request_method == 'POST':
|
||||
# Process normal Soap Operation
|
||||
@@ -4392,8 +4391,9 @@ class PluginManager(object):
|
||||
self.__dict__.clear()
|
||||
settings = self.__getattr__(plugin)
|
||||
settings.installed = True
|
||||
[settings.update({key:value}) for key,value in defaults.items() \
|
||||
if not key in settings]
|
||||
settings.update(
|
||||
(k,v) for k,v in defaults.items() if not k in settings)
|
||||
|
||||
def __getattr__(self, key):
|
||||
if not key in self.__dict__:
|
||||
self.__dict__[key] = Storage()
|
||||
@@ -4494,32 +4494,48 @@ class Wiki(object):
|
||||
self.host = current.request.env.http_host
|
||||
perms = self.manage_permissions = manage_permissions
|
||||
db = auth.db
|
||||
db.define_table(
|
||||
'wiki_page',
|
||||
Field('slug',
|
||||
requires=[IS_SLUG(),IS_NOT_IN_DB(db,'wiki_page.slug')],
|
||||
readable=False,writable=False),
|
||||
Field('title',unique=True),
|
||||
Field('body','text',notnull=True),
|
||||
Field('tags','list:string'),
|
||||
Field('can_read','list:string',writable=perms,readable=perms,
|
||||
default=[Wiki.everybody]),
|
||||
Field('can_edit','list:string',writable=perms,readable=perms,
|
||||
default=[Wiki.everybody]),
|
||||
Field('changelog'),
|
||||
Field('html','text',readable=False,writable=False,compute=render),
|
||||
auth.signature,format='%(title)s')
|
||||
db.define_table(
|
||||
'wiki_tag',
|
||||
Field('name'),
|
||||
Field('wiki_page',db.wiki_page),
|
||||
auth.signature,format='%(name)s')
|
||||
db.define_table(
|
||||
'wiki_media',
|
||||
Field('wiki_page',db.wiki_page),
|
||||
Field('title',required=True),
|
||||
Field('file','upload',required=True),
|
||||
auth.signature,format='%(title)s')
|
||||
table_definitions = {
|
||||
'wiki_page':{
|
||||
'args':[
|
||||
Field('slug',
|
||||
requires=[IS_SLUG(),
|
||||
IS_NOT_IN_DB(db,'wiki_page.slug')],
|
||||
readable=False,writable=False),
|
||||
Field('title',unique=True),
|
||||
Field('body','text',notnull=True),
|
||||
Field('tags','list:string'),
|
||||
Field('can_read','list:string',
|
||||
writable=perms,
|
||||
readable=perms,
|
||||
default=[Wiki.everybody]),
|
||||
Field('can_edit', 'list:string',
|
||||
writable=perms,readable=perms,
|
||||
default=[Wiki.everybody]),
|
||||
Field('changelog'),
|
||||
Field('html','text',compute=render,
|
||||
readable=False, writable=False),
|
||||
auth.signature],
|
||||
'vars':{'format':'%(title)s'}},
|
||||
'wiki_tag':{
|
||||
'args':[
|
||||
Field('name'),
|
||||
Field('wiki_page','reference wiki_page'),
|
||||
auth.signature],
|
||||
'vars':{'format':'%(name)s'}},
|
||||
'wiki_media':{
|
||||
'args':[
|
||||
Field('wiki_page','reference wiki_page'),
|
||||
Field('title',required=True),
|
||||
Field('file','upload',required=True),
|
||||
auth.signature],
|
||||
'vars':{'format':'%(title)s'}}
|
||||
}
|
||||
|
||||
# define only non-existent tables
|
||||
for key, value in table_definitions.iteritems():
|
||||
if not key in db.tables():
|
||||
db.define_table(key, *value['args'], **value['vars'])
|
||||
|
||||
def update_tags_insert(page,id,db=db):
|
||||
for tag in page.tags or []:
|
||||
tag = tag.strip().lower()
|
||||
|
||||