Merge ssh://github.com/web2py/web2py
This commit is contained in:
@@ -1 +1 @@
|
|||||||
Version 2.6.0-development+timestamp.2013.08.05.02.33.02
|
Version 2.6.0-development+timestamp.2013.08.09.16.07.32
|
||||||
|
|||||||
@@ -491,13 +491,11 @@ def enable():
|
|||||||
if is_gae:
|
if is_gae:
|
||||||
return SPAN(T('Not supported'), _style='color:yellow')
|
return SPAN(T('Not supported'), _style='color:yellow')
|
||||||
elif os.path.exists(filename):
|
elif os.path.exists(filename):
|
||||||
os.unlink(filename)
|
|
||||||
return SPAN(T('Disable'), _style='color:green')
|
return SPAN(T('Disable'), _style='color:green')
|
||||||
else:
|
else:
|
||||||
safe_open(filename, 'wb').write(time.ctime())
|
safe_open(filename, 'wb').write('disabled: True\ntime-disabled: %s' % request.now)
|
||||||
return SPAN(T('Enable'), _style='color:red')
|
return SPAN(T('Enable'), _style='color:red')
|
||||||
|
|
||||||
|
|
||||||
def peek():
|
def peek():
|
||||||
""" Visualize object code """
|
""" Visualize object code """
|
||||||
app = get_app(request.vars.app)
|
app = get_app(request.vars.app)
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ if MULTI_USER_MODE and not is_manager():
|
|||||||
session.flash = 'Not Authorized'
|
session.flash = 'Not Authorized'
|
||||||
redirect(URL('default', 'site'))
|
redirect(URL('default', 'site'))
|
||||||
|
|
||||||
|
from gluon.settings import settings
|
||||||
|
if not settings.is_source:
|
||||||
|
session.flash = 'Requires running web2py from source'
|
||||||
|
redirect(URL(request.application, 'default', 'site'))
|
||||||
|
|
||||||
forever = 10 ** 8
|
forever = 10 ** 8
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ except ImportError:
|
|||||||
session.flash = T('requires python-git, but not installed')
|
session.flash = T('requires python-git, but not installed')
|
||||||
redirect(URL('default', 'site'))
|
redirect(URL('default', 'site'))
|
||||||
|
|
||||||
|
from gluon.settings import settings
|
||||||
|
if not settings.is_source:
|
||||||
|
session.flash = 'Requires running web2py from source'
|
||||||
|
redirect(URL(request.application, 'default', 'site'))
|
||||||
|
|
||||||
def deploy():
|
def deploy():
|
||||||
apps = sorted(file for file in os.listdir(apath(r=request)))
|
apps = sorted(file for file in os.listdir(apath(r=request)))
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -24,6 +24,7 @@
|
|||||||
<li><a target="_blank" href="http://www.onemewebservices.com">OneMeWebServices</a> (Canada)</li>
|
<li><a target="_blank" href="http://www.onemewebservices.com">OneMeWebServices</a> (Canada)</li>
|
||||||
<li><a target="_blank" href="http://www.budgetbytes.nl">BudgetBytes</a> (The Netherlands)</li>
|
<li><a target="_blank" href="http://www.budgetbytes.nl">BudgetBytes</a> (The Netherlands)</li>
|
||||||
<li><a target="_blank" href="http://www.androsoft.pl">ANDROSoft</a> (Poland)</li>
|
<li><a target="_blank" href="http://www.androsoft.pl">ANDROSoft</a> (Poland)</li>
|
||||||
|
<li><a target="_blank" href="http://itjp.net.br/">ITJP</a> (Brazil)</li>
|
||||||
<li><a target="_blank" href="http://emotionull.com">Emotionull</a> (Greece and Cyprus)</li>
|
<li><a target="_blank" href="http://emotionull.com">Emotionull</a> (Greece and Cyprus)</li>
|
||||||
<li><a target="_blank" href="http://zarealye.com/ca/Collect_Advantage">Zarealye, Ltd.</a> (Russia)</li>
|
<li><a target="_blank" href="http://zarealye.com/ca/Collect_Advantage">Zarealye, Ltd.</a> (Russia)</li>
|
||||||
<li><a target="_blank" href="http://www.vsa-services.com/">VSA Services</a> (Singapore)</li>
|
<li><a target="_blank" href="http://www.vsa-services.com/">VSA Services</a> (Singapore)</li>
|
||||||
|
|||||||
@@ -1014,8 +1014,8 @@ def render(text,
|
|||||||
# - is empty -> this is an <hr /> tag
|
# - is empty -> this is an <hr /> tag
|
||||||
# - consists '|' -> table
|
# - consists '|' -> table
|
||||||
# - consists other characters -> blockquote
|
# - consists other characters -> blockquote
|
||||||
if ( lineno+1 >= strings_len or
|
if (lineno+1 >= strings_len or
|
||||||
not (s.count('-') == len(s) and len(s)>3) ):
|
not(s.count('-') == len(s) and len(s)>3)):
|
||||||
return (s, mtag, lineno)
|
return (s, mtag, lineno)
|
||||||
|
|
||||||
lineno+=1
|
lineno+=1
|
||||||
@@ -1034,7 +1034,8 @@ def render(text,
|
|||||||
while lineno < strings_len:
|
while lineno < strings_len:
|
||||||
s = strings[lineno].strip()
|
s = strings[lineno].strip()
|
||||||
if s[:1] == '=':
|
if s[:1] == '=':
|
||||||
if s.count('=')==len(s) and len(s)>3: # header or footer
|
# header or footer
|
||||||
|
if s.count('=')==len(s) and len(s)>3:
|
||||||
if not thead: # if thead list is empty:
|
if not thead: # if thead list is empty:
|
||||||
thead = tout
|
thead = tout
|
||||||
else:
|
else:
|
||||||
@@ -1054,16 +1055,16 @@ def render(text,
|
|||||||
tr = '<tr class="even">'
|
tr = '<tr class="even">'
|
||||||
else:
|
else:
|
||||||
tr = '<tr class="first">' if rownum == 0 else '<tr>'
|
tr = '<tr class="first">' if rownum == 0 else '<tr>'
|
||||||
tout.append(tr+''.join(['<td%s>%s</td>'% \
|
tout.append(tr + ''.join(['<td%s>%s</td>' % (
|
||||||
(' class="num"'
|
' class="num"'
|
||||||
if regex_num.match(f)
|
if regex_num.match(f) else '',
|
||||||
else '',
|
f.strip()
|
||||||
f.strip()
|
) for f in s.split('|')])+'</tr>'+pp)
|
||||||
) for f in s.split('|')])+'</tr>'+pp)
|
|
||||||
rownum+=1
|
rownum+=1
|
||||||
lineno+=1
|
lineno+=1
|
||||||
|
|
||||||
t_cls = ' class="%s%s"'%(class_prefix, t_cls) if t_cls and t_cls != 'id' else ''
|
t_cls = ' class="%s%s"'%(class_prefix, t_cls) \
|
||||||
|
if t_cls and t_cls != 'id' else ''
|
||||||
t_id = ' id="%s%s"'%(id_prefix, t_id) if t_id else ''
|
t_id = ' id="%s%s"'%(id_prefix, t_id) if t_id else ''
|
||||||
s = ''
|
s = ''
|
||||||
if thead:
|
if thead:
|
||||||
@@ -1080,7 +1081,7 @@ def render(text,
|
|||||||
else:
|
else:
|
||||||
# parse blockquote:
|
# parse blockquote:
|
||||||
bq_begin=lineno
|
bq_begin=lineno
|
||||||
t_mode = False # embidded table
|
t_mode = False # embedded table
|
||||||
t_cls = ''
|
t_cls = ''
|
||||||
t_id = ''
|
t_id = ''
|
||||||
|
|
||||||
@@ -1090,13 +1091,15 @@ def render(text,
|
|||||||
if not t_mode:
|
if not t_mode:
|
||||||
m = regex_tq.match(s)
|
m = regex_tq.match(s)
|
||||||
if m:
|
if m:
|
||||||
if lineno+1 == strings_len or '|' not in strings[lineno+1]:
|
if (lineno+1 == strings_len or
|
||||||
|
'|' not in strings[lineno+1]):
|
||||||
t_cls = m.group('c') or ''
|
t_cls = m.group('c') or ''
|
||||||
t_id = m.group('p') or ''
|
t_id = m.group('p') or ''
|
||||||
break
|
break
|
||||||
|
|
||||||
if regex_bq_headline.match(s):
|
if regex_bq_headline.match(s):
|
||||||
if lineno+1 < strings_len and strings[lineno+1].strip():
|
if (lineno+1 < strings_len and
|
||||||
|
strings[lineno+1].strip()):
|
||||||
t_mode = True
|
t_mode = True
|
||||||
lineno+=1
|
lineno+=1
|
||||||
continue
|
continue
|
||||||
@@ -1107,25 +1110,15 @@ def render(text,
|
|||||||
|
|
||||||
lineno+=1
|
lineno+=1
|
||||||
|
|
||||||
t_cls = ' class="%s%s"'%(class_prefix,t_cls) if t_cls and t_cls != 'id' else ''
|
t_cls = ' class="%s%s"'%(class_prefix,t_cls) \
|
||||||
t_id = ' id="%s%s"'%(id_prefix,t_id) if t_id else ''
|
if t_cls and t_cls != 'id' else ''
|
||||||
|
t_id = ' id="%s%s"'%(id_prefix,t_id) \
|
||||||
|
if t_id else ''
|
||||||
|
|
||||||
s = '<blockquote%s%s>%s</blockquote>%s' \
|
s = '<blockquote%s%s>%s</blockquote>%s' \
|
||||||
% (t_cls,
|
% (t_cls,
|
||||||
t_id,
|
t_id,
|
||||||
render('\n'.join(strings[bq_begin:lineno]),
|
'\n'.join(strings[bq_begin:lineno]),pp)
|
||||||
extra,
|
|
||||||
allowed,
|
|
||||||
'br',
|
|
||||||
URL,
|
|
||||||
environment,
|
|
||||||
latex,
|
|
||||||
autolinks,
|
|
||||||
protolinks,
|
|
||||||
class_prefix,
|
|
||||||
id_prefix,
|
|
||||||
pretty_print),
|
|
||||||
pp
|
|
||||||
)
|
|
||||||
mtag='q'
|
mtag='q'
|
||||||
else:
|
else:
|
||||||
s = '<hr />'
|
s = '<hr />'
|
||||||
|
|||||||
+107
-55
@@ -194,7 +194,7 @@ CALLABLETYPES = (types.LambdaType, types.FunctionType,
|
|||||||
|
|
||||||
TABLE_ARGS = set(
|
TABLE_ARGS = set(
|
||||||
('migrate','primarykey','fake_migrate','format','redefine',
|
('migrate','primarykey','fake_migrate','format','redefine',
|
||||||
'singular','plural','trigger_name','sequence_name',
|
'singular','plural','trigger_name','sequence_name','fields',
|
||||||
'common_filter','polymodel','table_class','on_define','actual_name'))
|
'common_filter','polymodel','table_class','on_define','actual_name'))
|
||||||
|
|
||||||
SELECT_ARGS = set(
|
SELECT_ARGS = set(
|
||||||
@@ -253,7 +253,7 @@ REGEX_TYPE = re.compile('^([\w\_\:]+)')
|
|||||||
REGEX_DBNAME = re.compile('^(\w+)(\:\w+)*')
|
REGEX_DBNAME = re.compile('^(\w+)(\:\w+)*')
|
||||||
REGEX_W = re.compile('^\w+$')
|
REGEX_W = re.compile('^\w+$')
|
||||||
REGEX_TABLE_DOT_FIELD = re.compile('^(\w+)\.(\w+)$')
|
REGEX_TABLE_DOT_FIELD = re.compile('^(\w+)\.(\w+)$')
|
||||||
REGEX_UPLOAD_PATTERN = re.compile('(?P<table>[\w\-]+)\.(?P<field>[\w\-]+)\.(?P<uuidkey>[\w\-]+)\.(?P<name>\w+)\.\w+$')
|
REGEX_UPLOAD_PATTERN = re.compile('(?P<table>[\w\-]+)\.(?P<field>[\w\-]+)\.(?P<uuidkey>[\w\-]+)(\.(?P<name>\w+))?\.\w+$')
|
||||||
REGEX_CLEANUP_FN = re.compile('[\'"\s;]+')
|
REGEX_CLEANUP_FN = re.compile('[\'"\s;]+')
|
||||||
REGEX_UNPACK = re.compile('(?<!\|)\|(?!\|)')
|
REGEX_UNPACK = re.compile('(?<!\|)\|(?!\|)')
|
||||||
REGEX_PYTHON_KEYWORDS = re.compile('^(and|del|from|not|while|as|elif|global|or|with|assert|else|if|pass|yield|break|except|import|print|class|exec|in|raise|continue|finally|is|return|def|for|lambda|try)$')
|
REGEX_PYTHON_KEYWORDS = re.compile('^(and|del|from|not|while|as|elif|global|or|with|assert|else|if|pass|yield|break|except|import|print|class|exec|in|raise|continue|finally|is|return|def|for|lambda|try)$')
|
||||||
@@ -1076,16 +1076,18 @@ class BaseAdapter(ConnectionPool):
|
|||||||
elif not key in sql_fields:
|
elif not key in sql_fields:
|
||||||
del sql_fields_current[key]
|
del sql_fields_current[key]
|
||||||
ftype = sql_fields_old[key]['type']
|
ftype = sql_fields_old[key]['type']
|
||||||
if self.dbengine in ('postgres',) and ftype.startswith('geometry'):
|
if (self.dbengine in ('postgres',) and
|
||||||
|
ftype.startswith('geometry')):
|
||||||
geotype, parms = ftype[:-1].split('(')
|
geotype, parms = ftype[:-1].split('(')
|
||||||
schema = parms.split(',')[0]
|
schema = parms.split(',')[0]
|
||||||
query = [ "SELECT DropGeometryColumn ('%(schema)s', '%(table)s', '%(field)s');" %
|
query = [ "SELECT DropGeometryColumn ('%(schema)s', "+
|
||||||
|
"'%(table)s', '%(field)s');" %
|
||||||
dict(schema=schema, table=tablename, field=key,) ]
|
dict(schema=schema, table=tablename, field=key,) ]
|
||||||
elif self.dbengine in ('firebird',):
|
elif self.dbengine in ('firebird',):
|
||||||
query = ['ALTER TABLE %s DROP %s;' % (tablename, key)]
|
query = ['ALTER TABLE %s DROP %s;' % (tablename, key)]
|
||||||
else:
|
else:
|
||||||
query = ['ALTER TABLE %s DROP COLUMN %s;'
|
query = ['ALTER TABLE %s DROP COLUMN %s;' %
|
||||||
% (tablename, key)]
|
(tablename, key)]
|
||||||
metadata_change = True
|
metadata_change = True
|
||||||
elif sql_fields[key]['sql'] != sql_fields_old[key]['sql'] \
|
elif sql_fields[key]['sql'] != sql_fields_old[key]['sql'] \
|
||||||
and not (key in table.fields and
|
and not (key in table.fields and
|
||||||
@@ -1124,8 +1126,10 @@ class BaseAdapter(ConnectionPool):
|
|||||||
self.log('faked!\n', table)
|
self.log('faked!\n', table)
|
||||||
else:
|
else:
|
||||||
self.execute(sub_query)
|
self.execute(sub_query)
|
||||||
# Caveat: mysql, oracle and firebird do not allow multiple alter table
|
# Caveat: mysql, oracle and firebird
|
||||||
# in one transaction so we must commit partial transactions and
|
# do not allow multiple alter table
|
||||||
|
# in one transaction so we must commit
|
||||||
|
# partial transactions and
|
||||||
# update table._dbt after alter table.
|
# update table._dbt after alter table.
|
||||||
if db._adapter.commit_on_alter_table:
|
if db._adapter.commit_on_alter_table:
|
||||||
db.commit()
|
db.commit()
|
||||||
@@ -1259,12 +1263,17 @@ class BaseAdapter(ConnectionPool):
|
|||||||
return '(%s OR %s)' % (self.expand(first), self.expand(second))
|
return '(%s OR %s)' % (self.expand(first), self.expand(second))
|
||||||
|
|
||||||
def BELONGS(self, first, second):
|
def BELONGS(self, first, second):
|
||||||
if isinstance(second, str):
|
if isinstance(second, str):
|
||||||
return '(%s IN (%s))' % (self.expand(first), second[:-1])
|
return '(%s IN (%s))' % (self.expand(first), second[:-1])
|
||||||
elif not second:
|
if not second:
|
||||||
return '(1=0)'
|
return '(1=0)'
|
||||||
items = ','.join(self.expand(item, first.type) for item in second)
|
if isinstance(second, (list,tuple,frozenset)):
|
||||||
return '(%s IN (%s))' % (self.expand(first), items)
|
second = set(second) # remove duplicates, make mutable
|
||||||
|
if isinstance(second, set) and None in second:
|
||||||
|
second.remove(None)
|
||||||
|
return self.OR(self.EQ(first, None), self.BELONGS(first, second))
|
||||||
|
items = ','.join(self.expand(item, first.type) for item in second)
|
||||||
|
return '(%s IN (%s))' % (self.expand(first), items)
|
||||||
|
|
||||||
def REGEXP(self, first, second):
|
def REGEXP(self, first, second):
|
||||||
"regular expression operator"
|
"regular expression operator"
|
||||||
@@ -4364,6 +4373,7 @@ class GoogleSQLAdapter(UseDatabaseStoredFile,MySQLAdapter):
|
|||||||
self.pool_size = pool_size
|
self.pool_size = pool_size
|
||||||
self.db_codec = db_codec
|
self.db_codec = db_codec
|
||||||
self._after_connection = after_connection
|
self._after_connection = after_connection
|
||||||
|
if do_connect: self.find_driver(adapter_args, uri)
|
||||||
self.folder = folder or pjoin('$HOME',THREAD_LOCAL.folder.split(
|
self.folder = folder or pjoin('$HOME',THREAD_LOCAL.folder.split(
|
||||||
os.sep+'applications'+os.sep,1)[1])
|
os.sep+'applications'+os.sep,1)[1])
|
||||||
ruri = uri.split("://")[1]
|
ruri = uri.split("://")[1]
|
||||||
@@ -4394,6 +4404,10 @@ class GoogleSQLAdapter(UseDatabaseStoredFile,MySQLAdapter):
|
|||||||
def execute(self, command, *a, **b):
|
def execute(self, command, *a, **b):
|
||||||
return self.log_execute(command.decode('utf8'), *a, **b)
|
return self.log_execute(command.decode('utf8'), *a, **b)
|
||||||
|
|
||||||
|
def find_driver(self,adapter_args,uri=None):
|
||||||
|
self.adapter_args = adapter_args
|
||||||
|
self.driver = "google"
|
||||||
|
|
||||||
class NoSQLAdapter(BaseAdapter):
|
class NoSQLAdapter(BaseAdapter):
|
||||||
can_select_for_update = False
|
can_select_for_update = False
|
||||||
|
|
||||||
@@ -5349,7 +5363,9 @@ class MongoDBAdapter(NoSQLAdapter):
|
|||||||
return datetime.datetime.combine(d, value)
|
return datetime.datetime.combine(d, value)
|
||||||
elif fieldtype == "blob":
|
elif fieldtype == "blob":
|
||||||
from bson import Binary
|
from bson import Binary
|
||||||
return Binary(value)
|
if not isinstance(value, Binary):
|
||||||
|
return Binary(value)
|
||||||
|
return value
|
||||||
elif (isinstance(fieldtype, basestring) and
|
elif (isinstance(fieldtype, basestring) and
|
||||||
fieldtype.startswith('list:')):
|
fieldtype.startswith('list:')):
|
||||||
if fieldtype.startswith('list:reference'):
|
if fieldtype.startswith('list:reference'):
|
||||||
@@ -7760,6 +7776,8 @@ def index():
|
|||||||
*fields,
|
*fields,
|
||||||
**args
|
**args
|
||||||
):
|
):
|
||||||
|
if not fields and 'fields' in args:
|
||||||
|
fields = args.get('fields',())
|
||||||
if not isinstance(tablename,str):
|
if not isinstance(tablename,str):
|
||||||
raise SyntaxError("missing table name")
|
raise SyntaxError("missing table name")
|
||||||
elif hasattr(self,tablename) or tablename in self.tables:
|
elif hasattr(self,tablename) or tablename in self.tables:
|
||||||
@@ -8240,24 +8258,27 @@ class Table(object):
|
|||||||
fieldnames.add('id')
|
fieldnames.add('id')
|
||||||
self._id = field
|
self._id = field
|
||||||
virtual_fields = []
|
virtual_fields = []
|
||||||
|
def include_new(field):
|
||||||
|
newfields.append(field)
|
||||||
|
fieldnames.add(field.name)
|
||||||
|
if field.type=='id':
|
||||||
|
self._id = field
|
||||||
for field in fields:
|
for field in fields:
|
||||||
if isinstance(field, (FieldMethod, FieldVirtual)):
|
if isinstance(field, (FieldMethod, FieldVirtual)):
|
||||||
virtual_fields.append(field)
|
virtual_fields.append(field)
|
||||||
elif isinstance(field, Field) and not field.name in fieldnames:
|
elif isinstance(field, Field) and not field.name in fieldnames:
|
||||||
if field.db is not None:
|
if field.db is not None:
|
||||||
field = copy.copy(field)
|
field = copy.copy(field)
|
||||||
newfields.append(field)
|
include_new(field)
|
||||||
fieldnames.add(field.name)
|
elif isinstance(field, dict) and 'fieldname' and \
|
||||||
if field.type=='id':
|
not field['fieldname'] in fieldnames:
|
||||||
self._id = field
|
include_new(Field(**field))
|
||||||
elif isinstance(field, Table):
|
elif isinstance(field, Table):
|
||||||
table = field
|
table = field
|
||||||
for field in table:
|
for field in table:
|
||||||
if not field.name in fieldnames and not field.type=='id':
|
if not field.name in fieldnames and not field.type=='id':
|
||||||
t2 = not table._actual and self._tablename
|
t2 = not table._actual and self._tablename
|
||||||
field = field.clone(point_self_references_to=t2)
|
include_new(field.clone(point_self_references_to=t2))
|
||||||
newfields.append(field)
|
|
||||||
fieldnames.add(field.name)
|
|
||||||
elif not isinstance(field, (Field, Table)):
|
elif not isinstance(field, (Field, Table)):
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
'define_table argument is not a Field or Table: %s' % field)
|
'define_table argument is not a Field or Table: %s' % field)
|
||||||
@@ -9431,9 +9452,6 @@ class Field(Expression):
|
|||||||
raise http.HTTP(404)
|
raise http.HTTP(404)
|
||||||
if self.authorize and not self.authorize(row):
|
if self.authorize and not self.authorize(row):
|
||||||
raise http.HTTP(403)
|
raise http.HTTP(403)
|
||||||
m = REGEX_UPLOAD_PATTERN.match(name)
|
|
||||||
if not m or not self.isattachment:
|
|
||||||
raise TypeError('Can\'t retrieve %s' % name)
|
|
||||||
file_properties = self.retrieve_file_properties(name,path)
|
file_properties = self.retrieve_file_properties(name,path)
|
||||||
filename = file_properties['filename']
|
filename = file_properties['filename']
|
||||||
if isinstance(self_uploadfield, str): # ## if file is in DB
|
if isinstance(self_uploadfield, str): # ## if file is in DB
|
||||||
@@ -9457,35 +9475,35 @@ class Field(Expression):
|
|||||||
return (filename, stream)
|
return (filename, stream)
|
||||||
|
|
||||||
def retrieve_file_properties(self, name, path=None):
|
def retrieve_file_properties(self, name, path=None):
|
||||||
|
m = REGEX_UPLOAD_PATTERN.match(name)
|
||||||
|
if not m or not self.isattachment:
|
||||||
|
raise TypeError('Can\'t retrieve %s file properties' % name)
|
||||||
self_uploadfield = self.uploadfield
|
self_uploadfield = self.uploadfield
|
||||||
if self.custom_retrieve_file_properties:
|
if self.custom_retrieve_file_properties:
|
||||||
return self.custom_retrieve_file_properties(name, path)
|
return self.custom_retrieve_file_properties(name, path)
|
||||||
try:
|
if m.group('name'):
|
||||||
m = REGEX_UPLOAD_PATTERN.match(name)
|
try:
|
||||||
if not m or not self.isattachment:
|
filename = base64.b16decode(m.group('name'), True)
|
||||||
raise TypeError('Can\'t retrieve %s file properties' % name)
|
filename = REGEX_CLEANUP_FN.sub('_', filename)
|
||||||
filename = base64.b16decode(m.group('name'), True)
|
except (TypeError, AttributeError):
|
||||||
filename = REGEX_CLEANUP_FN.sub('_', filename)
|
filename = name
|
||||||
except (TypeError, AttributeError):
|
|
||||||
filename = name
|
|
||||||
if isinstance(self_uploadfield, str): # ## if file is in DB
|
|
||||||
return dict(path=None,filename=filename)
|
|
||||||
elif isinstance(self_uploadfield,Field):
|
|
||||||
return dict(path=None,filename=filename)
|
|
||||||
else:
|
else:
|
||||||
# ## if file is on filesystem
|
filename = name
|
||||||
if path:
|
# ## if file is in DB
|
||||||
pass
|
if isinstance(self_uploadfield, (str, Field)):
|
||||||
elif self.uploadfolder:
|
return dict(path=None,filename=filename)
|
||||||
|
# ## if file is on filesystem
|
||||||
|
if not path:
|
||||||
|
if self.uploadfolder:
|
||||||
path = self.uploadfolder
|
path = self.uploadfolder
|
||||||
else:
|
else:
|
||||||
path = pjoin(self.db._adapter.folder, '..', 'uploads')
|
path = pjoin(self.db._adapter.folder, '..', 'uploads')
|
||||||
if self.uploadseparate:
|
if self.uploadseparate:
|
||||||
t = m.group('table')
|
t = m.group('table')
|
||||||
f = m.group('field')
|
f = m.group('field')
|
||||||
u = m.group('uuidkey')
|
u = m.group('uuidkey')
|
||||||
path = pjoin(path,"%s.%s" % (t,f),u[:2])
|
path = pjoin(path,"%s.%s" % (t,f),u[:2])
|
||||||
return dict(path=path,filename=filename)
|
return dict(path=path,filename=filename)
|
||||||
|
|
||||||
|
|
||||||
def formatter(self, value):
|
def formatter(self, value):
|
||||||
@@ -10312,22 +10330,56 @@ class Rows(object):
|
|||||||
rows.records = sorted(self,key=f,reverse=reverse)
|
rows.records = sorted(self,key=f,reverse=reverse)
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
|
def group_by_value(self, *fields, **args):
|
||||||
def group_by_value(self, field):
|
|
||||||
"""
|
"""
|
||||||
regroups the rows, by one of the fields
|
regroups the rows, by one of the fields
|
||||||
"""
|
"""
|
||||||
|
one_result = False
|
||||||
|
if 'one_result' in args:
|
||||||
|
one_result = args['one_result']
|
||||||
|
|
||||||
|
def build_fields_struct(row, fields, num, groups):
|
||||||
|
''' helper function:
|
||||||
|
'''
|
||||||
|
if num > len(fields)-1:
|
||||||
|
if one_result:
|
||||||
|
return row
|
||||||
|
else:
|
||||||
|
return [row]
|
||||||
|
|
||||||
|
key = fields[num]
|
||||||
|
value = row[key]
|
||||||
|
|
||||||
|
if value not in groups:
|
||||||
|
groups[value] = build_fields_struct(row, fields, num+1, {})
|
||||||
|
else:
|
||||||
|
struct = build_fields_struct(row, fields, num+1, groups[ value ])
|
||||||
|
|
||||||
|
# still have more grouping to do
|
||||||
|
if type(struct) == type(dict()):
|
||||||
|
groups[value].update()
|
||||||
|
# no more grouping, first only is off
|
||||||
|
elif type(struct) == type(list()):
|
||||||
|
groups[value] += struct
|
||||||
|
# no more grouping, first only on
|
||||||
|
else:
|
||||||
|
groups[value] = struct
|
||||||
|
|
||||||
|
return groups
|
||||||
|
|
||||||
|
if len(fields) == 0:
|
||||||
|
return self
|
||||||
|
|
||||||
|
# if select returned no results
|
||||||
if not self.records:
|
if not self.records:
|
||||||
return {}
|
return {}
|
||||||
key = str(field)
|
|
||||||
grouped_row_group = dict()
|
grouped_row_group = dict()
|
||||||
|
|
||||||
|
# build the struct
|
||||||
for row in self:
|
for row in self:
|
||||||
value = row[key]
|
build_fields_struct(row, fields, 0, grouped_row_group)
|
||||||
if not value in grouped_row_group:
|
|
||||||
grouped_row_group[value] = [row]
|
|
||||||
else:
|
|
||||||
grouped_row_group[value].append(row)
|
|
||||||
return grouped_row_group
|
return grouped_row_group
|
||||||
|
|
||||||
def render(self, i=None, fields=None):
|
def render(self, i=None, fields=None):
|
||||||
|
|||||||
+166
-5
@@ -36,6 +36,11 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import threading
|
import threading
|
||||||
|
import cgi
|
||||||
|
import copy
|
||||||
|
import tempfile
|
||||||
|
from cache import CacheInRam
|
||||||
|
from fileutils import copystream
|
||||||
|
|
||||||
FMT = '%a, %d-%b-%Y %H:%M:%S PST'
|
FMT = '%a, %d-%b-%Y %H:%M:%S PST'
|
||||||
PAST = 'Sat, 1-Jan-1971 00:00:00'
|
PAST = 'Sat, 1-Jan-1971 00:00:00'
|
||||||
@@ -47,6 +52,15 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
have_minify = False
|
have_minify = False
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
import simplejson as sj #external installed library
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
import json as sj #standard installed library
|
||||||
|
except:
|
||||||
|
import contrib.simplejson as sj #pure python library
|
||||||
|
|
||||||
regex_session_id = re.compile('^([\w\-]+/)?[\w\-\.]+$')
|
regex_session_id = re.compile('^([\w\-]+/)?[\w\-\.]+$')
|
||||||
|
|
||||||
__all__ = ['Request', 'Response', 'Session']
|
__all__ = ['Request', 'Response', 'Session']
|
||||||
@@ -62,6 +76,52 @@ css_inline = '<style type="text/css">\n%s\n</style>'
|
|||||||
js_inline = '<script type="text/javascript">\n%s\n</script>'
|
js_inline = '<script type="text/javascript">\n%s\n</script>'
|
||||||
|
|
||||||
|
|
||||||
|
def copystream_progress(request, chunk_size=10 ** 5):
|
||||||
|
"""
|
||||||
|
copies request.env.wsgi_input into request.body
|
||||||
|
and stores progress upload status in cache_ram
|
||||||
|
X-Progress-ID:length and X-Progress-ID:uploaded
|
||||||
|
"""
|
||||||
|
env = request.env
|
||||||
|
if not env.content_length:
|
||||||
|
return cStringIO.StringIO()
|
||||||
|
source = env.wsgi_input
|
||||||
|
try:
|
||||||
|
size = int(env.content_length)
|
||||||
|
except ValueError:
|
||||||
|
raise HTTP(400, "Invalid Content-Length header")
|
||||||
|
try: # Android requires this
|
||||||
|
dest = tempfile.NamedTemporaryFile()
|
||||||
|
except NotImplementedError: # and GAE this
|
||||||
|
dest = tempfile.TemporaryFile()
|
||||||
|
if not 'X-Progress-ID' in request.vars:
|
||||||
|
copystream(source, dest, size, chunk_size)
|
||||||
|
return dest
|
||||||
|
cache_key = 'X-Progress-ID:' + request.vars['X-Progress-ID']
|
||||||
|
cache_ram = CacheInRam(request) # same as cache.ram because meta_storage
|
||||||
|
cache_ram(cache_key + ':length', lambda: size, 0)
|
||||||
|
cache_ram(cache_key + ':uploaded', lambda: 0, 0)
|
||||||
|
while size > 0:
|
||||||
|
if size < chunk_size:
|
||||||
|
data = source.read(size)
|
||||||
|
cache_ram.increment(cache_key + ':uploaded', size)
|
||||||
|
else:
|
||||||
|
data = source.read(chunk_size)
|
||||||
|
cache_ram.increment(cache_key + ':uploaded', chunk_size)
|
||||||
|
length = len(data)
|
||||||
|
if length > size:
|
||||||
|
(data, length) = (data[:size], size)
|
||||||
|
size -= length
|
||||||
|
if length == 0:
|
||||||
|
break
|
||||||
|
dest.write(data)
|
||||||
|
if length < chunk_size:
|
||||||
|
break
|
||||||
|
dest.seek(0)
|
||||||
|
cache_ram(cache_key + ':length', None)
|
||||||
|
cache_ram(cache_key + ':uploaded', None)
|
||||||
|
return dest
|
||||||
|
|
||||||
class Request(Storage):
|
class Request(Storage):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -81,13 +141,15 @@ class Request(Storage):
|
|||||||
- restful()
|
- restful()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, env):
|
||||||
Storage.__init__(self)
|
Storage.__init__(self)
|
||||||
self.env = Storage()
|
self.env = Storage(env)
|
||||||
|
self.env.web2py_path = global_settings.applications_parent
|
||||||
|
self.env.update(global_settings)
|
||||||
self.cookies = Cookie.SimpleCookie()
|
self.cookies = Cookie.SimpleCookie()
|
||||||
self.get_vars = Storage()
|
self._get_vars = None
|
||||||
self.post_vars = Storage()
|
self._post_vars = None
|
||||||
self.vars = Storage()
|
self._vars = None
|
||||||
self.folder = None
|
self.folder = None
|
||||||
self.application = None
|
self.application = None
|
||||||
self.function = None
|
self.function = None
|
||||||
@@ -100,6 +162,105 @@ class Request(Storage):
|
|||||||
self.is_local = False
|
self.is_local = False
|
||||||
self.global_settings = settings.global_settings
|
self.global_settings = settings.global_settings
|
||||||
|
|
||||||
|
def parse_get_vars(self):
|
||||||
|
query_string = self.env.get('QUERY_STRING','')
|
||||||
|
dget = cgi.parse_qs(query_string, keep_blank_values=1)
|
||||||
|
get_vars = self._get_vars = Storage(dget)
|
||||||
|
for (key, value) in get_vars.iteritems():
|
||||||
|
if isinstance(value,list) and len(value)==1:
|
||||||
|
get_vars[key] = value[0]
|
||||||
|
|
||||||
|
def parse_post_vars(self):
|
||||||
|
env = self.env
|
||||||
|
post_vars = self._post_vars = Storage()
|
||||||
|
try:
|
||||||
|
self.body = body = copystream_progress(self)
|
||||||
|
except IOError:
|
||||||
|
raise HTTP(400, "Bad Request - HTTP body is incomplete")
|
||||||
|
|
||||||
|
#if content-type is application/json, we must read the body
|
||||||
|
is_json = env.get('content_type', '')[:16] == 'application/json'
|
||||||
|
|
||||||
|
if is_json:
|
||||||
|
try:
|
||||||
|
json_vars = sj.load(body)
|
||||||
|
except:
|
||||||
|
# incoherent request bodies can still be parsed "ad-hoc"
|
||||||
|
json_vars = {}
|
||||||
|
pass
|
||||||
|
# update vars and get_vars with what was posted as json
|
||||||
|
if isinstance(json_vars, dict):
|
||||||
|
post_vars.update(json_vars)
|
||||||
|
|
||||||
|
body.seek(0)
|
||||||
|
|
||||||
|
# parse POST variables on POST, PUT, BOTH only in post_vars
|
||||||
|
if (body and
|
||||||
|
env.request_method in ('POST', 'PUT', 'DELETE', 'BOTH') and
|
||||||
|
not is_json):
|
||||||
|
dpost = cgi.FieldStorage(fp=body, environ=env, keep_blank_values=1)
|
||||||
|
post_vars.update(dpost)
|
||||||
|
# The same detection used by FieldStorage to detect multipart POSTs
|
||||||
|
is_multipart = dpost.type[:10] == 'multipart/'
|
||||||
|
body.seek(0)
|
||||||
|
|
||||||
|
def listify(a):
|
||||||
|
return (not isinstance(a, list) and [a]) or a
|
||||||
|
try:
|
||||||
|
keys = sorted(dpost)
|
||||||
|
except TypeError:
|
||||||
|
keys = []
|
||||||
|
for key in keys:
|
||||||
|
if key is None:
|
||||||
|
continue # not sure why cgi.FieldStorage returns None key
|
||||||
|
dpk = dpost[key]
|
||||||
|
# if an element is not a file replace it with its value else leave it alone
|
||||||
|
if isinstance(dpk, list):
|
||||||
|
value = []
|
||||||
|
for _dpk in dpk:
|
||||||
|
if not _dpk.filename:
|
||||||
|
value.append(_dpk.value)
|
||||||
|
else:
|
||||||
|
value.append(_dpk)
|
||||||
|
elif not dpk.filename:
|
||||||
|
value = dpk.value
|
||||||
|
else:
|
||||||
|
value = dpk
|
||||||
|
pvalue = listify(value)
|
||||||
|
if len(pvalue):
|
||||||
|
post_vars[key] = (len(pvalue) > 1 and pvalue) or pvalue[0]
|
||||||
|
|
||||||
|
def parse_all_vars(self):
|
||||||
|
self._vars = copy.copy(self.get_vars)
|
||||||
|
for key,value in self.post_vars.iteritems():
|
||||||
|
if not key in self._vars:
|
||||||
|
self._vars[key] = value
|
||||||
|
else:
|
||||||
|
if not isinstance(self._vars[key],list):
|
||||||
|
self._vars[key] = [self._vars[key]]
|
||||||
|
self._vars[key] += value if isinstance(value,list) else [value]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_vars(self):
|
||||||
|
"lazily parse the query string into get_vars"
|
||||||
|
if self._get_vars is None:
|
||||||
|
self.parse_get_vars()
|
||||||
|
return self._get_vars
|
||||||
|
|
||||||
|
@property
|
||||||
|
def post_vars(self):
|
||||||
|
"lazily parse the body into post_vars"
|
||||||
|
if self._post_vars is None:
|
||||||
|
self.parse_post_vars()
|
||||||
|
return self._post_vars
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vars(self):
|
||||||
|
"lazily parse all get_vars and post_vars to fill vars"
|
||||||
|
if self._vars is None:
|
||||||
|
self.parse_all_vars()
|
||||||
|
return self._vars
|
||||||
|
|
||||||
def compute_uuid(self):
|
def compute_uuid(self):
|
||||||
self.uuid = '%s/%s.%s.%s' % (
|
self.uuid = '%s/%s.%s.%s' % (
|
||||||
self.application,
|
self.application,
|
||||||
|
|||||||
+32
-155
@@ -14,7 +14,6 @@ Contains:
|
|||||||
|
|
||||||
if False: import import_all # DO NOT REMOVE PART OF FREEZE PROCESS
|
if False: import import_all # DO NOT REMOVE PART OF FREEZE PROCESS
|
||||||
import gc
|
import gc
|
||||||
import cgi
|
|
||||||
import cStringIO
|
import cStringIO
|
||||||
import Cookie
|
import Cookie
|
||||||
import os
|
import os
|
||||||
@@ -25,11 +24,10 @@ import time
|
|||||||
import datetime
|
import datetime
|
||||||
import signal
|
import signal
|
||||||
import socket
|
import socket
|
||||||
import tempfile
|
|
||||||
import random
|
import random
|
||||||
import string
|
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import simplejson as sj #external installed library
|
import simplejson as sj #external installed library
|
||||||
except:
|
except:
|
||||||
@@ -40,7 +38,7 @@ except:
|
|||||||
|
|
||||||
from thread import allocate_lock
|
from thread import allocate_lock
|
||||||
|
|
||||||
from fileutils import abspath, write_file, parse_version, copystream
|
from fileutils import abspath, write_file
|
||||||
from settings import global_settings
|
from settings import global_settings
|
||||||
from admin import add_path_first, create_missing_folders, create_missing_app_folders
|
from admin import add_path_first, create_missing_folders, create_missing_app_folders
|
||||||
from globals import current
|
from globals import current
|
||||||
@@ -97,9 +95,7 @@ from compileapp import build_environment, run_models_in, \
|
|||||||
run_controller_in, run_view_in
|
run_controller_in, run_view_in
|
||||||
from contenttype import contenttype
|
from contenttype import contenttype
|
||||||
from dal import BaseAdapter
|
from dal import BaseAdapter
|
||||||
from settings import global_settings
|
|
||||||
from validators import CRYPT
|
from validators import CRYPT
|
||||||
from cache import CacheInRam
|
|
||||||
from html import URL, xmlescape
|
from html import URL, xmlescape
|
||||||
from utils import is_valid_ip_address, getipaddrinfo
|
from utils import is_valid_ip_address, getipaddrinfo
|
||||||
from rewrite import load, url_in, THREAD_LOCAL as rwthread, \
|
from rewrite import load, url_in, THREAD_LOCAL as rwthread, \
|
||||||
@@ -143,10 +139,11 @@ def get_client(env):
|
|||||||
first tries 'http_x_forwarded_for', secondly 'remote_addr'
|
first tries 'http_x_forwarded_for', secondly 'remote_addr'
|
||||||
if all fails, assume '127.0.0.1' or '::1' (running locally)
|
if all fails, assume '127.0.0.1' or '::1' (running locally)
|
||||||
"""
|
"""
|
||||||
g = regex_client.search(env.get('http_x_forwarded_for', ''))
|
eget = env.get
|
||||||
|
g = regex_client.search(eget('http_x_forwarded_for', ''))
|
||||||
client = (g.group() or '').split(',')[0] if g else None
|
client = (g.group() or '').split(',')[0] if g else None
|
||||||
if client in (None, '', 'unknown'):
|
if client in (None, '', 'unknown'):
|
||||||
g = regex_client.search(env.get('remote_addr', ''))
|
g = regex_client.search(eget('remote_addr', ''))
|
||||||
if g:
|
if g:
|
||||||
client = g.group()
|
client = g.group()
|
||||||
elif env.http_host.startswith('['): # IPv6
|
elif env.http_host.startswith('['): # IPv6
|
||||||
@@ -158,51 +155,6 @@ def get_client(env):
|
|||||||
return client
|
return client
|
||||||
|
|
||||||
|
|
||||||
def copystream_progress(request, chunk_size=10 ** 5):
|
|
||||||
"""
|
|
||||||
copies request.env.wsgi_input into request.body
|
|
||||||
and stores progress upload status in cache_ram
|
|
||||||
X-Progress-ID:length and X-Progress-ID:uploaded
|
|
||||||
"""
|
|
||||||
env = request.env
|
|
||||||
if not env.content_length:
|
|
||||||
return cStringIO.StringIO()
|
|
||||||
source = env.wsgi_input
|
|
||||||
try:
|
|
||||||
size = int(env.content_length)
|
|
||||||
except ValueError:
|
|
||||||
raise HTTP(400, "Invalid Content-Length header")
|
|
||||||
try: # Android requires this
|
|
||||||
dest = tempfile.NamedTemporaryFile()
|
|
||||||
except NotImplementedError: # and GAE this
|
|
||||||
dest = tempfile.TemporaryFile()
|
|
||||||
if not 'X-Progress-ID' in request.vars:
|
|
||||||
copystream(source, dest, size, chunk_size)
|
|
||||||
return dest
|
|
||||||
cache_key = 'X-Progress-ID:' + request.vars['X-Progress-ID']
|
|
||||||
cache_ram = CacheInRam(request) # same as cache.ram because meta_storage
|
|
||||||
cache_ram(cache_key + ':length', lambda: size, 0)
|
|
||||||
cache_ram(cache_key + ':uploaded', lambda: 0, 0)
|
|
||||||
while size > 0:
|
|
||||||
if size < chunk_size:
|
|
||||||
data = source.read(size)
|
|
||||||
cache_ram.increment(cache_key + ':uploaded', size)
|
|
||||||
else:
|
|
||||||
data = source.read(chunk_size)
|
|
||||||
cache_ram.increment(cache_key + ':uploaded', chunk_size)
|
|
||||||
length = len(data)
|
|
||||||
if length > size:
|
|
||||||
(data, length) = (data[:size], size)
|
|
||||||
size -= length
|
|
||||||
if length == 0:
|
|
||||||
break
|
|
||||||
dest.write(data)
|
|
||||||
if length < chunk_size:
|
|
||||||
break
|
|
||||||
dest.seek(0)
|
|
||||||
cache_ram(cache_key + ':length', None)
|
|
||||||
cache_ram(cache_key + ':uploaded', None)
|
|
||||||
return dest
|
|
||||||
|
|
||||||
|
|
||||||
def serve_controller(request, response, session):
|
def serve_controller(request, response, session):
|
||||||
@@ -292,7 +244,7 @@ class LazyWSGI(object):
|
|||||||
self.response.headers = dict(headers)
|
self.response.headers = dict(headers)
|
||||||
return lambda *args, **kargs: \
|
return lambda *args, **kargs: \
|
||||||
self.response.write(escape=False, *args, **kargs)
|
self.response.write(escape=False, *args, **kargs)
|
||||||
def middleware(self,*a):
|
def middleware(self,*middleware_apps):
|
||||||
"""
|
"""
|
||||||
In you controller use::
|
In you controller use::
|
||||||
|
|
||||||
@@ -316,95 +268,6 @@ class LazyWSGI(object):
|
|||||||
return lambda caller=caller, app=app: caller(app)
|
return lambda caller=caller, app=app: caller(app)
|
||||||
return middleware
|
return middleware
|
||||||
|
|
||||||
ISLE25 = sys.version_info[1] <= 5
|
|
||||||
|
|
||||||
def parse_get_post_vars(request, environ):
|
|
||||||
|
|
||||||
# always parse variables in URL for GET, POST, PUT, DELETE, etc. in get_vars
|
|
||||||
env = request.env
|
|
||||||
dget = cgi.parse_qsl(env.query_string or '', keep_blank_values=1)
|
|
||||||
for (key, value) in dget:
|
|
||||||
if key in request.get_vars:
|
|
||||||
if isinstance(request.get_vars[key], list):
|
|
||||||
request.get_vars[key] += [value]
|
|
||||||
else:
|
|
||||||
request.get_vars[key] = [request.get_vars[key]] + [value]
|
|
||||||
else:
|
|
||||||
request.get_vars[key] = value
|
|
||||||
request.vars[key] = request.get_vars[key]
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
request.body = body = copystream_progress(request)
|
|
||||||
except IOError:
|
|
||||||
raise HTTP(400, "Bad Request - HTTP body is incomplete")
|
|
||||||
|
|
||||||
#if content-type is application/json, we must read the body
|
|
||||||
is_json = env.get('http_content_type', '')[:16] == 'application/json'
|
|
||||||
|
|
||||||
|
|
||||||
if is_json:
|
|
||||||
try:
|
|
||||||
json_vars = sj.load(body)
|
|
||||||
body.seek(0)
|
|
||||||
except:
|
|
||||||
# incoherent request bodies can still be parsed "ad-hoc"
|
|
||||||
json_vars = {}
|
|
||||||
pass
|
|
||||||
# update vars and get_vars with what was posted as json
|
|
||||||
if isinstance(json_vars,dict):
|
|
||||||
request.get_vars.update(json_vars)
|
|
||||||
request.vars.update(json_vars)
|
|
||||||
|
|
||||||
|
|
||||||
# parse POST variables on POST, PUT, BOTH only in post_vars
|
|
||||||
if (body and env.request_method in ('POST', 'PUT', 'DELETE', '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/'
|
|
||||||
body.seek(0)
|
|
||||||
|
|
||||||
|
|
||||||
def listify(a):
|
|
||||||
return (not isinstance(a, list) and [a]) or a
|
|
||||||
try:
|
|
||||||
keys = sorted(dpost)
|
|
||||||
except TypeError:
|
|
||||||
keys = []
|
|
||||||
for key in keys:
|
|
||||||
if key is None:
|
|
||||||
continue # not sure why cgi.FieldStorage returns None key
|
|
||||||
dpk = dpost[key]
|
|
||||||
# if en element is not a file replace it with its value else leave it alone
|
|
||||||
if isinstance(dpk, list):
|
|
||||||
value = []
|
|
||||||
for _dpk in dpk:
|
|
||||||
if not _dpk.filename:
|
|
||||||
value.append(_dpk.value)
|
|
||||||
else:
|
|
||||||
value.append(_dpk)
|
|
||||||
elif not dpk.filename:
|
|
||||||
value = dpk.value
|
|
||||||
else:
|
|
||||||
value = dpk
|
|
||||||
pvalue = listify(value)
|
|
||||||
if key in request.vars:
|
|
||||||
gvalue = listify(request.vars[key])
|
|
||||||
if ISLE25:
|
|
||||||
value = pvalue + gvalue
|
|
||||||
elif is_multipart:
|
|
||||||
pvalue = pvalue[len(gvalue):]
|
|
||||||
else:
|
|
||||||
pvalue = pvalue[:-len(gvalue)]
|
|
||||||
request.vars[key] = value
|
|
||||||
if len(pvalue):
|
|
||||||
request.post_vars[key] = (len(pvalue) >
|
|
||||||
1 and pvalue) or pvalue[0]
|
|
||||||
if is_json and isinstance(json_vars,dict):
|
|
||||||
# update post_vars with what was posted as json
|
|
||||||
request.post_vars.update(json_vars)
|
|
||||||
|
|
||||||
|
|
||||||
def wsgibase(environ, responder):
|
def wsgibase(environ, responder):
|
||||||
"""
|
"""
|
||||||
this is the gluon wsgi application. the first function called when a page
|
this is the gluon wsgi application. the first function called when a page
|
||||||
@@ -435,15 +298,15 @@ def wsgibase(environ, responder):
|
|||||||
[a-zA-Z0-9_]
|
[a-zA-Z0-9_]
|
||||||
- file and sub may also contain '-', '=', '.' and '/'
|
- file and sub may also contain '-', '=', '.' and '/'
|
||||||
"""
|
"""
|
||||||
|
eget = environ.get
|
||||||
current.__dict__.clear()
|
current.__dict__.clear()
|
||||||
request = Request()
|
request = Request(environ)
|
||||||
response = Response()
|
response = Response()
|
||||||
session = Session()
|
session = Session()
|
||||||
env = request.env
|
env = request.env
|
||||||
env.web2py_path = global_settings.applications_parent
|
#env.web2py_path = global_settings.applications_parent
|
||||||
env.web2py_version = web2py_version
|
env.web2py_version = web2py_version
|
||||||
env.update(global_settings)
|
#env.update(global_settings)
|
||||||
static_file = False
|
static_file = False
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
@@ -462,8 +325,7 @@ def wsgibase(environ, responder):
|
|||||||
response.status = env.web2py_status_code or response.status
|
response.status = env.web2py_status_code or response.status
|
||||||
|
|
||||||
if static_file:
|
if static_file:
|
||||||
if environ.get('QUERY_STRING', '').startswith(
|
if eget('QUERY_STRING', '').startswith('attachment'):
|
||||||
'attachment'):
|
|
||||||
response.headers['Content-Disposition'] \
|
response.headers['Content-Disposition'] \
|
||||||
= 'attachment'
|
= 'attachment'
|
||||||
if version:
|
if version:
|
||||||
@@ -472,6 +334,7 @@ def wsgibase(environ, responder):
|
|||||||
'Expires'] = 'Thu, 31 Dec 2037 23:59:59 GMT'
|
'Expires'] = 'Thu, 31 Dec 2037 23:59:59 GMT'
|
||||||
response.stream(static_file, request=request)
|
response.stream(static_file, request=request)
|
||||||
|
|
||||||
|
|
||||||
# ##################################################
|
# ##################################################
|
||||||
# fill in request items
|
# fill in request items
|
||||||
# ##################################################
|
# ##################################################
|
||||||
@@ -508,7 +371,8 @@ def wsgibase(environ, responder):
|
|||||||
is_local = env.remote_addr in local_hosts,
|
is_local = env.remote_addr in local_hosts,
|
||||||
is_https = env.wsgi_url_scheme in HTTPS_SCHEMES or \
|
is_https = env.wsgi_url_scheme in HTTPS_SCHEMES or \
|
||||||
request.env.http_x_forwarded_proto in HTTPS_SCHEMES \
|
request.env.http_x_forwarded_proto in HTTPS_SCHEMES \
|
||||||
or env.https == 'on')
|
or env.https == 'on'
|
||||||
|
)
|
||||||
request.compute_uuid() # requires client
|
request.compute_uuid() # requires client
|
||||||
request.url = environ['PATH_INFO']
|
request.url = environ['PATH_INFO']
|
||||||
|
|
||||||
@@ -516,6 +380,7 @@ def wsgibase(environ, responder):
|
|||||||
# access the requested application
|
# access the requested application
|
||||||
# ##################################################
|
# ##################################################
|
||||||
|
|
||||||
|
disabled = pjoin(request.folder, 'DISABLED')
|
||||||
if not exists(request.folder):
|
if not exists(request.folder):
|
||||||
if app == rwthread.routes.default_application \
|
if app == rwthread.routes.default_application \
|
||||||
and app != 'welcome':
|
and app != 'welcome':
|
||||||
@@ -530,9 +395,15 @@ def wsgibase(environ, responder):
|
|||||||
raise HTTP(404, rwthread.routes.error_message
|
raise HTTP(404, rwthread.routes.error_message
|
||||||
% 'invalid request',
|
% 'invalid request',
|
||||||
web2py_error='invalid application')
|
web2py_error='invalid application')
|
||||||
elif not request.is_local and \
|
elif request.is_local and exists(disabled):
|
||||||
exists(pjoin(request.folder, 'DISABLED')):
|
data = dict([item.strip() for item in line.split(':',1)]
|
||||||
raise HTTP(503, "<html><body><h1>Temporarily down for maintenance</h1></body></html>")
|
for line in open(disabled) if line.strip())
|
||||||
|
if data.get('disabled','True').lower() != 'false':
|
||||||
|
if 'redirect' in data:
|
||||||
|
redirect(data['redirect'])
|
||||||
|
if 'message' in data:
|
||||||
|
raise HTTP(503, data['message'])
|
||||||
|
raise HTTP(503, "<html><body><h1>Temporarily down for maintenance</h1></body></html>")
|
||||||
|
|
||||||
# ##################################################
|
# ##################################################
|
||||||
# build missing folders
|
# build missing folders
|
||||||
@@ -544,7 +415,7 @@ def wsgibase(environ, responder):
|
|||||||
# get the GET and POST data
|
# get the GET and POST data
|
||||||
# ##################################################
|
# ##################################################
|
||||||
|
|
||||||
parse_get_post_vars(request, environ)
|
#parse_get_post_vars(request, environ)
|
||||||
|
|
||||||
# ##################################################
|
# ##################################################
|
||||||
# expose wsgi hooks for convenience
|
# expose wsgi hooks for convenience
|
||||||
@@ -643,11 +514,17 @@ def wsgibase(environ, responder):
|
|||||||
# on application error, rollback database
|
# on application error, rollback database
|
||||||
# ##################################################
|
# ##################################################
|
||||||
|
|
||||||
ticket = e.log(request) or 'unknown'
|
# log tickets before rollback if not in DB
|
||||||
|
if not request.tickets_db:
|
||||||
|
ticket = e.log(request) or 'unknown'
|
||||||
|
# rollback
|
||||||
if response._custom_rollback:
|
if response._custom_rollback:
|
||||||
response._custom_rollback()
|
response._custom_rollback()
|
||||||
else:
|
else:
|
||||||
BaseAdapter.close_all_instances('rollback')
|
BaseAdapter.close_all_instances('rollback')
|
||||||
|
# if tickets in db, reconnect and store it in db
|
||||||
|
if request.tickets_db:
|
||||||
|
ticket = e.log(request) or 'unknown'
|
||||||
|
|
||||||
http_response = \
|
http_response = \
|
||||||
HTTP(500, rwthread.routes.error_message_ticket %
|
HTTP(500, rwthread.routes.error_message_ticket %
|
||||||
|
|||||||
+16
-11
@@ -48,11 +48,19 @@ class TicketStorage(Storage):
|
|||||||
self._store_on_disk(request, ticket_id, ticket_data)
|
self._store_on_disk(request, ticket_id, ticket_data)
|
||||||
|
|
||||||
def _store_in_db(self, request, ticket_id, ticket_data):
|
def _store_in_db(self, request, ticket_id, ticket_data):
|
||||||
table = self._get_table(self.db, self.tablename, request.application)
|
self.db._adapter.reconnect()
|
||||||
table.insert(ticket_id=ticket_id,
|
try:
|
||||||
ticket_data=cPickle.dumps(ticket_data),
|
table = self._get_table(self.db, self.tablename, request.application)
|
||||||
created_datetime=request.now)
|
id = table.insert(ticket_id=ticket_id,
|
||||||
logger.error('In FILE: %(layer)s\n\n%(traceback)s\n' % ticket_data)
|
ticket_data=cPickle.dumps(ticket_data),
|
||||||
|
created_datetime=request.now)
|
||||||
|
self.db.commit()
|
||||||
|
message = 'In FILE: %(layer)s\n\n%(traceback)s\n'
|
||||||
|
except Exception, e:
|
||||||
|
self.db.rollback()
|
||||||
|
message =' Unable to store in FILE: %(layer)s\n\n%(traceback)s\n'
|
||||||
|
self.db.close()
|
||||||
|
logger.error(message % ticket_data)
|
||||||
|
|
||||||
def _store_on_disk(self, request, ticket_id, ticket_data):
|
def _store_on_disk(self, request, ticket_id, ticket_data):
|
||||||
ef = self._error_file(request, ticket_id, 'wb')
|
ef = self._error_file(request, ticket_id, 'wb')
|
||||||
@@ -71,16 +79,13 @@ class TicketStorage(Storage):
|
|||||||
|
|
||||||
def _get_table(self, db, tablename, app):
|
def _get_table(self, db, tablename, app):
|
||||||
tablename = tablename + '_' + app
|
tablename = tablename + '_' + app
|
||||||
table = db.get(tablename, None)
|
table = db.get(tablename)
|
||||||
if table is None:
|
if not table:
|
||||||
db.rollback() # not necessary but one day
|
|
||||||
# any app may store tickets on DB
|
|
||||||
table = db.define_table(
|
table = db.define_table(
|
||||||
tablename,
|
tablename,
|
||||||
db.Field('ticket_id', length=100),
|
db.Field('ticket_id', length=100),
|
||||||
db.Field('ticket_data', 'text'),
|
db.Field('ticket_data', 'text'),
|
||||||
db.Field('created_datetime', 'datetime'),
|
db.Field('created_datetime', 'datetime'))
|
||||||
)
|
|
||||||
return table
|
return table
|
||||||
|
|
||||||
def load(
|
def load(
|
||||||
|
|||||||
@@ -36,3 +36,6 @@ global_settings.is_jython = \
|
|||||||
'java' in sys.platform.lower() or \
|
'java' in sys.platform.lower() or \
|
||||||
hasattr(sys, 'JYTHON_JAR') or \
|
hasattr(sys, 'JYTHON_JAR') or \
|
||||||
str(sys.copyright).find('Jython') > 0
|
str(sys.copyright).find('Jython') > 0
|
||||||
|
|
||||||
|
global_settings.is_source = os.path.exists(os.path.join(
|
||||||
|
global_settings.gluon_parent,'web2py.py'))
|
||||||
|
|||||||
+48
-21
@@ -24,7 +24,7 @@ from utils import web2py_uuid
|
|||||||
from compileapp import build_environment, read_pyc, run_models_in
|
from compileapp import build_environment, read_pyc, run_models_in
|
||||||
from restricted import RestrictedError
|
from restricted import RestrictedError
|
||||||
from globals import Request, Response, Session
|
from globals import Request, Response, Session
|
||||||
from storage import Storage
|
from storage import Storage, List
|
||||||
from admin import w2p_unpack
|
from admin import w2p_unpack
|
||||||
from dal import BaseAdapter
|
from dal import BaseAdapter
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ def exec_environment(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if request is None:
|
if request is None:
|
||||||
request = Request()
|
request = Request({})
|
||||||
if response is None:
|
if response is None:
|
||||||
response = Response()
|
response = Response()
|
||||||
if session is None:
|
if session is None:
|
||||||
@@ -116,7 +116,7 @@ def env(
|
|||||||
web2py environment.
|
web2py environment.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
request = Request()
|
request = Request({})
|
||||||
response = Response()
|
response = Response()
|
||||||
session = Session()
|
session = Session()
|
||||||
request.application = a
|
request.application = a
|
||||||
@@ -131,7 +131,6 @@ def env(
|
|||||||
request.function = f or 'index'
|
request.function = f or 'index'
|
||||||
response.view = '%s/%s.html' % (request.controller,
|
response.view = '%s/%s.html' % (request.controller,
|
||||||
request.function)
|
request.function)
|
||||||
request.env.path_info = '/%s/%s/%s' % (a, c, f)
|
|
||||||
request.env.http_host = '127.0.0.1:8000'
|
request.env.http_host = '127.0.0.1:8000'
|
||||||
request.env.remote_addr = '127.0.0.1'
|
request.env.remote_addr = '127.0.0.1'
|
||||||
request.env.web2py_runtime_gae = global_settings.web2py_runtime_gae
|
request.env.web2py_runtime_gae = global_settings.web2py_runtime_gae
|
||||||
@@ -139,6 +138,15 @@ def env(
|
|||||||
for k, v in extra_request.items():
|
for k, v in extra_request.items():
|
||||||
request[k] = v
|
request[k] = v
|
||||||
|
|
||||||
|
path_info = '/%s/%s/%s' % (a, c, f)
|
||||||
|
if request.args:
|
||||||
|
path_info = '%s/%s' % (path_info, '/'.join(request.args))
|
||||||
|
if request.vars:
|
||||||
|
vars = ['%s=%s' % (k,v) if v else '%s' % k
|
||||||
|
for (k,v) in request.vars.iteritems()]
|
||||||
|
path_info = '%s?%s' % (path_info, '&'.join(vars))
|
||||||
|
request.env.path_info = path_info
|
||||||
|
|
||||||
# Monkey patch so credentials checks pass.
|
# Monkey patch so credentials checks pass.
|
||||||
|
|
||||||
def check_credentials(request, other_application='admin'):
|
def check_credentials(request, other_application='admin'):
|
||||||
@@ -178,7 +186,8 @@ def run(
|
|||||||
import_models=False,
|
import_models=False,
|
||||||
startfile=None,
|
startfile=None,
|
||||||
bpython=False,
|
bpython=False,
|
||||||
python_code=False):
|
python_code=False,
|
||||||
|
cronjob=False):
|
||||||
"""
|
"""
|
||||||
Start interactive shell or run Python script (startfile) in web2py
|
Start interactive shell or run Python script (startfile) in web2py
|
||||||
controller environment. appname is formatted like:
|
controller environment. appname is formatted like:
|
||||||
@@ -187,7 +196,7 @@ def run(
|
|||||||
a/c exec the controller c into the application environment
|
a/c exec the controller c into the application environment
|
||||||
"""
|
"""
|
||||||
|
|
||||||
(a, c, f) = parse_path_info(appname)
|
(a, c, f, args, vars) = parse_path_info(appname, av=True)
|
||||||
errmsg = 'invalid application name: %s' % appname
|
errmsg = 'invalid application name: %s' % appname
|
||||||
if not a:
|
if not a:
|
||||||
die(errmsg)
|
die(errmsg)
|
||||||
@@ -219,18 +228,23 @@ def run(
|
|||||||
|
|
||||||
if c:
|
if c:
|
||||||
import_models = True
|
import_models = True
|
||||||
_env = env(a, c=c, f=f, import_models=import_models)
|
extra_request = {}
|
||||||
|
if args:
|
||||||
|
extra_request['args'] = args
|
||||||
|
if vars:
|
||||||
|
extra_request['vars'] = vars
|
||||||
|
_env = env(a, c=c, f=f, import_models=import_models, extra_request=extra_request)
|
||||||
if c:
|
if c:
|
||||||
cfile = os.path.join('applications', a, 'controllers', c + '.py')
|
pyfile = os.path.join('applications', a, 'controllers', c + '.py')
|
||||||
if not os.path.isfile(cfile):
|
pycfile = os.path.join('applications', a, 'compiled',
|
||||||
cfile = os.path.join('applications', a, 'compiled',
|
|
||||||
"controllers_%s_%s.pyc" % (c, f))
|
"controllers_%s_%s.pyc" % (c, f))
|
||||||
if not os.path.isfile(cfile):
|
if ((cronjob and os.path.isfile(pycfile))
|
||||||
die(errmsg)
|
or not os.path.isfile(pyfile)):
|
||||||
else:
|
exec read_pyc(pycfile) in _env
|
||||||
exec read_pyc(cfile) in _env
|
elif os.path.isfile(pyfile):
|
||||||
|
execfile(pyfile, _env)
|
||||||
else:
|
else:
|
||||||
execfile(cfile, _env)
|
die(errmsg)
|
||||||
|
|
||||||
if f:
|
if f:
|
||||||
exec ('print %s()' % f, _env)
|
exec ('print %s()' % f, _env)
|
||||||
@@ -294,13 +308,25 @@ def run(
|
|||||||
code.interact(local=_env)
|
code.interact(local=_env)
|
||||||
|
|
||||||
|
|
||||||
def parse_path_info(path_info):
|
def parse_path_info(path_info, av=False):
|
||||||
"""
|
"""
|
||||||
Parse path info formatted like a/c/f where c and f are optional
|
Parse path info formatted like a/c/f where c and f are optional
|
||||||
and a leading / accepted.
|
and a leading / accepted.
|
||||||
Return tuple (a, c, f). If invalid path_info a is set to None.
|
Return tuple (a, c, f). If invalid path_info a is set to None.
|
||||||
If c or f are omitted they are set to None.
|
If c or f are omitted they are set to None.
|
||||||
|
If av=True, parse args and vars
|
||||||
"""
|
"""
|
||||||
|
if av:
|
||||||
|
vars = None
|
||||||
|
if '?' in path_info:
|
||||||
|
path_info, query = path_info.split('?', 2)
|
||||||
|
vars = Storage()
|
||||||
|
for var in query.split('&'):
|
||||||
|
(var, val) = var.split('=', 2) if '=' in var else (var, None)
|
||||||
|
vars[var] = val
|
||||||
|
items = List(path_info.split('/'))
|
||||||
|
args = List(items[3:]) if len(items) > 3 else None
|
||||||
|
return (items(0), items(1), items(2), args, vars)
|
||||||
|
|
||||||
mo = re.match(r'^/?(?P<a>\w+)(/(?P<c>\w+)(/(?P<f>\w+))?)?$',
|
mo = re.match(r'^/?(?P<a>\w+)(/(?P<c>\w+)(/(?P<f>\w+))?)?$',
|
||||||
path_info)
|
path_info)
|
||||||
@@ -368,9 +394,10 @@ def test(testpath, import_models=True, verbose=False):
|
|||||||
|
|
||||||
globs = env(a, c=c, f=f, import_models=import_models)
|
globs = env(a, c=c, f=f, import_models=import_models)
|
||||||
execfile(testfile, globs)
|
execfile(testfile, globs)
|
||||||
doctest.run_docstring_examples(obj, globs=globs,
|
doctest.run_docstring_examples(
|
||||||
name='%s: %s' % (os.path.basename(testfile),
|
obj, globs=globs,
|
||||||
name), verbose=verbose)
|
name='%s: %s' % (os.path.basename(testfile),
|
||||||
|
name), verbose=verbose)
|
||||||
if type(obj) in (types.TypeType, types.ClassType):
|
if type(obj) in (types.TypeType, types.ClassType):
|
||||||
for attr_name in dir(obj):
|
for attr_name in dir(obj):
|
||||||
|
|
||||||
@@ -398,8 +425,8 @@ def execute_from_command_line(argv=None):
|
|||||||
parser = optparse.OptionParser(usage=get_usage())
|
parser = optparse.OptionParser(usage=get_usage())
|
||||||
|
|
||||||
parser.add_option('-S', '--shell', dest='shell', metavar='APPNAME',
|
parser.add_option('-S', '--shell', dest='shell', metavar='APPNAME',
|
||||||
help='run web2py in interactive shell or IPython(if installed) ' +
|
help='run web2py in interactive shell ' +
|
||||||
'with specified appname')
|
'or IPython(if installed) with specified appname')
|
||||||
msg = 'run web2py in interactive shell or bpython (if installed) with'
|
msg = 'run web2py in interactive shell or bpython (if installed) with'
|
||||||
msg += ' specified appname (if app does not exist it will be created).'
|
msg += ' specified appname (if app does not exist it will be created).'
|
||||||
msg += '\n Use combined with --shell'
|
msg += '\n Use combined with --shell'
|
||||||
|
|||||||
+17
-10
@@ -26,7 +26,7 @@ from html import FORM, INPUT, LABEL, OPTION, SELECT
|
|||||||
from html import TABLE, THEAD, TBODY, TR, TD, TH, STYLE
|
from html import TABLE, THEAD, TBODY, TR, TD, TH, STYLE
|
||||||
from html import URL, truncate_string, FIELDSET
|
from html import URL, truncate_string, FIELDSET
|
||||||
from dal import DAL, Field, Table, Row, CALLABLETYPES, smart_query, \
|
from dal import DAL, Field, Table, Row, CALLABLETYPES, smart_query, \
|
||||||
bar_encode, Reference, REGEX_TABLE_DOT_FIELD
|
bar_encode, Reference, REGEX_TABLE_DOT_FIELD, Expression
|
||||||
from storage import Storage
|
from storage import Storage
|
||||||
from utils import md5_hash
|
from utils import md5_hash
|
||||||
from validators import IS_EMPTY_OR, IS_NOT_EMPTY, IS_LIST_OF, IS_DATE, \
|
from validators import IS_EMPTY_OR, IS_NOT_EMPTY, IS_LIST_OF, IS_DATE, \
|
||||||
@@ -1454,7 +1454,7 @@ class SQLFORM(FORM):
|
|||||||
(cStringIO.StringIO(f), 'file.txt')
|
(cStringIO.StringIO(f), 'file.txt')
|
||||||
else:
|
else:
|
||||||
# this should never happen, why does it happen?
|
# this should never happen, why does it happen?
|
||||||
print 'f=',repr(f)
|
#print 'f=',repr(f)
|
||||||
continue
|
continue
|
||||||
newfilename = field.store(source_file, original_filename,
|
newfilename = field.store(source_file, original_filename,
|
||||||
field.uploadfolder)
|
field.uploadfolder)
|
||||||
@@ -1822,10 +1822,11 @@ class SQLFORM(FORM):
|
|||||||
session = current.session
|
session = current.session
|
||||||
response = current.response
|
response = current.response
|
||||||
logged = session.auth and session.auth.user
|
logged = session.auth and session.auth.user
|
||||||
wenabled = (not user_signature or logged)
|
wenabled = (not user_signature or logged) and not groupby
|
||||||
create = wenabled and create
|
create = wenabled and create
|
||||||
editable = wenabled and editable
|
editable = wenabled and editable
|
||||||
deletable = wenabled and deletable
|
deletable = wenabled and deletable
|
||||||
|
details = details and not groupby
|
||||||
rows = None
|
rows = None
|
||||||
|
|
||||||
def fetch_count(dbset):
|
def fetch_count(dbset):
|
||||||
@@ -1835,9 +1836,10 @@ class SQLFORM(FORM):
|
|||||||
if groupby:
|
if groupby:
|
||||||
c = 'count(*)'
|
c = 'count(*)'
|
||||||
nrows = db.executesql(
|
nrows = db.executesql(
|
||||||
'select count(*) from (%s);' %
|
'select count(*) from (%s) _tmp;' %
|
||||||
dbset._select(c, left=left, cacheable=True,
|
dbset._select(c, left=left, cacheable=True,
|
||||||
groupby=groupby, cache=cache_count)[:-1])[0][0]
|
groupby=groupby,
|
||||||
|
cache=cache_count)[:-1])[0][0]
|
||||||
elif left:
|
elif left:
|
||||||
c = 'count(*)'
|
c = 'count(*)'
|
||||||
nrows = dbset.select(c, left=left, cacheable=True, cache=cache_count).first()[c]
|
nrows = dbset.select(c, left=left, cacheable=True, cache=cache_count).first()[c]
|
||||||
@@ -1930,11 +1932,16 @@ class SQLFORM(FORM):
|
|||||||
columns.append(f)
|
columns.append(f)
|
||||||
fields.append(f)
|
fields.append(f)
|
||||||
if not field_id:
|
if not field_id:
|
||||||
field_id = tables[0]._id
|
if groupby is None:
|
||||||
if not any(str(f)==str(field_id) for f in fields):
|
field_id = tables[0]._id
|
||||||
fields = [f for f in fields]+[field_id]
|
elif groupby and isinstance(groupby, Field):
|
||||||
|
field_id = groupby #take the field passed as groupby
|
||||||
|
elif groupby and isinstance(groupby, Expression):
|
||||||
|
field_id = groupby.first #take the first groupby field
|
||||||
table = field_id.table
|
table = field_id.table
|
||||||
tablename = table._tablename
|
tablename = table._tablename
|
||||||
|
if not any(str(f)==str(field_id) for f in fields):
|
||||||
|
fields = [f for f in fields]+[field_id]
|
||||||
if upload == '<default>':
|
if upload == '<default>':
|
||||||
upload = lambda filename: url(args=['download', filename])
|
upload = lambda filename: url(args=['download', filename])
|
||||||
if request.args(-2) == 'download':
|
if request.args(-2) == 'download':
|
||||||
@@ -2175,8 +2182,8 @@ class SQLFORM(FORM):
|
|||||||
order = request.vars.order or ''
|
order = request.vars.order or ''
|
||||||
if sortable:
|
if sortable:
|
||||||
if order and not order == 'None':
|
if order and not order == 'None':
|
||||||
tablename, fieldname = order.split('~')[-1].split('.', 1)
|
otablename, ofieldname = order.split('~')[-1].split('.', 1)
|
||||||
sort_field = db[tablename][fieldname]
|
sort_field = db[otablename][ofieldname]
|
||||||
exception = sort_field.type in ('date', 'datetime', 'time')
|
exception = sort_field.type in ('date', 'datetime', 'time')
|
||||||
if exception:
|
if exception:
|
||||||
orderby = (order[:1] == '~' and sort_field) or ~sort_field
|
orderby = (order[:1] == '~' and sort_field) or ~sort_field
|
||||||
|
|||||||
+67
-24
@@ -1154,9 +1154,6 @@ class Auth(object):
|
|||||||
del session.auth
|
del session.auth
|
||||||
# ## what happens after login?
|
# ## 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_index = URL(controller, 'index')
|
||||||
url_login = URL(controller, function, args='login')
|
url_login = URL(controller, function, args='login')
|
||||||
# ## what happens after registration?
|
# ## what happens after registration?
|
||||||
@@ -1235,6 +1232,12 @@ class Auth(object):
|
|||||||
else:
|
else:
|
||||||
self.signature = None
|
self.signature = None
|
||||||
|
|
||||||
|
def get_vars_next(self):
|
||||||
|
next = current.request.vars._next
|
||||||
|
if isinstance(next, (list, tuple)):
|
||||||
|
next = next[0]
|
||||||
|
return next
|
||||||
|
|
||||||
def _get_user_id(self):
|
def _get_user_id(self):
|
||||||
"accessor for auth.user_id"
|
"accessor for auth.user_id"
|
||||||
return self.user and self.user.id or None
|
return self.user and self.user.id or None
|
||||||
@@ -1756,7 +1759,8 @@ class Auth(object):
|
|||||||
description=str(description % vars),
|
description=str(description % vars),
|
||||||
origin=origin, user_id=user_id)
|
origin=origin, user_id=user_id)
|
||||||
|
|
||||||
def get_or_create_user(self, keys, update_fields=['email'], login=True):
|
def get_or_create_user(self, keys, update_fields=['email'],
|
||||||
|
login=True, get=True):
|
||||||
"""
|
"""
|
||||||
Used for alternate login methods:
|
Used for alternate login methods:
|
||||||
If the user exists already then password is updated.
|
If the user exists already then password is updated.
|
||||||
@@ -1786,6 +1790,9 @@ class Auth(object):
|
|||||||
and ('registration_id' not in keys or user.registration_id != str(keys['registration_id'])):
|
and ('registration_id' not in keys or user.registration_id != str(keys['registration_id'])):
|
||||||
user = None # THINK MORE ABOUT THIS? DO WE TRUST OPENID PROVIDER?
|
user = None # THINK MORE ABOUT THIS? DO WE TRUST OPENID PROVIDER?
|
||||||
if user:
|
if user:
|
||||||
|
if not get:
|
||||||
|
# added for register_bare to avoid overwriting users
|
||||||
|
return None
|
||||||
update_keys = dict(registration_id=keys['registration_id'])
|
update_keys = dict(registration_id=keys['registration_id'])
|
||||||
for key in update_fields:
|
for key in update_fields:
|
||||||
if key in keys:
|
if key in keys:
|
||||||
@@ -1874,10 +1881,7 @@ class Auth(object):
|
|||||||
self.user = user
|
self.user = user
|
||||||
self.update_groups()
|
self.update_groups()
|
||||||
|
|
||||||
def login_bare(self, username, password):
|
def _get_login_settings(self):
|
||||||
"""
|
|
||||||
logins user as specified by usernname (or email) and password
|
|
||||||
"""
|
|
||||||
table_user = self.table_user()
|
table_user = self.table_user()
|
||||||
if self.settings.login_userfield:
|
if self.settings.login_userfield:
|
||||||
userfield = self.settings.login_userfield
|
userfield = self.settings.login_userfield
|
||||||
@@ -1886,20 +1890,58 @@ class Auth(object):
|
|||||||
else:
|
else:
|
||||||
userfield = 'email'
|
userfield = 'email'
|
||||||
passfield = self.settings.password_field
|
passfield = self.settings.password_field
|
||||||
user = self.db(table_user[userfield] == username).select().first()
|
return Storage({"table_user": table_user,
|
||||||
if user and user.get(passfield, False):
|
"userfield": userfield,
|
||||||
password = table_user[passfield].validate(password)[0]
|
"passfield": passfield})
|
||||||
if not user.registration_key and password == user[passfield]:
|
|
||||||
|
def login_bare(self, username, password):
|
||||||
|
"""
|
||||||
|
logins user as specified by username (or email) and password
|
||||||
|
"""
|
||||||
|
settings = self._get_login_settings()
|
||||||
|
user = self.db(settings.table_user[settings.userfield] == \
|
||||||
|
username).select().first()
|
||||||
|
if user and user.get(settings.passfield, False):
|
||||||
|
password = settings.table_user[
|
||||||
|
settings.passfield].validate(password)[0]
|
||||||
|
if not user.registration_key and password == \
|
||||||
|
user[settings.passfield]:
|
||||||
self.login_user(user)
|
self.login_user(user)
|
||||||
return user
|
return user
|
||||||
else:
|
else:
|
||||||
# user not in database try other login methods
|
# user not in database try other login methods
|
||||||
for login_method in self.settings.login_methods:
|
for login_method in self.settings.login_methods:
|
||||||
if login_method != self and login_method(username, password):
|
if login_method != self and \
|
||||||
|
login_method(username, password):
|
||||||
self.user = username
|
self.user = username
|
||||||
return username
|
return username
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def register_bare(self, **fields):
|
||||||
|
"""
|
||||||
|
registers a user as specified by username (or email)
|
||||||
|
and a raw password.
|
||||||
|
"""
|
||||||
|
settings = self._get_login_settings()
|
||||||
|
if not fields.get(settings.passfield):
|
||||||
|
raise ValueError("register_bare: " +
|
||||||
|
"password not provided or invalid")
|
||||||
|
elif not fields.get(settings.userfield):
|
||||||
|
raise ValueError("register_bare: " +
|
||||||
|
"userfield not provided or invalid")
|
||||||
|
fields[settings.passfield
|
||||||
|
] = settings.table_user[settings.passfield].validate(
|
||||||
|
fields[settings.passfield])[0]
|
||||||
|
user = self.get_or_create_user(fields, login=False,
|
||||||
|
get=False,
|
||||||
|
update_fields=self.settings.update_fields)
|
||||||
|
if not user:
|
||||||
|
# get or create did not create a user (it ignores
|
||||||
|
# duplicate records)
|
||||||
|
return False
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
def cas_login(
|
def cas_login(
|
||||||
self,
|
self,
|
||||||
next=DEFAULT,
|
next=DEFAULT,
|
||||||
@@ -2039,16 +2081,17 @@ class Auth(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
### use session for federated login
|
### use session for federated login
|
||||||
if self.next:
|
snext = self.get_vars_next()
|
||||||
session._auth_next = self.next
|
if snext:
|
||||||
|
session._auth_next = snext
|
||||||
elif session._auth_next:
|
elif session._auth_next:
|
||||||
self.next = session._auth_next
|
snext = session._auth_next
|
||||||
### pass
|
### pass
|
||||||
|
|
||||||
if next is DEFAULT:
|
if next is DEFAULT:
|
||||||
# important for security
|
# important for security
|
||||||
next = self.settings.login_next
|
next = self.settings.login_next
|
||||||
user_next = self.next
|
user_next = snext
|
||||||
if user_next:
|
if user_next:
|
||||||
external = user_next.split('://')
|
external = user_next.split('://')
|
||||||
if external[0].lower() in ['http', 'https', 'ftp']:
|
if external[0].lower() in ['http', 'https', 'ftp']:
|
||||||
@@ -2292,7 +2335,7 @@ class Auth(object):
|
|||||||
redirect(self.settings.logged_url,
|
redirect(self.settings.logged_url,
|
||||||
client_side=self.settings.client_side)
|
client_side=self.settings.client_side)
|
||||||
if next is DEFAULT:
|
if next is DEFAULT:
|
||||||
next = self.next or self.settings.register_next
|
next = self.get_vars_next() or self.settings.register_next
|
||||||
if onvalidation is DEFAULT:
|
if onvalidation is DEFAULT:
|
||||||
onvalidation = self.settings.register_onvalidation
|
onvalidation = self.settings.register_onvalidation
|
||||||
if onaccept is DEFAULT:
|
if onaccept is DEFAULT:
|
||||||
@@ -2487,7 +2530,7 @@ class Auth(object):
|
|||||||
response.flash = self.messages.function_disabled
|
response.flash = self.messages.function_disabled
|
||||||
return ''
|
return ''
|
||||||
if next is DEFAULT:
|
if next is DEFAULT:
|
||||||
next = self.next or self.settings.retrieve_username_next
|
next = self.get_vars_next() or self.settings.retrieve_username_next
|
||||||
if onvalidation is DEFAULT:
|
if onvalidation is DEFAULT:
|
||||||
onvalidation = self.settings.retrieve_username_onvalidation
|
onvalidation = self.settings.retrieve_username_onvalidation
|
||||||
if onaccept is DEFAULT:
|
if onaccept is DEFAULT:
|
||||||
@@ -2569,7 +2612,7 @@ class Auth(object):
|
|||||||
response.flash = self.messages.function_disabled
|
response.flash = self.messages.function_disabled
|
||||||
return ''
|
return ''
|
||||||
if next is DEFAULT:
|
if next is DEFAULT:
|
||||||
next = self.next or self.settings.retrieve_password_next
|
next = self.get_vars_next() or self.settings.retrieve_password_next
|
||||||
if onvalidation is DEFAULT:
|
if onvalidation is DEFAULT:
|
||||||
onvalidation = self.settings.retrieve_password_onvalidation
|
onvalidation = self.settings.retrieve_password_onvalidation
|
||||||
if onaccept is DEFAULT:
|
if onaccept is DEFAULT:
|
||||||
@@ -2645,7 +2688,7 @@ class Auth(object):
|
|||||||
session = current.session
|
session = current.session
|
||||||
|
|
||||||
if next is DEFAULT:
|
if next is DEFAULT:
|
||||||
next = self.next or self.settings.reset_password_next
|
next = self.get_vars_next() or self.settings.reset_password_next
|
||||||
try:
|
try:
|
||||||
key = request.vars.key or getarg(-1)
|
key = request.vars.key or getarg(-1)
|
||||||
t0 = int(key.split('-')[0])
|
t0 = int(key.split('-')[0])
|
||||||
@@ -2706,7 +2749,7 @@ class Auth(object):
|
|||||||
(self.settings.retrieve_password_captcha != False and self.settings.captcha)
|
(self.settings.retrieve_password_captcha != False and self.settings.captcha)
|
||||||
|
|
||||||
if next is DEFAULT:
|
if next is DEFAULT:
|
||||||
next = self.next or self.settings.request_reset_password_next
|
next = self.get_vars_next() or self.settings.request_reset_password_next
|
||||||
if not self.settings.mailer:
|
if not self.settings.mailer:
|
||||||
response.flash = self.messages.function_disabled
|
response.flash = self.messages.function_disabled
|
||||||
return ''
|
return ''
|
||||||
@@ -2809,7 +2852,7 @@ class Auth(object):
|
|||||||
request = current.request
|
request = current.request
|
||||||
session = current.session
|
session = current.session
|
||||||
if next is DEFAULT:
|
if next is DEFAULT:
|
||||||
next = self.next or self.settings.change_password_next
|
next = self.get_vars_next() or self.settings.change_password_next
|
||||||
if onvalidation is DEFAULT:
|
if onvalidation is DEFAULT:
|
||||||
onvalidation = self.settings.change_password_onvalidation
|
onvalidation = self.settings.change_password_onvalidation
|
||||||
if onaccept is DEFAULT:
|
if onaccept is DEFAULT:
|
||||||
@@ -2878,7 +2921,7 @@ class Auth(object):
|
|||||||
request = current.request
|
request = current.request
|
||||||
session = current.session
|
session = current.session
|
||||||
if next is DEFAULT:
|
if next is DEFAULT:
|
||||||
next = self.next or self.settings.profile_next
|
next = self.get_vars_next() or self.settings.profile_next
|
||||||
if onvalidation is DEFAULT:
|
if onvalidation is DEFAULT:
|
||||||
onvalidation = self.settings.profile_onvalidation
|
onvalidation = self.settings.profile_onvalidation
|
||||||
if onaccept is DEFAULT:
|
if onaccept is DEFAULT:
|
||||||
|
|||||||
+22
-15
@@ -63,11 +63,13 @@ def run_system_tests(options):
|
|||||||
if options.with_coverage:
|
if options.with_coverage:
|
||||||
try:
|
try:
|
||||||
import coverage
|
import coverage
|
||||||
coverage_config = os.environ.get("COVERAGE_PROCESS_START",
|
coverage_config = os.environ.get(
|
||||||
os.path.join('gluon', 'tests', 'coverage.ini')
|
"COVERAGE_PROCESS_START",
|
||||||
)
|
os.path.join('gluon', 'tests', 'coverage.ini'))
|
||||||
call_args = ['coverage', 'run', '--rcfile=%s' % coverage_config,
|
|
||||||
'-m', 'unittest', '-v', 'gluon.tests']
|
call_args = ['coverage', 'run', '--rcfile=%s' %
|
||||||
|
coverage_config,
|
||||||
|
'-m', 'unittest', '-v', 'gluon.tests']
|
||||||
except:
|
except:
|
||||||
sys.stderr.write('Coverage was not installed, skipping\n')
|
sys.stderr.write('Coverage was not installed, skipping\n')
|
||||||
sys.stderr.write("Python 2.7\n")
|
sys.stderr.write("Python 2.7\n")
|
||||||
@@ -157,7 +159,8 @@ def presentation(root):
|
|||||||
# Prevent garbage collection of img
|
# Prevent garbage collection of img
|
||||||
pnl.image = img
|
pnl.image = img
|
||||||
|
|
||||||
def add_label(text='Change Me', font_size=12, foreground='#195866', height=1):
|
def add_label(text='Change Me', font_size=12,
|
||||||
|
foreground='#195866', height=1):
|
||||||
return Tkinter.Label(
|
return Tkinter.Label(
|
||||||
master=canvas,
|
master=canvas,
|
||||||
width=250,
|
width=250,
|
||||||
@@ -331,11 +334,13 @@ class web2pyDialog(object):
|
|||||||
apps = []
|
apps = []
|
||||||
available_apps = [arq for arq in os.listdir('applications/')]
|
available_apps = [arq for arq in os.listdir('applications/')]
|
||||||
available_apps = [arq for arq in available_apps
|
available_apps = [arq for arq in available_apps
|
||||||
if os.path.exists('applications/%s/models/scheduler.py' % arq)]
|
if os.path.exists(
|
||||||
|
'applications/%s/models/scheduler.py' % arq)]
|
||||||
if start:
|
if start:
|
||||||
#the widget takes care of starting the scheduler
|
#the widget takes care of starting the scheduler
|
||||||
if self.options.scheduler and self.options.with_scheduler:
|
if self.options.scheduler and self.options.with_scheduler:
|
||||||
apps = [app.strip() for app in self.options.scheduler.split(',')
|
apps = [app.strip() for app
|
||||||
|
in self.options.scheduler.split(',')
|
||||||
if app in available_apps]
|
if app in available_apps]
|
||||||
for app in apps:
|
for app in apps:
|
||||||
self.try_start_scheduler(app)
|
self.try_start_scheduler(app)
|
||||||
@@ -419,12 +424,13 @@ class web2pyDialog(object):
|
|||||||
""" Connect pages """
|
""" Connect pages """
|
||||||
#reset the menu
|
#reset the menu
|
||||||
available_apps = [arq for arq in os.listdir('applications/')
|
available_apps = [arq for arq in os.listdir('applications/')
|
||||||
if os.path.exists('applications/%s/__init__.py' % arq)]
|
if os.path.exists(
|
||||||
|
'applications/%s/__init__.py' % arq)]
|
||||||
self.pagesmenu.delete(0, len(available_apps))
|
self.pagesmenu.delete(0, len(available_apps))
|
||||||
for arq in available_apps:
|
for arq in available_apps:
|
||||||
url = self.url + arq
|
url = self.url + arq
|
||||||
self.pagesmenu.add_command(label=url,
|
self.pagesmenu.add_command(
|
||||||
command=lambda u=url: start_browser(u))
|
label=url, command=lambda u=url: start_browser(u))
|
||||||
|
|
||||||
def quit(self, justHide=False):
|
def quit(self, justHide=False):
|
||||||
""" Finish the program execution """
|
""" Finish the program execution """
|
||||||
@@ -478,7 +484,8 @@ class web2pyDialog(object):
|
|||||||
return self.error('invalid port number')
|
return self.error('invalid port number')
|
||||||
|
|
||||||
# Check for non default value for ssl inputs
|
# Check for non default value for ssl inputs
|
||||||
if (len(self.options.ssl_certificate) > 0) or (len(self.options.ssl_private_key) > 0):
|
if (len(self.options.ssl_certificate) > 0 or
|
||||||
|
len(self.options.ssl_private_key) > 0):
|
||||||
proto = 'https'
|
proto = 'https'
|
||||||
else:
|
else:
|
||||||
proto = 'http'
|
proto = 'http'
|
||||||
@@ -521,8 +528,8 @@ class web2pyDialog(object):
|
|||||||
self.button_stop.configure(state='normal')
|
self.button_stop.configure(state='normal')
|
||||||
|
|
||||||
if not options.taskbar:
|
if not options.taskbar:
|
||||||
thread.start_new_thread(start_browser,
|
thread.start_new_thread(
|
||||||
(get_url(ip, proto=proto, port=port), True))
|
start_browser, (get_url(ip, proto=proto, port=port), True))
|
||||||
|
|
||||||
self.password.configure(state='readonly')
|
self.password.configure(state='readonly')
|
||||||
[ip.configure(state='disabled') for ip in self.ips.values()]
|
[ip.configure(state='disabled') for ip in self.ips.values()]
|
||||||
@@ -1110,7 +1117,7 @@ def start(cron=True):
|
|||||||
if not options.args is None:
|
if not options.args is None:
|
||||||
sys.argv[:] = options.args
|
sys.argv[:] = options.args
|
||||||
run(options.shell, plain=options.plain, bpython=options.bpython,
|
run(options.shell, plain=options.plain, bpython=options.bpython,
|
||||||
import_models=options.import_models, startfile=options.run)
|
import_models=options.import_models, startfile=options.run, cronjob=options.cronjob)
|
||||||
return
|
return
|
||||||
|
|
||||||
# ## if -C start cron run (extcron) and exit
|
# ## if -C start cron run (extcron) and exit
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import glob
|
||||||
|
import shutil
|
||||||
|
import re
|
||||||
|
|
||||||
|
regex_link = re.compile("""(href|src)\s*=\s*("|')(.+?)("|')""")
|
||||||
|
|
||||||
|
def getname(filename):
|
||||||
|
return re.compile('\W').sub('',filename.split('/')[-1].rsplit('.',1)[0])
|
||||||
|
|
||||||
|
def make_controller(html_files):
|
||||||
|
controller = ''
|
||||||
|
for filename in html_files:
|
||||||
|
name = getname(filename)
|
||||||
|
controller += 'def %s():\n return locals()\n\n' % name
|
||||||
|
return controller
|
||||||
|
|
||||||
|
def fix_links(html,prefix):
|
||||||
|
def fix(match):
|
||||||
|
href,link = match.group(1), match.group(3)
|
||||||
|
if not '://' in link:
|
||||||
|
if link.lower().endswith('.html') and not '/' in link:
|
||||||
|
link = "{{=URL('%s','%s')}}" % (prefix, getname(link))
|
||||||
|
elif link.startswith('./'):
|
||||||
|
link = "{{=URL('static','%s/%s')}}" % (prefix,link[2:])
|
||||||
|
elif link.startswith('/'):
|
||||||
|
link = "{{=URL('static','%s/%s')}}" % (prefix,link[1:])
|
||||||
|
else:
|
||||||
|
link = "{{=URL('static','%s/%s')}}" % (prefix,link)
|
||||||
|
return '%s="%s"' % (href,link)
|
||||||
|
return regex_link.sub(fix,html)
|
||||||
|
|
||||||
|
def make_views(html_files,prefix):
|
||||||
|
views = {}
|
||||||
|
layout_name = os.path.join(prefix,'layout.html')
|
||||||
|
extend = "{{extend '%s'}}" % layout_name
|
||||||
|
for filename in html_files:
|
||||||
|
html = open(filename).read()
|
||||||
|
name = getname(filename)
|
||||||
|
views[os.path.join(prefix,name+'.html')] = fix_links(html,prefix)
|
||||||
|
start = stop = None
|
||||||
|
k = 0
|
||||||
|
while start is None or stop is None:
|
||||||
|
try:
|
||||||
|
if start is None:
|
||||||
|
if len(set(v[k] for v in views.values()))>1:
|
||||||
|
start=k
|
||||||
|
if stop is None:
|
||||||
|
if len(set(v[len(v)-k] for v in views.values()))>1:
|
||||||
|
stop=k
|
||||||
|
except:
|
||||||
|
if start is None:
|
||||||
|
start = k
|
||||||
|
if stop is None:
|
||||||
|
stop = k
|
||||||
|
k+=1
|
||||||
|
header = footer = ''
|
||||||
|
for name in views:
|
||||||
|
html = views[name]
|
||||||
|
n = len(html)
|
||||||
|
header, views[name], footer = \
|
||||||
|
html[:start], extend+html[start:n-stop], html[n-stop:]
|
||||||
|
layout_html = header+'{{include}}'+footer
|
||||||
|
views[layout_name] = layout_html
|
||||||
|
return views
|
||||||
|
|
||||||
|
def recursive_overwrite(src, dest, ignore=None):
|
||||||
|
if os.path.isdir(src):
|
||||||
|
if not os.path.isdir(dest):
|
||||||
|
os.makedirs(dest)
|
||||||
|
files = os.listdir(src)
|
||||||
|
if ignore is not None:
|
||||||
|
ignored = ignore(src, files)
|
||||||
|
else:
|
||||||
|
ignored = set()
|
||||||
|
for f in files:
|
||||||
|
if f not in ignored:
|
||||||
|
recursive_overwrite(os.path.join(src, f),
|
||||||
|
os.path.join(dest, f),
|
||||||
|
ignore)
|
||||||
|
else:
|
||||||
|
shutil.copyfile(src, dest)
|
||||||
|
|
||||||
|
def convert(source, destination,prefix='imported'):
|
||||||
|
html_files = glob.glob(os.path.join(source,'*.html'))
|
||||||
|
static_folder = os.path.join(destination,'static',prefix)
|
||||||
|
recursive_overwrite(source,static_folder)
|
||||||
|
controller = make_controller(html_files)
|
||||||
|
views = make_views(html_files,prefix)
|
||||||
|
controller_filename = os.path.join(destination,'controllers',prefix+'.py')
|
||||||
|
|
||||||
|
open(controller_filename,'w').write(controller)
|
||||||
|
for name in views:
|
||||||
|
fullname = os.path.join(destination,'views',name)
|
||||||
|
if not os.path.exists(os.path.split(fullname)[0]):
|
||||||
|
os.makedirs(os.path.split(fullname)[0])
|
||||||
|
open(fullname,'w').write(views[name])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
convert(sys.argv[1],sys.argv[2])
|
||||||
Reference in New Issue
Block a user