Merge github.com:web2py/web2py
This commit is contained in:
@@ -54,20 +54,19 @@ src:
|
||||
### build web2py_src.zip
|
||||
echo '' > NEWINSTALL
|
||||
mv web2py_src.zip web2py_src_old.zip | echo 'no old'
|
||||
cd ..; zip -r web2py/web2py_src.zip web2py/gluon/*.py web2py/gluon/contrib/* web2py/splashlogo.gif web2py/*.py web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/Makefile web2py/epydoc.css web2py/epydoc.conf web2py/app.example.yaml web2py/logging.example.conf web2py_exe.conf web2py/queue.example.yaml MANIFEST.in w2p_apps w2p_clone w2p_run startweb2py web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py
|
||||
cd ..; zip -r web2py/web2py_src.zip web2py/gluon/*.py web2py/gluon/contrib/* web2py/splashlogo.gif web2py/*.py web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/Makefile web2py/epydoc.css web2py/epydoc.conf web2py/app.example.yaml web2py/logging.example.conf web2py/queue.example.yaml MANIFEST.in w2p_apps w2p_clone w2p_run web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py
|
||||
|
||||
mdp:
|
||||
make src
|
||||
make app
|
||||
make win
|
||||
app:
|
||||
echo 'did you uncomment import_all in gluon/main.py?'
|
||||
python2.5 -c 'import compileall; compileall.compile_dir("gluon/")'
|
||||
python2.7 -c 'import compileall; compileall.compile_dir("gluon/")'
|
||||
#python web2py.py -S welcome -R __exit__.py
|
||||
#cd ../web2py_osx/site-packages/; unzip ../site-packages.zip
|
||||
find gluon -path '*.pyc' -exec cp {} ../web2py_osx/site-packages/{} \;
|
||||
cd ../web2py_osx/site-packages/; zip -r ../site-packages.zip *
|
||||
mv ../web2py_osx/site-packages.zip ../web2py_osx/web2py/web2py.app/Contents/Resources/lib/python2.5
|
||||
mv ../web2py_osx/site-packages.zip ../web2py_osx/web2py/web2py.app/Contents/Resources/lib/python2.7
|
||||
cp README.markdown ../web2py_osx/web2py/web2py.app/Contents/Resources
|
||||
cp NEWINSTALL ../web2py_osx/web2py/web2py.app/Contents/Resources
|
||||
cp LICENSE ../web2py_osx/web2py/web2py.app/Contents/Resources
|
||||
@@ -86,7 +85,6 @@ app:
|
||||
cd ../web2py_osx; zip -r web2py_osx.zip web2py
|
||||
mv ../web2py_osx/web2py_osx.zip .
|
||||
win:
|
||||
echo 'did you uncomment import_all in gluon/main.py?'
|
||||
python2.7 -c 'import compileall; compileall.compile_dir("gluon/")'
|
||||
#cd ../web2py_win/library/; unzip ../library.zip
|
||||
find gluon -path '*.pyc' -exec cp {} ../web2py_win/library/{} \;
|
||||
@@ -110,7 +108,7 @@ win:
|
||||
cd ../web2py_win; zip -r web2py_win.zip web2py
|
||||
mv ../web2py_win/web2py_win.zip .
|
||||
run:
|
||||
python2.5 web2py.py -a hello
|
||||
python2.7 web2py.py -a hello
|
||||
commit:
|
||||
python web2py.py --run_system_tests
|
||||
make src
|
||||
|
||||
@@ -1 +1 @@
|
||||
Version 2.4.1-alpha.2+timestamp.2013.01.23.08.48.16
|
||||
Version 2.4.1-alpha.2+timestamp.2013.02.08.12.41.08
|
||||
|
||||
@@ -469,10 +469,10 @@ def ccache():
|
||||
|
||||
def table_template(table):
|
||||
from gluon.html import TR, TD, TABLE, TAG
|
||||
|
||||
|
||||
def FONT(*args, **kwargs):
|
||||
return TAG.font(*args, **kwargs)
|
||||
|
||||
|
||||
def types(field):
|
||||
f_type = field.type
|
||||
if not isinstance(f_type,str):
|
||||
@@ -480,7 +480,7 @@ def table_template(table):
|
||||
elif f_type == 'string':
|
||||
return field.length
|
||||
elif f_type == 'id':
|
||||
return B('pk')
|
||||
return B('pk')
|
||||
elif f_type.startswith('reference') or \
|
||||
f_type.startswith('list:reference'):
|
||||
return B('fk')
|
||||
@@ -495,7 +495,7 @@ def table_template(table):
|
||||
face = "Helvetica"
|
||||
face_bold = "Helvetica Bold"
|
||||
border = 0
|
||||
|
||||
|
||||
rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor),
|
||||
_colspan=3, _cellpadding=cellpadding,
|
||||
_align="center", _bgcolor=color)))
|
||||
@@ -515,10 +515,31 @@ def table_template(table):
|
||||
|
||||
|
||||
def bg_graph_model():
|
||||
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
||||
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
||||
|
||||
subgraphs = dict()
|
||||
for tablename in db.tables:
|
||||
graph.add_node(tablename, name=tablename, shape='plaintext',
|
||||
if hasattr(db[tablename],'_meta_graphmodel'):
|
||||
meta_graphmodel = db[tablename]._meta_graphmodel
|
||||
else:
|
||||
meta_graphmodel = dict(group='Undefined', color='#ECECEC')
|
||||
|
||||
group = meta_graphmodel['group'].replace(' ', '')
|
||||
if not subgraphs.has_key(group):
|
||||
subgraphs[group] = dict(meta=meta_graphmodel, tables=[])
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
else:
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
|
||||
graph.add_node(tablename, name=tablename, shape='plaintext',
|
||||
label=table_template(tablename))
|
||||
|
||||
for n, key in enumerate(subgraphs.iterkeys()):
|
||||
graph.subgraph(nbunch=subgraphs[key]['tables'],
|
||||
name='cluster%d' % n,
|
||||
style='filled',
|
||||
color=subgraphs[key]['meta']['color'],
|
||||
label=subgraphs[key]['meta']['group'])
|
||||
|
||||
for tablename in db.tables:
|
||||
for field in db[tablename]:
|
||||
@@ -532,7 +553,15 @@ def bg_graph_model():
|
||||
graph.add_edge(n1, n2, color="#4C4C4C", label='')
|
||||
|
||||
graph.layout()
|
||||
return graph.draw(format='png', prog='dot')
|
||||
#return graph.draw(format='png', prog='dot')
|
||||
if not request.args:
|
||||
return graph.draw(format='png', prog='dot')
|
||||
else:
|
||||
response.headers['Content-Disposition']='attachment;filename=graph.%s'%request.args(0)
|
||||
if request.args(0) == 'dot':
|
||||
return graph.string()
|
||||
else:
|
||||
return graph.draw(format=request.args(0), prog='dot')
|
||||
|
||||
def graph_model():
|
||||
def graph_model():
|
||||
return dict(databases=databases, pgv=pgv)
|
||||
|
||||
@@ -565,6 +565,7 @@ def edit():
|
||||
# Load json only if it is ajax edited...
|
||||
app = get_app(request.vars.app)
|
||||
filename = '/'.join(request.args)
|
||||
response.title = request.args[-1]
|
||||
if request.vars.app:
|
||||
path = abspath(filename)
|
||||
else:
|
||||
@@ -645,6 +646,7 @@ def edit():
|
||||
code = request.vars.data.rstrip().replace('\r\n', '\n') + '\n'
|
||||
compile(code, path, "exec", _ast.PyCF_ONLY_AST)
|
||||
except Exception, e:
|
||||
# offset calculation is only used for textarea (start/stop)
|
||||
start = sum([len(line) + 1 for l, line
|
||||
in enumerate(request.vars.data.split("\n"))
|
||||
if l < e.lineno - 1])
|
||||
@@ -654,7 +656,7 @@ def edit():
|
||||
else:
|
||||
offset = 0
|
||||
highlight = {'start': start, 'end': start +
|
||||
offset + 1, 'lineno': e.lineno}
|
||||
offset + 1, 'lineno': e.lineno, 'offset': offset}
|
||||
try:
|
||||
ex_name = e.__class__.__name__
|
||||
except:
|
||||
@@ -811,6 +813,7 @@ def edit_language():
|
||||
""" Edit language file """
|
||||
app = get_app()
|
||||
filename = '/'.join(request.args)
|
||||
response.title = request.args[-1]
|
||||
strings = read_dict(apath(filename, r=request))
|
||||
|
||||
if '__corrupted__' in strings:
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
'Admin language': 'Admin language',
|
||||
'administrative interface': 'administrative interface',
|
||||
'Administrator Password:': 'Password Amministratore:',
|
||||
'An error occured, please %s the page': 'An error occured, please %s the page',
|
||||
'and rename it (required):': 'e rinominala (obbligatorio):',
|
||||
'and rename it:': 'e rinominala:',
|
||||
'appadmin': 'appadmin ',
|
||||
@@ -144,8 +145,10 @@
|
||||
'Get from URL:': 'Get from URL:',
|
||||
'Git Pull': 'Git Pull',
|
||||
'Git Push': 'Git Push',
|
||||
'graph model': 'graph model',
|
||||
'Hello World': 'Salve Mondo',
|
||||
'Help': 'aiuto',
|
||||
'Hide/Show Translated strings': 'Hide/Show Translated strings',
|
||||
'htmledit': 'modifica come html',
|
||||
'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.',
|
||||
'Import/Export': 'Importa/Esporta',
|
||||
@@ -195,6 +198,8 @@
|
||||
'NO': 'NO',
|
||||
'No databases in this application': 'Nessun database presente in questa applicazione',
|
||||
'no match': 'nessuna corrispondenza',
|
||||
'or alternatively': 'or alternatively',
|
||||
'Or Get from URL:': 'Or Get from URL:',
|
||||
'or import from csv file': 'oppure importa da file CSV',
|
||||
'or provide app url:': "oppure fornisci url dell'applicazione:",
|
||||
'Original/Translation': 'Originale/Traduzione',
|
||||
@@ -219,6 +224,7 @@
|
||||
'record does not exist': 'il record non esiste',
|
||||
'record id': 'ID del record',
|
||||
'register': 'registrazione',
|
||||
'reload': 'reload',
|
||||
'Remove compiled': 'rimozione codice compilato',
|
||||
'request': 'request',
|
||||
'Resolve Conflict file': 'File di risoluzione conflitto',
|
||||
@@ -242,6 +248,7 @@
|
||||
'Start wizard': 'start wizard',
|
||||
'state': 'stato',
|
||||
'static': 'statico',
|
||||
'Static': 'Static',
|
||||
'Static files': 'Files statici',
|
||||
'Stylesheet': 'Foglio di stile (stylesheet)',
|
||||
'submit': 'invia',
|
||||
@@ -261,6 +268,7 @@
|
||||
'There are no models': 'Non ci sono modelli',
|
||||
'There are no modules': 'Non ci sono moduli',
|
||||
'There are no plugins': 'There are no plugins',
|
||||
'There are no private files': 'There are no private files',
|
||||
'There are no static files': 'Non ci sono file statici',
|
||||
'There are no translators, only default language is supported': 'Non ci sono traduzioni, viene solo supportato il linguaggio di base',
|
||||
'There are no views': 'Non ci sono viste ("view")',
|
||||
@@ -280,6 +288,7 @@
|
||||
'Translation strings for the application': 'Translation strings for the application',
|
||||
'try': 'prova',
|
||||
'try something like': 'prova qualcosa come',
|
||||
'Try the mobile interface': 'Try the mobile interface',
|
||||
'try view': 'try view',
|
||||
'Unable to check for upgrades': 'Impossibile controllare presenza di aggiornamenti',
|
||||
'unable to create application "%s"': 'impossibile creare applicazione "%s"',
|
||||
@@ -298,6 +307,7 @@
|
||||
'Update:': 'Aggiorna:',
|
||||
'upgrade web2py now': 'upgrade web2py now',
|
||||
'upload': 'upload',
|
||||
'Upload': 'Upload',
|
||||
'Upload & install packed application': 'Carica ed installa pacchetto con applicazione',
|
||||
'Upload a package:': 'Upload a package:',
|
||||
'Upload and install packed application': 'Upload and install packed application',
|
||||
@@ -308,6 +318,7 @@
|
||||
'Use an url:': 'Use an url:',
|
||||
'variables': 'variables',
|
||||
'Version': 'Versione',
|
||||
'Version %s.%s.%s %s (%s)': 'Version %s.%s.%s %s (%s)',
|
||||
'Version %s.%s.%s (%s) %s': 'Version %s.%s.%s (%s) %s',
|
||||
'versioning': 'sistema di versioni',
|
||||
'Versioning': 'Versioning',
|
||||
|
||||
@@ -43,8 +43,9 @@ function doHighlight(highlight) {
|
||||
if (window.ace_editor) {
|
||||
window.ace_editor.gotoLine(highlight.lineno);
|
||||
} else if (window.mirror) {
|
||||
window.mirror.setSelection({line:highlight.lineno,ch:0},
|
||||
{line:highlight.end,ch:0});
|
||||
// Put the cursor at the offending line:
|
||||
window.mirror.setCursor({line:highlight.lineno-1,
|
||||
ch:highlight.offset+1});
|
||||
} else if (window.eamy) {
|
||||
// not implemented
|
||||
} else if (window.textarea) {
|
||||
|
||||
@@ -229,7 +229,21 @@
|
||||
{{=T('pygraphviz library not found')}}
|
||||
{{elif not databases:}}
|
||||
{{=T("No databases in this application")}}
|
||||
{{else:}}
|
||||
{{else:}}
|
||||
<div class="btn-group">
|
||||
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<i class="icon-download"></i> {{=T('Save model as...')}}
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['png'])}}">png</a></li>
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['svg'])}}">svg</a></li>
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['pdf'])}}">pdf</a></li>
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['ps'])}}">ps</a></li>
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['dot'])}}">dot</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br />
|
||||
{{=IMG(_src=URL('appadmin', 'bg_graph_model'))}}
|
||||
{{pass}}
|
||||
{{pass}}
|
||||
|
||||
@@ -22,4 +22,5 @@ function hideShowTranslated(){
|
||||
<div class="languageform">
|
||||
{{=form}}
|
||||
</div>
|
||||
<!-- end "edit_language" block -->
|
||||
<button class="btn" style="position:fixed; right:10px; bottom: 10px;" onclick="jQuery('form input[type=submit]').click()">Save Changes</button>
|
||||
<!-- end "edit_language" block -->
|
||||
|
||||
@@ -469,10 +469,10 @@ def ccache():
|
||||
|
||||
def table_template(table):
|
||||
from gluon.html import TR, TD, TABLE, TAG
|
||||
|
||||
|
||||
def FONT(*args, **kwargs):
|
||||
return TAG.font(*args, **kwargs)
|
||||
|
||||
|
||||
def types(field):
|
||||
f_type = field.type
|
||||
if not isinstance(f_type,str):
|
||||
@@ -480,7 +480,7 @@ def table_template(table):
|
||||
elif f_type == 'string':
|
||||
return field.length
|
||||
elif f_type == 'id':
|
||||
return B('pk')
|
||||
return B('pk')
|
||||
elif f_type.startswith('reference') or \
|
||||
f_type.startswith('list:reference'):
|
||||
return B('fk')
|
||||
@@ -495,7 +495,7 @@ def table_template(table):
|
||||
face = "Helvetica"
|
||||
face_bold = "Helvetica Bold"
|
||||
border = 0
|
||||
|
||||
|
||||
rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor),
|
||||
_colspan=3, _cellpadding=cellpadding,
|
||||
_align="center", _bgcolor=color)))
|
||||
@@ -515,10 +515,31 @@ def table_template(table):
|
||||
|
||||
|
||||
def bg_graph_model():
|
||||
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
||||
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
||||
|
||||
subgraphs = dict()
|
||||
for tablename in db.tables:
|
||||
graph.add_node(tablename, name=tablename, shape='plaintext',
|
||||
if hasattr(db[tablename],'_meta_graphmodel'):
|
||||
meta_graphmodel = db[tablename]._meta_graphmodel
|
||||
else:
|
||||
meta_graphmodel = dict(group='Undefined', color='#ECECEC')
|
||||
|
||||
group = meta_graphmodel['group'].replace(' ', '')
|
||||
if not subgraphs.has_key(group):
|
||||
subgraphs[group] = dict(meta=meta_graphmodel, tables=[])
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
else:
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
|
||||
graph.add_node(tablename, name=tablename, shape='plaintext',
|
||||
label=table_template(tablename))
|
||||
|
||||
for n, key in enumerate(subgraphs.iterkeys()):
|
||||
graph.subgraph(nbunch=subgraphs[key]['tables'],
|
||||
name='cluster%d' % n,
|
||||
style='filled',
|
||||
color=subgraphs[key]['meta']['color'],
|
||||
label=subgraphs[key]['meta']['group'])
|
||||
|
||||
for tablename in db.tables:
|
||||
for field in db[tablename]:
|
||||
@@ -532,7 +553,15 @@ def bg_graph_model():
|
||||
graph.add_edge(n1, n2, color="#4C4C4C", label='')
|
||||
|
||||
graph.layout()
|
||||
return graph.draw(format='png', prog='dot')
|
||||
#return graph.draw(format='png', prog='dot')
|
||||
if not request.args:
|
||||
return graph.draw(format='png', prog='dot')
|
||||
else:
|
||||
response.headers['Content-Disposition']='attachment;filename=graph.%s'%request.args(0)
|
||||
if request.args(0) == 'dot':
|
||||
return graph.string()
|
||||
else:
|
||||
return graph.draw(format=request.args(0), prog='dot')
|
||||
|
||||
def graph_model():
|
||||
def graph_model():
|
||||
return dict(databases=databases, pgv=pgv)
|
||||
|
||||
@@ -229,7 +229,21 @@
|
||||
{{=T('pygraphviz library not found')}}
|
||||
{{elif not databases:}}
|
||||
{{=T("No databases in this application")}}
|
||||
{{else:}}
|
||||
{{else:}}
|
||||
<div class="btn-group">
|
||||
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<i class="icon-download"></i> {{=T('Save model as...')}}
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['png'])}}">png</a></li>
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['svg'])}}">svg</a></li>
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['pdf'])}}">pdf</a></li>
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['ps'])}}">ps</a></li>
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['dot'])}}">dot</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br />
|
||||
{{=IMG(_src=URL('appadmin', 'bg_graph_model'))}}
|
||||
{{pass}}
|
||||
{{pass}}
|
||||
|
||||
@@ -469,10 +469,10 @@ def ccache():
|
||||
|
||||
def table_template(table):
|
||||
from gluon.html import TR, TD, TABLE, TAG
|
||||
|
||||
|
||||
def FONT(*args, **kwargs):
|
||||
return TAG.font(*args, **kwargs)
|
||||
|
||||
|
||||
def types(field):
|
||||
f_type = field.type
|
||||
if not isinstance(f_type,str):
|
||||
@@ -480,7 +480,7 @@ def table_template(table):
|
||||
elif f_type == 'string':
|
||||
return field.length
|
||||
elif f_type == 'id':
|
||||
return B('pk')
|
||||
return B('pk')
|
||||
elif f_type.startswith('reference') or \
|
||||
f_type.startswith('list:reference'):
|
||||
return B('fk')
|
||||
@@ -495,7 +495,7 @@ def table_template(table):
|
||||
face = "Helvetica"
|
||||
face_bold = "Helvetica Bold"
|
||||
border = 0
|
||||
|
||||
|
||||
rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor),
|
||||
_colspan=3, _cellpadding=cellpadding,
|
||||
_align="center", _bgcolor=color)))
|
||||
@@ -515,10 +515,31 @@ def table_template(table):
|
||||
|
||||
|
||||
def bg_graph_model():
|
||||
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
||||
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
||||
|
||||
subgraphs = dict()
|
||||
for tablename in db.tables:
|
||||
graph.add_node(tablename, name=tablename, shape='plaintext',
|
||||
if hasattr(db[tablename],'_meta_graphmodel'):
|
||||
meta_graphmodel = db[tablename]._meta_graphmodel
|
||||
else:
|
||||
meta_graphmodel = dict(group='Undefined', color='#ECECEC')
|
||||
|
||||
group = meta_graphmodel['group'].replace(' ', '')
|
||||
if not subgraphs.has_key(group):
|
||||
subgraphs[group] = dict(meta=meta_graphmodel, tables=[])
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
else:
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
|
||||
graph.add_node(tablename, name=tablename, shape='plaintext',
|
||||
label=table_template(tablename))
|
||||
|
||||
for n, key in enumerate(subgraphs.iterkeys()):
|
||||
graph.subgraph(nbunch=subgraphs[key]['tables'],
|
||||
name='cluster%d' % n,
|
||||
style='filled',
|
||||
color=subgraphs[key]['meta']['color'],
|
||||
label=subgraphs[key]['meta']['group'])
|
||||
|
||||
for tablename in db.tables:
|
||||
for field in db[tablename]:
|
||||
@@ -532,7 +553,15 @@ def bg_graph_model():
|
||||
graph.add_edge(n1, n2, color="#4C4C4C", label='')
|
||||
|
||||
graph.layout()
|
||||
return graph.draw(format='png', prog='dot')
|
||||
#return graph.draw(format='png', prog='dot')
|
||||
if not request.args:
|
||||
return graph.draw(format='png', prog='dot')
|
||||
else:
|
||||
response.headers['Content-Disposition']='attachment;filename=graph.%s'%request.args(0)
|
||||
if request.args(0) == 'dot':
|
||||
return graph.string()
|
||||
else:
|
||||
return graph.draw(format=request.args(0), prog='dot')
|
||||
|
||||
def graph_model():
|
||||
def graph_model():
|
||||
return dict(databases=databases, pgv=pgv)
|
||||
|
||||
@@ -229,7 +229,21 @@
|
||||
{{=T('pygraphviz library not found')}}
|
||||
{{elif not databases:}}
|
||||
{{=T("No databases in this application")}}
|
||||
{{else:}}
|
||||
{{else:}}
|
||||
<div class="btn-group">
|
||||
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<i class="icon-download"></i> {{=T('Save model as...')}}
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['png'])}}">png</a></li>
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['svg'])}}">svg</a></li>
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['pdf'])}}">pdf</a></li>
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['ps'])}}">ps</a></li>
|
||||
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['dot'])}}">dot</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br />
|
||||
{{=IMG(_src=URL('appadmin', 'bg_graph_model'))}}
|
||||
{{pass}}
|
||||
{{pass}}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!--[if HTML5]><![endif]-->
|
||||
<!DOCTYPE html>
|
||||
<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ -->
|
||||
<!--[if lt IE 7]><html class="ie ie6 ie-lte9 ie-lte8 ie-lte7 no-js" lang="{{=T.accepted_language or 'en'}}"> <![endif]-->
|
||||
@@ -6,16 +7,16 @@
|
||||
<!--[if IE 9]><html class="ie9 ie-lte9 no-js" lang="{{=T.accepted_language or 'en'}}"> <![endif]-->
|
||||
<!--[if (gt IE 9)|!(IE)]><!--> <html class="no-js" lang="{{=T.accepted_language or 'en'}}"> <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>{{=response.title or request.application}}</title>
|
||||
<!--[if !HTML5]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge{{=not request.is_local and ',chrome=1' or ''}}">
|
||||
<![endif]-->
|
||||
<!-- www.phpied.com/conditional-comments-block-downloads/ -->
|
||||
<!-- Always force latest IE rendering engine
|
||||
(even in intranet) & Chrome Frame
|
||||
Remove this if you use the .htaccess -->
|
||||
<!--[if IE]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge{{=not request.is_local and ',chrome=1' or ''}}">
|
||||
<![endif]-->
|
||||
|
||||
<title>{{=response.title or request.application}}</title>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<!-- http://dev.w3.org/html5/markup/meta.name.html -->
|
||||
<meta name="application-name" content="{{=request.application}}" />
|
||||
|
||||
@@ -609,7 +609,7 @@ def protolinks_simple(proto, url):
|
||||
#elif proto == 'embed': # NOTE: embed is a synonym to iframe now
|
||||
# return '<a href="%s" class="%sembed">%s></a>'%(url,class_prefix,url)
|
||||
elif proto == 'qr':
|
||||
return '<img width="80px" src="http://qrcode.kaywa.com/img.php?s=8&d=%s" alt="qr code" />'%url
|
||||
return '<img style="width:80px" src="http://qrcode.kaywa.com/img.php?s=8&d=%s" alt="qr code" />'%url
|
||||
return proto+':'+url
|
||||
|
||||
def render(text,
|
||||
@@ -701,7 +701,7 @@ def render(text,
|
||||
'<p><img src="http://example.com" alt="this is an image" style="float:left" /></p>'
|
||||
|
||||
>>> render('[[this is an image http://example.com left 200px]]')
|
||||
'<p><img src="http://example.com" alt="this is an image" style="float:left" width="200px" /></p>'
|
||||
'<p><img src="http://example.com" alt="this is an image" style="float:left;width:200px" /></p>'
|
||||
|
||||
>>> render("[[Your browser doesn't support <video> HTML5 tag http://example.com video]]")
|
||||
'<p><video controls="controls"><source src="http://example.com" />Your browser doesn\\'t support <video> HTML5 tag</video></p>'
|
||||
@@ -740,7 +740,7 @@ def render(text,
|
||||
'<p>auto-image: (<img src="http://example.com/image.jpeg" controls />)</p>'
|
||||
|
||||
>>> render("qr: (qr:http://example.com/image.jpeg)")
|
||||
'<p>qr: (<img width="80px" src="http://qrcode.kaywa.com/img.php?s=8&d=http://example.com/image.jpeg" alt="qr code" />)</p>'
|
||||
'<p>qr: (<img style="width:80px" src="http://qrcode.kaywa.com/img.php?s=8&d=http://example.com/image.jpeg" alt="qr code" />)</p>'
|
||||
|
||||
>>> render("embed: (embed:http://example.com/page)")
|
||||
'<p>embed: (<iframe src="http://example.com/page" frameborder="0" allowfullscreen></iframe>)</p>'
|
||||
@@ -776,10 +776,10 @@ def render(text,
|
||||
'<p>title9: <img src="http://example.com" alt="test message" title="title" style="float:left" /></p>'
|
||||
|
||||
>>> render("title10: [[test message [title] http://example.com right 100px]]")
|
||||
'<p>title10: <img src="http://example.com" alt="test message" title="title" style="float:right" width="100px" /></p>'
|
||||
'<p>title10: <img src="http://example.com" alt="test message" title="title" style="float:right;width:100px" /></p>'
|
||||
|
||||
>>> render("title11: [[test message [title] http://example.com center 200px]]")
|
||||
'<p>title11: <p style="text-align:center"><img src="http://example.com" alt="test message" title="title" width="200px" /></p></p>'
|
||||
'<p>title11: <p style="text-align:center"><img src="http://example.com" alt="test message" title="title" style="width:200px" /></p></p>'
|
||||
|
||||
>>> render(r"\\[[probe]]")
|
||||
'<p>[[probe]]</p>'
|
||||
@@ -1236,23 +1236,24 @@ def render(text,
|
||||
return m.group(0)
|
||||
k = escape(k)
|
||||
t = t or ''
|
||||
width = ' width="%s"' % w if w else ''
|
||||
style = 'width:%s' % w if w else ''
|
||||
title = ' title="%s"' % escape(a).replace(META, DISABLED_META) if a else ''
|
||||
style = p_begin = p_end = ''
|
||||
p_begin = p_end = ''
|
||||
if p == 'center':
|
||||
p_begin = '<p style="text-align:center">'
|
||||
p_end = '</p>'+pp
|
||||
elif p in ('left','right'):
|
||||
style = ' style="float:%s"' % p
|
||||
style = ('float:%s' % p)+(';%s' % style if style else '')
|
||||
if style:
|
||||
style = ' style="%s"' % style
|
||||
if p in ('video','audio'):
|
||||
t = render(t, {}, {}, 'br', URL, environment, latex,
|
||||
autolinks, protolinks, class_prefix, id_prefix, pretty_print)
|
||||
return '<%(p)s controls="controls"%(title)s%(width)s><source src="%(k)s" />%(t)s</%(p)s>' \
|
||||
% dict(p=p, title=title, width=width, k=k, t=t)
|
||||
return '<%(p)s controls="controls"%(title)s%(style)s><source src="%(k)s" />%(t)s</%(p)s>' \
|
||||
% dict(p=p, title=title, style=style, k=k, t=t)
|
||||
alt = ' alt="%s"'%escape(t).replace(META, DISABLED_META) if t else ''
|
||||
return '%(begin)s<img src="%(k)s"%(alt)s%(title)s%(style)s%(width)s />%(end)s' \
|
||||
% dict(begin=p_begin, k=k, alt=alt, title=title,
|
||||
style=style, width=width, end=p_end)
|
||||
return '%(begin)s<img src="%(k)s"%(alt)s%(title)s%(style)s />%(end)s' \
|
||||
% dict(begin=p_begin, k=k, alt=alt, title=title, style=style, end=p_end)
|
||||
|
||||
def sub_link(m):
|
||||
t,a,k,p = m.group('t','a','k','p')
|
||||
|
||||
+24
-20
@@ -61,26 +61,30 @@ def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
|
||||
and isinstance(globals, dict):
|
||||
import_tb = None
|
||||
try:
|
||||
items = current.request.folder.split(os.path.sep)
|
||||
if not items[-1]:
|
||||
items = items[:-1]
|
||||
modules_prefix = '.'.join(items[-2:]) + '.modules'
|
||||
if not fromlist:
|
||||
# import like "import x" or "import x.y"
|
||||
result = None
|
||||
for itemname in name.split("."):
|
||||
new_mod = base_importer(
|
||||
modules_prefix, globals, locals, [itemname], level)
|
||||
try:
|
||||
result = result or new_mod.__dict__[itemname]
|
||||
except KeyError, e:
|
||||
raise ImportError, 'Cannot import module %s' % str(e)
|
||||
modules_prefix += "." + itemname
|
||||
return result
|
||||
else:
|
||||
# import like "from x import a, b, ..."
|
||||
pname = modules_prefix + "." + name
|
||||
return base_importer(pname, globals, locals, fromlist, level)
|
||||
try:
|
||||
oname = name if not name.startswith('.') else '.'+name
|
||||
return NATIVE_IMPORTER(oname, globals, locals, fromlist, level)
|
||||
except ImportError:
|
||||
items = current.request.folder.split(os.path.sep)
|
||||
if not items[-1]:
|
||||
items = items[:-1]
|
||||
modules_prefix = '.'.join(items[-2:]) + '.modules'
|
||||
if not fromlist:
|
||||
# import like "import x" or "import x.y"
|
||||
result = None
|
||||
for itemname in name.split("."):
|
||||
new_mod = base_importer(
|
||||
modules_prefix, globals, locals, [itemname], level)
|
||||
try:
|
||||
result = result or new_mod.__dict__[itemname]
|
||||
except KeyError, e:
|
||||
raise ImportError, 'Cannot import module %s' % str(e)
|
||||
modules_prefix += "." + itemname
|
||||
return result
|
||||
else:
|
||||
# import like "from x import a, b, ..."
|
||||
pname = modules_prefix + "." + name
|
||||
return base_importer(pname, globals, locals, fromlist, level)
|
||||
except ImportError, e1:
|
||||
import_tb = sys.exc_info()[2]
|
||||
try:
|
||||
|
||||
+445
-63
@@ -339,7 +339,10 @@ if not 'google' in DRIVERS:
|
||||
LOGGER.debug('no Oracle driver cx_Oracle')
|
||||
|
||||
try:
|
||||
#try:
|
||||
import pyodbc
|
||||
#except ImportError:
|
||||
# from contrib.pypyodbc import pypyodbc as pyodbc
|
||||
DRIVERS.append('MSSQL(pyodbc)')
|
||||
DRIVERS.append('DB2(pyodbc)')
|
||||
DRIVERS.append('Teradata(pyodbc)')
|
||||
@@ -876,7 +879,7 @@ class BaseAdapter(ConnectionPool):
|
||||
# The reason is that we do not want to trigger
|
||||
# a migration simply because a default value changes.
|
||||
not_null = self.NOT_NULL(field.default, field_type)
|
||||
ftype = ftype.replace('NOT NULL', not_null)
|
||||
ftype = ftype.replace('NOT NULL', not_null)
|
||||
sql_fields_aux[field_name] = dict(sql=ftype)
|
||||
# Postgres - PostGIS:
|
||||
# geometry fields are added after the table has been created, not now
|
||||
@@ -1233,12 +1236,24 @@ class BaseAdapter(ConnectionPool):
|
||||
return '(%s LIKE %s)' % (self.expand(first),
|
||||
self.expand('%'+second, 'string'))
|
||||
|
||||
def CONTAINS(self, first, second):
|
||||
if first.type in ('string', 'text', 'json'):
|
||||
key = '%'+str(second).replace('%','%%')+'%'
|
||||
elif first.type.startswith('list:'):
|
||||
key = '%|'+str(second).replace('|','||').replace('%','%%')+'|%'
|
||||
return '(%s LIKE %s)' % (self.expand(first),self.expand(key,'string'))
|
||||
def CONTAINS(self, first, second, case_sensitive=False):
|
||||
if isinstance(second,Expression):
|
||||
field = self.expand(first)
|
||||
expr = self.expand(second,'string')
|
||||
if first.type.startswith('list:'):
|
||||
expr = 'CONCAT("|", %s, "|")' % expr
|
||||
elif not first.type in ('string', 'text', 'json'):
|
||||
raise RuntimeError("Expression Not Supported")
|
||||
return 'INSTR(%s,%s)' % (field, expr)
|
||||
else:
|
||||
if first.type in ('string', 'text', 'json'):
|
||||
key = '%'+str(second).replace('%','%%')+'%'
|
||||
elif first.type.startswith('list:'):
|
||||
key = '%|'+str(second).replace('|','||').replace('%','%%')+'|%'
|
||||
else:
|
||||
raise RuntimeError("Expression Not Supported")
|
||||
op = case_sensitive and self.LIKE or self.ILIKE
|
||||
return op(first,key)
|
||||
|
||||
def EQ(self, first, second=None):
|
||||
if second is None:
|
||||
@@ -1317,10 +1332,11 @@ class BaseAdapter(ConnectionPool):
|
||||
first = expression.first
|
||||
second = expression.second
|
||||
op = expression.op
|
||||
optional_args = expression.optional_args or {}
|
||||
if not second is None:
|
||||
return op(first, second)
|
||||
return op(first, second, **optional_args)
|
||||
elif not first is None:
|
||||
return op(first)
|
||||
return op(first,**optional_args)
|
||||
elif isinstance(op, str):
|
||||
if op.endswith(';'):
|
||||
op=op[:-1]
|
||||
@@ -2569,11 +2585,25 @@ class PostgreSQLAdapter(BaseAdapter):
|
||||
def after_connection(self):
|
||||
self.connection.set_client_encoding('UTF8')
|
||||
self.execute("SET standard_conforming_strings=on;")
|
||||
self.try_json()
|
||||
|
||||
def lastrowid(self,table):
|
||||
self.execute("select currval('%s')" % table._sequence_name)
|
||||
return int(self.cursor.fetchone()[0])
|
||||
|
||||
def try_json(self):
|
||||
# check JSON data type support
|
||||
# (to be added to after_connection)
|
||||
if self.driver_name == "pg8000":
|
||||
supports_json = self.connection.server_version >= "9.2.0"
|
||||
elif (self.driver_name == "psycopg2") and \
|
||||
(self.driver.__version__ >= "2.0.12"):
|
||||
supports_json = self.connection.server_version >= 90200
|
||||
elif self.driver_name == "zxJDBC":
|
||||
supports_json = self.connection.dbversion >= "9.2.0"
|
||||
else: supports_json = None
|
||||
if supports_json: self.types["json"] = "JSON"
|
||||
else: LOGGER.debug("Your database version does not support the JSON data type (using TEXT instead)")
|
||||
|
||||
def LIKE(self,first,second):
|
||||
args = (self.expand(first), self.expand(second,'string'))
|
||||
@@ -2601,12 +2631,13 @@ class PostgreSQLAdapter(BaseAdapter):
|
||||
return '(%s ILIKE %s)' % (self.expand(first),
|
||||
self.expand('%'+second,'string'))
|
||||
|
||||
def CONTAINS(self,first,second):
|
||||
def CONTAINS(self,first,second,case_sensitive=False):
|
||||
if first.type in ('string','text', 'json'):
|
||||
key = '%'+str(second).replace('%','%%')+'%'
|
||||
elif first.type.startswith('list:'):
|
||||
key = '%|'+str(second).replace('|','||').replace('%','%%')+'|%'
|
||||
return '(%s ILIKE %s)' % (self.expand(first),self.expand(key,'string'))
|
||||
op = case_sensitive and self.LIKE or self.ILIKE
|
||||
return op(first,key)
|
||||
|
||||
# GIS functions
|
||||
|
||||
@@ -2797,6 +2828,7 @@ class JDBCPostgreSQLAdapter(PostgreSQLAdapter):
|
||||
self.connection.set_client_encoding('UTF8')
|
||||
self.execute('BEGIN;')
|
||||
self.execute("SET CLIENT_ENCODING TO 'UNICODE';")
|
||||
self.try_json()
|
||||
|
||||
|
||||
class OracleAdapter(BaseAdapter):
|
||||
@@ -3037,6 +3069,8 @@ class MSSQLAdapter(BaseAdapter):
|
||||
if limitby:
|
||||
(lmin, lmax) = limitby
|
||||
sql_s += ' TOP %i' % lmax
|
||||
if 'GROUP BY' in sql_o:
|
||||
sql_o = sql_o[:sql_o.find('ORDER BY ')]
|
||||
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
|
||||
|
||||
TRUE = 1
|
||||
@@ -3179,7 +3213,18 @@ class MSSQL3Adapter(MSSQLAdapter):
|
||||
def select_limitby(self, sql_s, sql_f, sql_t, sql_w, sql_o, limitby):
|
||||
if limitby:
|
||||
(lmin, lmax) = limitby
|
||||
return 'SELECT %s FROM (SELECT %s ROW_NUMBER() over (order by id) AS w_row, %s FROM %s%s%s) TMP WHERE w_row BETWEEN %i AND %s;' % (sql_f,sql_s,sql_f,sql_t,sql_w,sql_o,lmin,lmax)
|
||||
if lmin == 0:
|
||||
sql_s += ' TOP %i' % lmax
|
||||
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
|
||||
lmin += 1
|
||||
sql_o_inner = sql_o[sql_o.find('ORDER BY ')+9:]
|
||||
sql_g_inner = sql_o[:sql_o.find('ORDER BY ')]
|
||||
sql_f_outer = ['f_%s' % f for f in range(len(sql_f.split(',')))]
|
||||
sql_f_inner = [f for f in sql_f.split(',')]
|
||||
sql_f_iproxy = ['%s AS %s' % (o, n) for (o, n) in zip(sql_f_inner, sql_f_outer)]
|
||||
sql_f_iproxy = ', '.join(sql_f_iproxy)
|
||||
sql_f_oproxy = ', '.join(sql_f_outer)
|
||||
return 'SELECT %s %s FROM (SELECT %s ROW_NUMBER() OVER (ORDER BY %s) AS w_row, %s FROM %s%s%s) TMP WHERE w_row BETWEEN %i AND %s;' % (sql_s,sql_f_oproxy,sql_s,sql_f,sql_f_iproxy,sql_t,sql_w,sql_g_inner,lmin,lmax)
|
||||
return 'SELECT %s %s FROM %s%s%s;' % (sql_s,sql_f,sql_t,sql_w,sql_o)
|
||||
def rowslice(self,rows,minimum=0,maximum=None):
|
||||
return rows
|
||||
@@ -3224,6 +3269,7 @@ class MSSQL2Adapter(MSSQLAdapter):
|
||||
def execute(self,a):
|
||||
return self.log_execute(a.decode('utf8'))
|
||||
|
||||
|
||||
class SybaseAdapter(MSSQLAdapter):
|
||||
drivers = ('Sybase',)
|
||||
|
||||
@@ -3365,13 +3411,17 @@ class FireBirdAdapter(BaseAdapter):
|
||||
def SUBSTRING(self,field,parameters):
|
||||
return 'SUBSTRING(%s from %s for %s)' % (self.expand(field), parameters[0], parameters[1])
|
||||
|
||||
def CONTAINS(self, first, second):
|
||||
def CONTAINING(self,first,second):
|
||||
"case in-sensitive like operator"
|
||||
return '(%s CONTAINING %s)' % (self.expand(first),
|
||||
self.expand(second, 'string'))
|
||||
|
||||
def CONTAINS(self, first, second, case_sensitive=False):
|
||||
if first.type in ('string','text'):
|
||||
key = str(second).replace('%','%%')
|
||||
elif first.type.startswith('list:'):
|
||||
key = '|'+str(second).replace('|','||').replace('%','%%')+'|'
|
||||
return '(%s CONTAINING %s)' % (self.expand(first),
|
||||
self.expand(key,'string'))
|
||||
return self.CONTAINING(first,second)
|
||||
|
||||
def _drop(self,table,mode):
|
||||
sequence_name = table._sequence_name
|
||||
@@ -4531,7 +4581,8 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
|
||||
second = [Key.from_path(first._tablename, int(i)) for i in second]
|
||||
return [GAEF(first.name,'in',second,lambda a,b:a in b)]
|
||||
|
||||
def CONTAINS(self,first,second):
|
||||
def CONTAINS(self,first,second,case_sensitive=False):
|
||||
# silently ignoring: GAE can only do case sensitive matches!
|
||||
if not first.type.startswith('list:'):
|
||||
raise SyntaxError("Not supported")
|
||||
return [GAEF(first.name,'=',self.expand(second,first.type[5:]),lambda a,b:b in a)]
|
||||
@@ -4989,7 +5040,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
||||
'list:reference': list,
|
||||
}
|
||||
|
||||
error_messages = {"javascript_needed": "This must yet be replaced" +
|
||||
error_messages = {"javascript_needed": "This must yet be replaced" +
|
||||
" with javascript in order to work."}
|
||||
|
||||
def __init__(self,db,uri='mongodb://127.0.0.1:5984/db',
|
||||
@@ -5068,12 +5119,12 @@ class MongoDBAdapter(NoSQLAdapter):
|
||||
raise ValueError(
|
||||
"invalid objectid argument string: %s" % e)
|
||||
else:
|
||||
raise ValueError("Invalid objectid argument string. " +
|
||||
raise ValueError("Invalid objectid argument string. " +
|
||||
"Requires an integer or base 16 value")
|
||||
elif isinstance(arg, self.ObjectId):
|
||||
return arg
|
||||
if not isinstance(arg, (int, long)):
|
||||
raise TypeError("object_id argument must be of type " +
|
||||
raise TypeError("object_id argument must be of type " +
|
||||
"ObjectId or an objectid representable integer")
|
||||
if arg == 0:
|
||||
hexvalue = "".zfill(24)
|
||||
@@ -5105,7 +5156,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
||||
return value
|
||||
return value
|
||||
|
||||
# Safe determines whether a asynchronious request is done or a
|
||||
# Safe determines whether a asynchronious request is done or a
|
||||
# synchronious action is done
|
||||
# For safety, we use by default synchronious requests
|
||||
def insert(self, table, fields, safe=None):
|
||||
@@ -5225,7 +5276,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
||||
elif len(fields) != 0:
|
||||
tablename = fields[0].tablename
|
||||
else:
|
||||
raise SyntaxError("The table name could not be found in " +
|
||||
raise SyntaxError("The table name could not be found in " +
|
||||
"the query nor from the select statement.")
|
||||
|
||||
mongoqry_dict = self.expand(query)
|
||||
@@ -5251,7 +5302,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
||||
sort=mongosort_list, snapshot=snapshot).count()}
|
||||
else:
|
||||
# pymongo cursor object
|
||||
mongo_list_dicts = ctable.find(mongoqry_dict,
|
||||
mongo_list_dicts = ctable.find(mongoqry_dict,
|
||||
mongofields_dict, skip=limitby_skip,
|
||||
limit=limitby_limit, sort=mongosort_list,
|
||||
snapshot=snapshot)
|
||||
@@ -5348,7 +5399,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
||||
if safe is None:
|
||||
safe = self.safe
|
||||
amount = 0
|
||||
amount = self.count(query, False)
|
||||
amount = self.count(query, False)
|
||||
if not isinstance(query, Query):
|
||||
raise RuntimeError("query type %s is not supported" % \
|
||||
type(query))
|
||||
@@ -5484,8 +5535,9 @@ class MongoDBAdapter(NoSQLAdapter):
|
||||
return {self.expand(first): ('/%s^/' % \
|
||||
self.expand(second, 'string'))}
|
||||
|
||||
def CONTAINS(self, first, second):
|
||||
#There is a technical difference, but mongodb doesn't support
|
||||
def CONTAINS(self, first, second, case_sensitive=False):
|
||||
# silently ignore, only case sensitive
|
||||
# There is a technical difference, but mongodb doesn't support
|
||||
# that, but the result will be the same
|
||||
return {self.expand(first) : ('/%s/' % \
|
||||
self.expand(second, 'string'))}
|
||||
@@ -5515,7 +5567,8 @@ class MongoDBAdapter(NoSQLAdapter):
|
||||
re.escape(self.expand(second, 'string')) + '$'}}
|
||||
|
||||
#TODO verify full compatibilty with official oracle contains operator
|
||||
def CONTAINS(self, first, second):
|
||||
def CONTAINS(self, first, second, case_sensitive=False):
|
||||
# silently ignore, only case sensitive
|
||||
#There is a technical difference, but mongodb doesn't support
|
||||
# that, but the result will be the same
|
||||
#TODO contains operators need to be transformed to Regex
|
||||
@@ -5645,7 +5698,7 @@ class IMAPAdapter(NoSQLAdapter):
|
||||
# This avoids the extra server names retrieval
|
||||
|
||||
imapdb.define_tables({"inbox": "INBOX"})
|
||||
|
||||
|
||||
# Selects without content/attachments/email columns will only
|
||||
# fetch header and flags
|
||||
|
||||
@@ -6038,7 +6091,7 @@ class IMAPAdapter(NoSQLAdapter):
|
||||
|
||||
# keep the requests small for header/flags
|
||||
if any([(field.name in ["content", "size",
|
||||
"attachments", "email"]) for
|
||||
"attachments", "email"]) for
|
||||
field in fields]):
|
||||
imap_fields = "(RFC822 FLAGS)"
|
||||
else:
|
||||
@@ -6305,7 +6358,8 @@ class IMAPAdapter(NoSQLAdapter):
|
||||
# result = "(%s %s)" % (self.expand(first), self.expand(second))
|
||||
return result
|
||||
|
||||
def CONTAINS(self, first, second):
|
||||
def CONTAINS(self, first, second, case_sensitive=False):
|
||||
# silently ignore, only case sensitive
|
||||
result = None
|
||||
name = self.search_fields[first.name]
|
||||
|
||||
@@ -6482,6 +6536,7 @@ ADAPTERS = {
|
||||
'oracle': OracleAdapter,
|
||||
'mssql': MSSQLAdapter,
|
||||
'mssql2': MSSQL2Adapter,
|
||||
'mssql3': MSSQL3Adapter,
|
||||
'sybase': SybaseAdapter,
|
||||
'db2': DB2Adapter,
|
||||
'teradata': TeradataAdapter,
|
||||
@@ -6644,6 +6699,8 @@ class Row(object):
|
||||
def __setitem__(self, key, value):
|
||||
setattr(self, str(key), value)
|
||||
|
||||
__delitem__ = delattr
|
||||
|
||||
__copy__ = lambda self: Row(self)
|
||||
|
||||
__call__ = __getitem__
|
||||
@@ -7081,8 +7138,12 @@ class DAL(object):
|
||||
:attempts (defaults to 5). Number of times to attempt connecting
|
||||
"""
|
||||
|
||||
dbdict = None
|
||||
if uri == '<zombie>' and db_uid is not None: return
|
||||
|
||||
elif isinstance(uri, dict):
|
||||
dbdict = uri
|
||||
uri = dbdict["uri"]
|
||||
codec = dbdict["codec"] or codec
|
||||
if not decode_credentials:
|
||||
credential_decoder = lambda cred: cred
|
||||
else:
|
||||
@@ -7164,32 +7225,44 @@ class DAL(object):
|
||||
self._fake_migrate = fake_migrate
|
||||
self._migrate_enabled = migrate_enabled
|
||||
self._fake_migrate_all = fake_migrate_all
|
||||
if auto_import:
|
||||
self.import_table_definitions(adapter.folder)
|
||||
if auto_import or dbdict:
|
||||
self.import_table_definitions(adapter.folder,
|
||||
items=dbdict["items"])
|
||||
|
||||
@property
|
||||
def tables(self):
|
||||
return self._tables
|
||||
|
||||
def import_table_definitions(self,path,migrate=False,fake_migrate=False):
|
||||
def import_table_definitions(self, path, migrate=False,
|
||||
fake_migrate=False, items=None):
|
||||
pattern = pjoin(path,self._uri_hash+'_*.table')
|
||||
for filename in glob.glob(pattern):
|
||||
tfile = self._adapter.file_open(filename, 'r')
|
||||
try:
|
||||
sql_fields = pickle.load(tfile)
|
||||
name = filename[len(pattern)-7:-6]
|
||||
mf = [(value['sortable'],
|
||||
Field(key,
|
||||
type=value['type'],
|
||||
length=value.get('length',None),
|
||||
notnull=value.get('notnull',False),
|
||||
unique=value.get('unique',False))) \
|
||||
for key, value in sql_fields.iteritems()]
|
||||
mf.sort(lambda a,b: cmp(a[0],b[0]))
|
||||
self.define_table(name,*[item[1] for item in mf],
|
||||
**dict(migrate=migrate,fake_migrate=fake_migrate))
|
||||
finally:
|
||||
self._adapter.file_close(tfile)
|
||||
if items:
|
||||
for tablename, table in items.iteritems():
|
||||
# TODO: read all field/table options
|
||||
fields = []
|
||||
for fieldname, field in table["items"].iteritems():
|
||||
type = field["type"]
|
||||
fields.append(Field(fieldname, type))
|
||||
self.define_table(tablename, *fields)
|
||||
else:
|
||||
for filename in glob.glob(pattern):
|
||||
tfile = self._adapter.file_open(filename, 'r')
|
||||
try:
|
||||
sql_fields = pickle.load(tfile)
|
||||
name = filename[len(pattern)-7:-6]
|
||||
mf = [(value['sortable'],
|
||||
Field(key,
|
||||
type=value['type'],
|
||||
length=value.get('length',None),
|
||||
notnull=value.get('notnull',False),
|
||||
unique=value.get('unique',False))) \
|
||||
for key, value in sql_fields.iteritems()]
|
||||
mf.sort(lambda a,b: cmp(a[0],b[0]))
|
||||
self.define_table(name,*[item[1] for item in mf],
|
||||
**dict(migrate=migrate,
|
||||
fake_migrate=fake_migrate))
|
||||
finally:
|
||||
self._adapter.file_close(tfile)
|
||||
|
||||
def check_reserved_keyword(self, name):
|
||||
"""
|
||||
@@ -7223,14 +7296,14 @@ def index():
|
||||
"/{person.name}/pets[pet.ownedby]/{pet.name}",
|
||||
"/{person.name}/pets[pet.ownedby]/{pet.name}/:field",
|
||||
("/dogs[pet]", db.pet.info=='dog'),
|
||||
("/dogs[pet]/{pet.name.startswith}", db.pet.info=='dog'),
|
||||
("/dogs[pet]/{pet.name.startswith}", db.pet.info=='dog'),
|
||||
]
|
||||
parser = db.parse_as_rest(patterns,args,vars)
|
||||
if parser.status == 200:
|
||||
return dict(content=parser.response)
|
||||
else:
|
||||
raise HTTP(parser.status,parser.error)
|
||||
|
||||
|
||||
def POST(table_name,**vars):
|
||||
if table_name == 'person':
|
||||
return db.person.validate_and_insert(**vars)
|
||||
@@ -7321,10 +7394,12 @@ def index():
|
||||
return Row({'status':200,'pattern':'list',
|
||||
'error':None,'response':patterns})
|
||||
for pattern in patterns:
|
||||
basequery, exposedfields = None, []
|
||||
if isinstance(pattern,tuple):
|
||||
pattern, basequery = pattern
|
||||
else:
|
||||
basequery = None
|
||||
if len(pattern)==2:
|
||||
pattern, basequery = pattern
|
||||
elif len(pattern)>2:
|
||||
pattern, basequery, exposedfields = pattern[0:3]
|
||||
otable=table=None
|
||||
if not isinstance(queries,dict):
|
||||
dbset=db(queries)
|
||||
@@ -7437,7 +7512,10 @@ def index():
|
||||
orderby = [db[table][f] if not f.startswith('~') else ~db[table][f[1:]] for f in ofields]
|
||||
except (KeyError, AttributeError):
|
||||
return Row({'status':400,'error':'invalid orderby','response':None})
|
||||
fields = [field for field in db[table] if field.readable]
|
||||
if exposedfields:
|
||||
fields = [field for field in db[table] if str(field).split('.')[-1] in exposedfields and field.readable]
|
||||
else:
|
||||
fields = [field for field in db[table] if field.readable]
|
||||
count = dbset.count()
|
||||
try:
|
||||
offset = int(vars.get('offset',None) or 0)
|
||||
@@ -7525,6 +7603,38 @@ def index():
|
||||
if on_define: on_define(table)
|
||||
return table
|
||||
|
||||
def as_dict(self, flat=False, sanitize=True):
|
||||
dbname = codec = uid = uri = None
|
||||
if not sanitize:
|
||||
uri, dbname, codec, uid = (self._uri, self._dbname,
|
||||
self._db_codec, self._db_uid)
|
||||
db_as_dict = dict(items={}, tables=[], uri=uri, dbname=dbname,
|
||||
codec=codec, uid=uid)
|
||||
for table in self:
|
||||
tablename = str(table)
|
||||
db_as_dict["tables"].append(tablename)
|
||||
db_as_dict["items"][tablename] = table.as_dict(flat=flat,
|
||||
sanitize=sanitize)
|
||||
return db_as_dict
|
||||
|
||||
def as_xml(self, sanitize=True):
|
||||
if not have_serializers:
|
||||
raise ImportError("No xml serializers available")
|
||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||
return serializers.xml(d)
|
||||
|
||||
def as_json(self, sanitize=True):
|
||||
if not have_serializers:
|
||||
raise ImportError("No json serializers available")
|
||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||
return serializers.json(d)
|
||||
|
||||
def as_yaml(self, sanitize=True):
|
||||
if not have_serializers:
|
||||
raise ImportError("No YAML serializers available")
|
||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||
return serializers.yaml(d)
|
||||
|
||||
def __contains__(self, tablename):
|
||||
try:
|
||||
return tablename in self.tables
|
||||
@@ -7576,6 +7686,9 @@ def index():
|
||||
query = self._adapter.id_query(query)
|
||||
elif isinstance(query,Field):
|
||||
query = query!=None
|
||||
elif isinstance(query, dict):
|
||||
icf = query.get("ignore_common_filters")
|
||||
if icf: ignore_common_filters = icf
|
||||
return Set(self, query, ignore_common_filters=ignore_common_filters)
|
||||
|
||||
def commit(self):
|
||||
@@ -8411,6 +8524,34 @@ class Table(object):
|
||||
if id_map and cid is not None:
|
||||
id_map_self[int(line[cid])] = new_id
|
||||
|
||||
def as_dict(self, flat=False, sanitize=True):
|
||||
tablename = str(self)
|
||||
table_as_dict = dict(name=tablename, items={}, fields=[])
|
||||
for field in self:
|
||||
if (field.readable or field.writable) or (not sanitize):
|
||||
table_as_dict["fields"].append(field.name)
|
||||
table_as_dict["items"][field.name] = \
|
||||
field.as_dict(flat=flat, sanitize=sanitize)
|
||||
return table_as_dict
|
||||
|
||||
def as_xml(self, sanitize=True):
|
||||
if not have_serializers:
|
||||
raise ImportError("No xml serializers available")
|
||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||
return serializers.xml(d)
|
||||
|
||||
def as_json(self, sanitize=True):
|
||||
if not have_serializers:
|
||||
raise ImportError("No json serializers available")
|
||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||
return serializers.json(d)
|
||||
|
||||
def as_yaml(self, sanitize=True):
|
||||
if not have_serializers:
|
||||
raise ImportError("No YAML serializers available")
|
||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||
return serializers.yaml(d)
|
||||
|
||||
def with_alias(self, alias):
|
||||
return self._db._adapter.alias(self,alias)
|
||||
|
||||
@@ -8438,6 +8579,7 @@ class Expression(object):
|
||||
first=None,
|
||||
second=None,
|
||||
type=None,
|
||||
**optional_args
|
||||
):
|
||||
|
||||
self.db = db
|
||||
@@ -8450,6 +8592,7 @@ class Expression(object):
|
||||
self.type = first.type
|
||||
else:
|
||||
self.type = type
|
||||
self.optional_args = optional_args
|
||||
|
||||
def sum(self):
|
||||
db = self.db
|
||||
@@ -8639,24 +8782,30 @@ class Expression(object):
|
||||
raise SyntaxError("endswith used with incompatible field type")
|
||||
return Query(db, db._adapter.ENDSWITH, self, value)
|
||||
|
||||
def contains(self, value, all=False):
|
||||
def contains(self, value, all=False, case_sensitive=False):
|
||||
"""
|
||||
The case_sensitive parameters is only useful for PostgreSQL
|
||||
For other RDMBs it is ignored and contains is always case in-sensitive
|
||||
For MongoDB and GAE contains is always case sensitive
|
||||
"""
|
||||
db = self.db
|
||||
if isinstance(value,(list, tuple)):
|
||||
subqueries = [self.contains(str(v).strip()) for v in value if str(v).strip()]
|
||||
subqueries = [self.contains(str(v).strip(),case_sensitive=case_sensitive)
|
||||
for v in value if str(v).strip()]
|
||||
if not subqueries:
|
||||
return self.contains('')
|
||||
else:
|
||||
return reduce(all and AND or OR,subqueries)
|
||||
if not self.type in ('string', 'text', 'json') and not self.type.startswith('list:'):
|
||||
raise SyntaxError("contains used with incompatible field type")
|
||||
return Query(db, db._adapter.CONTAINS, self, value)
|
||||
return Query(db, db._adapter.CONTAINS, self, value, case_sensitive=case_sensitive)
|
||||
|
||||
def with_alias(self, alias):
|
||||
db = self.db
|
||||
return Expression(db, db._adapter.AS, self, alias, self.type)
|
||||
|
||||
# GIS expressions
|
||||
|
||||
|
||||
def st_asgeojson(self, precision=15, options=0, version=1):
|
||||
return Expression(self.db, self.db._adapter.ST_ASGEOJSON, self,
|
||||
dict(precision=precision, options=options,
|
||||
@@ -8990,7 +9139,7 @@ class Field(Expression):
|
||||
stream = self.uploadfs.open(name, 'rb')
|
||||
else:
|
||||
# ## if file is on regular filesystem
|
||||
stream = open(pjoin(file_properties['path'], name), 'rb')
|
||||
stream = pjoin(file_properties['path'], name)
|
||||
return (filename, stream)
|
||||
|
||||
def retrieve_file_properties(self, name, path=None):
|
||||
@@ -9056,6 +9205,76 @@ class Field(Expression):
|
||||
def count(self, distinct=None):
|
||||
return Expression(self.db, self.db._adapter.COUNT, self, distinct, 'integer')
|
||||
|
||||
def as_dict(self, flat=False, sanitize=True):
|
||||
attrs = ("readable", "writable", "label", "default", "name",
|
||||
"type", "represent", "compute")
|
||||
SERIALIZABLE_TYPES = (int, long, basestring, dict, list,
|
||||
float, tuple, bool, type(None))
|
||||
def flatten(obj):
|
||||
if flat:
|
||||
if isinstance(obj, flatten.__class__):
|
||||
return str(type(obj))
|
||||
elif isinstance(obj, type):
|
||||
try:
|
||||
obj = str(obj).split("'")[1]
|
||||
except IndexError:
|
||||
obj = str(obj)
|
||||
elif not isinstance(obj, SERIALIZABLE_TYPES):
|
||||
obj = str(obj)
|
||||
return obj
|
||||
|
||||
def filter_requires(t, r):
|
||||
if sanitize and any([keyword in str(t).upper() for
|
||||
keyword in ("CRYPT", "IS_STRONG")]):
|
||||
return None
|
||||
for k, v in r.items():
|
||||
if k == "other":
|
||||
if not isinstance(v, dict):
|
||||
other = v.__dict__
|
||||
else: other = v
|
||||
r[k] = {flatten(type(v)):
|
||||
filter_requires(type(v), other)}
|
||||
elif flat and (not isinstance(v, SERIALIZABLE_TYPES)):
|
||||
r[k] = str(v)
|
||||
return r
|
||||
|
||||
if isinstance(self.requires, (tuple, list, set)):
|
||||
requires = dict([(flatten(type(r)),
|
||||
filter_requires(type(r), r.__dict__)) for
|
||||
r in self.requires])
|
||||
else:
|
||||
requires = {flatten(type(self.requires)):
|
||||
filter_requires(type(self.requires),
|
||||
self.requires.__dict__)}
|
||||
|
||||
d = dict(colname="%s.%s" % (self.tablename, self.name),
|
||||
requires=requires)
|
||||
d.update([(attr, flatten(getattr(self, attr))) for attr in attrs])
|
||||
return d
|
||||
|
||||
def as_xml(self, sanitize=True):
|
||||
if have_serializers:
|
||||
xml = serializers.xml
|
||||
else:
|
||||
raise ImportError("No xml serializers available")
|
||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||
return xml(d)
|
||||
|
||||
def as_json(self, sanitize=True):
|
||||
if have_serializers:
|
||||
json = serializers.json
|
||||
else:
|
||||
raise ImportError("No json serializers available")
|
||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||
return json(d)
|
||||
|
||||
def as_yaml(self, sanitize=True):
|
||||
if have_serializers:
|
||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||
return serializers.yaml(d)
|
||||
else:
|
||||
raise ImportError("No YAML serializers available")
|
||||
|
||||
def __nonzero__(self):
|
||||
return True
|
||||
|
||||
@@ -9087,12 +9306,14 @@ class Query(object):
|
||||
first=None,
|
||||
second=None,
|
||||
ignore_common_filters = False,
|
||||
**optional_args
|
||||
):
|
||||
self.db = self._db = db
|
||||
self.op = op
|
||||
self.first = first
|
||||
self.second = second
|
||||
self.ignore_common_filters = ignore_common_filters
|
||||
self.optional_args = optional_args
|
||||
|
||||
def __repr__(self):
|
||||
return '<Query %s>' % BaseAdapter.expand(self.db._adapter,self)
|
||||
@@ -9111,10 +9332,77 @@ class Query(object):
|
||||
return self.first
|
||||
return Query(self.db,self.db._adapter.NOT,self)
|
||||
|
||||
def __eq__(self, other):
|
||||
return repr(self) == repr(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not (self == other)
|
||||
|
||||
def case(self,t=1,f=0):
|
||||
return self.db._adapter.CASE(self,t,f)
|
||||
|
||||
def as_dict(self, flat=False, sanitize=True):
|
||||
"""Experimental stuff
|
||||
|
||||
This allows to return a plain dictionary with the basic
|
||||
query representation. Can be used with json/xml services
|
||||
for client-side db I/O
|
||||
|
||||
Example:
|
||||
>>> q = db.auth_user.id != 0
|
||||
>>> q.as_dict(flat=True)
|
||||
{"op": "NE", "first":{"tablename": "auth_user",
|
||||
"fieldname": "id"},
|
||||
"second":0}
|
||||
"""
|
||||
|
||||
SERIALIZABLE_TYPES = (tuple, dict, list, int, long, float,
|
||||
basestring, type(None), bool)
|
||||
def loop(d):
|
||||
newd = dict()
|
||||
for k, v in d.items():
|
||||
if k in ("first", "second"):
|
||||
if isinstance(v, self.__class__):
|
||||
newd[k] = loop(v.__dict__)
|
||||
elif isinstance(v, Field):
|
||||
newd[k] = {"tablename": v._tablename,
|
||||
"fieldname": v.name}
|
||||
elif isinstance(v, Expression):
|
||||
newd[k] = loop(v.__dict__)
|
||||
elif isinstance(v, SERIALIZABLE_TYPES):
|
||||
newd[k] = v
|
||||
elif k == "op":
|
||||
if callable(v):
|
||||
newd[k] = v.__name__
|
||||
elif isinstance(v, basestring):
|
||||
newd[k] = v
|
||||
else: pass # not callable or string
|
||||
elif isinstance(v, SERIALIZABLE_TYPES):
|
||||
if isinstance(v, dict):
|
||||
newd[k] = loop(v)
|
||||
else: newd[k] = v
|
||||
return newd
|
||||
|
||||
if flat:
|
||||
return loop(self.__dict__)
|
||||
else: return self.__dict__
|
||||
|
||||
|
||||
def as_xml(self, sanitize=True):
|
||||
if have_serializers:
|
||||
xml = serializers.xml
|
||||
else:
|
||||
raise ImportError("No xml serializers available")
|
||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||
return xml(d)
|
||||
|
||||
def as_json(self, sanitize=True):
|
||||
if have_serializers:
|
||||
json = serializers.json
|
||||
else:
|
||||
raise ImportError("No json serializers available")
|
||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||
return json(d)
|
||||
|
||||
def xorify(orderby):
|
||||
if not orderby:
|
||||
@@ -9148,6 +9436,12 @@ class Set(object):
|
||||
def __init__(self, db, query, ignore_common_filters = None):
|
||||
self.db = db
|
||||
self._db = db # for backward compatibility
|
||||
self.dquery = None
|
||||
|
||||
# if query is a dict, parse it
|
||||
if isinstance(query, dict):
|
||||
query = self.parse(query)
|
||||
|
||||
if not ignore_common_filters is None and \
|
||||
use_common_filters(query) == ignore_common_filters:
|
||||
query = copy.copy(query)
|
||||
@@ -9166,10 +9460,10 @@ class Set(object):
|
||||
query = query!=None
|
||||
if self.query:
|
||||
return Set(self.db, self.query & query,
|
||||
ignore_common_filters = ignore_common_filters)
|
||||
ignore_common_filters=ignore_common_filters)
|
||||
else:
|
||||
return Set(self.db, query,
|
||||
ignore_common_filters = ignore_common_filters)
|
||||
ignore_common_filters=ignore_common_filters)
|
||||
|
||||
def _count(self,distinct=None):
|
||||
return self.db._adapter._count(self.query,distinct)
|
||||
@@ -9195,6 +9489,94 @@ class Set(object):
|
||||
fields = db[tablename]._listify(update_fields,update=True)
|
||||
return db._adapter._update(tablename,self.query,fields)
|
||||
|
||||
def as_dict(self, flat=False, sanitize=True):
|
||||
if flat:
|
||||
uid = dbname = uri = None
|
||||
codec = self.db._db_codec
|
||||
if not sanitize:
|
||||
uri, dbname, uid = (self.db._dbname, str(self.db),
|
||||
self.db._db_uid)
|
||||
d = {"query": self.query.as_dict(flat=flat)}
|
||||
d["db"] = {"uid": uid, "codec": codec,
|
||||
"name": dbname, "uri": uri}
|
||||
return d
|
||||
else: return self.__dict__
|
||||
|
||||
def as_xml(self, sanitize=True):
|
||||
if have_serializers:
|
||||
xml = serializers.xml
|
||||
else:
|
||||
raise ImportError("No xml serializers available")
|
||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||
return xml(d)
|
||||
|
||||
def as_json(self, sanitize=True):
|
||||
if have_serializers:
|
||||
json = serializers.json
|
||||
else:
|
||||
raise ImportError("No json serializers available")
|
||||
d = self.as_dict(flat=True, sanitize=sanitize)
|
||||
return json(d)
|
||||
|
||||
def parse(self, dquery):
|
||||
"Experimental: Turn a dictionary into a Query object"
|
||||
self.dquery = dquery
|
||||
return self.build(self.dquery)
|
||||
|
||||
def build(self, d):
|
||||
"Experimental: see .parse()"
|
||||
op, first, second = (d["op"], d["first"],
|
||||
d.get("second", None))
|
||||
left = right = built = None
|
||||
|
||||
if op in ("AND", "OR"):
|
||||
if not (type(first), type(second)) == (dict, dict):
|
||||
raise SyntaxError("Invalid AND/OR query")
|
||||
if op == "AND":
|
||||
built = self.build(first) & self.build(second)
|
||||
else: built = self.build(first) | self.build(second)
|
||||
|
||||
elif op == "NOT":
|
||||
if first is None:
|
||||
raise SyntaxError("Invalid NOT query")
|
||||
built = ~self.build(first)
|
||||
else:
|
||||
# normal operation (GT, EQ, LT, ...)
|
||||
for k, v in {"left": first, "right": second}.items():
|
||||
if isinstance(v, dict) and v.get("op"):
|
||||
v = self.build(v)
|
||||
if isinstance(v, dict) and ("tablename" in v):
|
||||
v = self.db[v["tablename"]][v["fieldname"]]
|
||||
if k == "left": left = v
|
||||
else: right = v
|
||||
|
||||
if hasattr(self.db._adapter, op):
|
||||
opm = getattr(self.db._adapter, op)
|
||||
|
||||
if op == "EQ": built = left == right
|
||||
elif op == "NE": built = left != right
|
||||
elif op == "GT": built = left > right
|
||||
elif op == "GE": built = left >= right
|
||||
elif op == "LT": built = left < right
|
||||
elif op == "LE": built = left <= right
|
||||
elif op in ("JOIN", "LEFT_JOIN", "RANDOM", "ALLOW_NULL"):
|
||||
built = Expression(self.db, opm)
|
||||
elif op in ("LOWER", "UPPER", "EPOCH", "PRIMARY_KEY",
|
||||
"COALESCE_ZERO", "RAW", "INVERT"):
|
||||
built = Expression(self.db, opm, left)
|
||||
elif op in ("COUNT", "EXTRACT", "AGGREGATE", "SUBSTRING",
|
||||
"REGEXP", "LIKE", "ILIKE", "STARTSWITH",
|
||||
"ENDSWITH", "ADD", "SUB", "MUL", "DIV",
|
||||
"MOD", "AS", "ON", "COMMA", "NOT_NULL",
|
||||
"COALESCE", "CONTAINS", "BELONGS"):
|
||||
built = Expression(self.db, opm, left, right)
|
||||
# expression as string
|
||||
elif not (left or right): built = Expression(self.db, op)
|
||||
else:
|
||||
raise SyntaxError("Operator not supported: %s" % op)
|
||||
|
||||
return built
|
||||
|
||||
def isempty(self):
|
||||
return not self.select(limitby=(0,1))
|
||||
|
||||
|
||||
+7
-5
@@ -308,8 +308,8 @@ class Response(Storage):
|
||||
chunk_size=DEFAULT_CHUNK_SIZE,
|
||||
request=None,
|
||||
attachment=False,
|
||||
filename=None,
|
||||
):
|
||||
filename=None
|
||||
):
|
||||
"""
|
||||
if a controller function::
|
||||
|
||||
@@ -385,6 +385,8 @@ class Response(Storage):
|
||||
downloads from http://..../download/filename
|
||||
"""
|
||||
|
||||
current.session.forget(current.response)
|
||||
|
||||
if not request.args:
|
||||
raise HTTP(404)
|
||||
name = request.args[-1]
|
||||
@@ -403,8 +405,8 @@ class Response(Storage):
|
||||
raise HTTP(404)
|
||||
headers = self.headers
|
||||
headers['Content-Type'] = contenttype(name)
|
||||
if download_filename == None:
|
||||
download_filename = filename
|
||||
if download_filename == None:
|
||||
download_filename = filename
|
||||
if attachment:
|
||||
headers['Content-Disposition'] = \
|
||||
'attachment; filename="%s"' % download_filename.replace('"','\"')
|
||||
@@ -689,7 +691,7 @@ class Session(Storage):
|
||||
|
||||
def forget(self, response=None):
|
||||
self._close(response)
|
||||
self._forget = True
|
||||
self._forget = True
|
||||
|
||||
def _try_store_in_cookie(self, request, response):
|
||||
if response.session_storage_type != 'cookie':
|
||||
|
||||
+1
-1
@@ -553,7 +553,7 @@ class XML(XmlComponent):
|
||||
'img/',
|
||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
||||
'table', 'tr', 'td', 'div',
|
||||
'strong',
|
||||
'strong','span',
|
||||
],
|
||||
allowed_attributes={
|
||||
'a': ['href', 'title', 'target'],
|
||||
|
||||
@@ -12,6 +12,7 @@ Contains:
|
||||
|
||||
"""
|
||||
|
||||
if False: import import_all # DO NOT REMOVE PART OF FREEZE PROCESS
|
||||
import gc
|
||||
import cgi
|
||||
import cStringIO
|
||||
|
||||
@@ -212,6 +212,7 @@ def sanitize(text, permitted_tags=[
|
||||
'img/',
|
||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
||||
'table', 'tr', 'td', 'div',
|
||||
'strong', 'span',
|
||||
],
|
||||
allowed_attributes={
|
||||
'a': ['href', 'title'],
|
||||
|
||||
+22
-2
@@ -19,6 +19,11 @@ except ImportError:
|
||||
except:
|
||||
import contrib.simplejson as json_parser # fallback to pure-Python module
|
||||
|
||||
have_yaml = True
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
have_yaml = False
|
||||
|
||||
def loads_json(o):
|
||||
# deserialize a json string
|
||||
@@ -68,8 +73,12 @@ def xml(value, encoding='UTF-8', key='document', quote=True):
|
||||
|
||||
|
||||
def json(value, default=custom_json):
|
||||
return json_parser.dumps(value, default=default)
|
||||
|
||||
# replace JavaScript incompatible spacing
|
||||
# http://timelessrepo.com/json-isnt-a-javascript-subset
|
||||
return json_parser.dumps(value,
|
||||
default=default).replace(ur'\u2028',
|
||||
'\\u2028').replace(ur'\2029',
|
||||
'\\u2029')
|
||||
|
||||
def csv(value):
|
||||
return ''
|
||||
@@ -119,3 +128,14 @@ def rss(feed):
|
||||
pubDate=entry.get('created_on', now)
|
||||
) for entry in feed.get('entries', [])])
|
||||
return rss.to_xml(encoding='utf-8')
|
||||
|
||||
def yaml(data):
|
||||
if have_yaml:
|
||||
return yaml.dump(data)
|
||||
else: raise ImportError("No YAML serializer available")
|
||||
|
||||
def loads_yaml(data):
|
||||
if have_yaml:
|
||||
return yaml.load(data)
|
||||
else: raise ImportError("No YAML serializer available")
|
||||
|
||||
|
||||
+6
-3
@@ -1867,7 +1867,10 @@ class SQLFORM(FORM):
|
||||
dbset = db(query)
|
||||
tablenames = db._adapter.tables(dbset.query)
|
||||
if left is not None:
|
||||
tablenames += db._adapter.tables(left)
|
||||
if not isinstance(left, (list, tuple)):
|
||||
left = [left]
|
||||
for join in left:
|
||||
tablenames += db._adapter.tables(join)
|
||||
tables = [db[tablename] for tablename in tablenames]
|
||||
if not fields:
|
||||
fields = reduce(lambda a, b: a + b,
|
||||
@@ -2511,7 +2514,7 @@ class SQLFORM(FORM):
|
||||
except (KeyError, ValueError, TypeError):
|
||||
redirect(URL(args=table._tablename))
|
||||
if nargs == len(args) + 1:
|
||||
query = table._id > 0
|
||||
query = table._id != None
|
||||
|
||||
# filter out data info for displayed table
|
||||
if table._tablename in constraints:
|
||||
@@ -2549,7 +2552,7 @@ class SQLFORM(FORM):
|
||||
links_in_grid=links_in_grid,
|
||||
user_signature=user_signature, **kwargs)
|
||||
if isinstance(grid, DIV):
|
||||
header = table._plural + (field and ' for ' + field.name or '')
|
||||
header = table._plural + (field and ' for ' + field.label or '')
|
||||
breadcrumbs.append(LI(A(T(header), _class=trap_class(),
|
||||
_href=url()), _class='active w2p_grid_breadcrumb_elem'))
|
||||
grid.insert(
|
||||
|
||||
+2
-2
@@ -46,8 +46,8 @@ def stream_file_or_304_or_206(
|
||||
request=None,
|
||||
headers={},
|
||||
status=200,
|
||||
error_message=None,
|
||||
):
|
||||
error_message=None
|
||||
):
|
||||
if error_message is None:
|
||||
error_message = rewrite.THREAD_LOCAL.routes.error_message % 'invalid request'
|
||||
try:
|
||||
|
||||
+6
-1
@@ -5092,7 +5092,12 @@ class Wiki(object):
|
||||
url = URL(args=('_edit', slug))
|
||||
return dict(content=A('Create page "%s"' % slug, _href=url, _class="btn"))
|
||||
else:
|
||||
return dict(content=XML(self.fix_hostname(page.html)))
|
||||
return dict(title=page.title,
|
||||
slug=page.slug,
|
||||
content=XML(self.fix_hostname(page.html)),
|
||||
tags=page.tags,
|
||||
created_on=page.created_on,
|
||||
modified_on=page.modified_on)
|
||||
elif current.request.extension == 'load':
|
||||
return self.fix_hostname(page.html) if page else ''
|
||||
else:
|
||||
|
||||
@@ -1,19 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script for installing Web2py with Nginx and Uwsgi on Centos 5
|
||||
# Created By Hutchinson
|
||||
# Modified by spametki
|
||||
# License: BSD
|
||||
|
||||
# It was originally posted in this web2py-users group thread:
|
||||
# https://groups.google.com/forum/?fromgroups#!topic/web2py/O4c4Jfr18tM
|
||||
|
||||
# There are lots of subtleties of ownership, and one has to take care
|
||||
# when installing python 2.6 not to stop the systems python2.4 from working.
|
||||
|
||||
# NOTE: The only thing that should need changing for
|
||||
# each installation is the $BASEARCH (base architecture) of the machine.
|
||||
# This is determined by doing uname -i. This is needed for the nginx installation.
|
||||
# -------------------------------------------------------------------
|
||||
# Description : Installation and basic configuration of web2py,
|
||||
# uWSGI, andNGINX.
|
||||
# in CentOS 5.x GNU/Linux
|
||||
# Usage : Copy the script in /home/username and run it as root,
|
||||
# you may need to allow execution (chmod +x)
|
||||
#
|
||||
# WARNING: This script was modified to install compiled
|
||||
# versions of Python and other packages that may be
|
||||
# available at your Centos package repository.
|
||||
# This change was made in order to get the latest
|
||||
# stable libraries available for avoiding compatibility
|
||||
#
|
||||
# A bug was reported for the 2.7.3 version of python
|
||||
# here http://bugs.python.org/issue14572
|
||||
# in case you choose to change to VERSION=2.7
|
||||
# (see below) mind that the automatic patch applied
|
||||
# could not work for your environment. By default,
|
||||
# Python 2.6 is installed.
|
||||
#
|
||||
# It was originally posted in this web2py-users group
|
||||
# thread: https://groups.google.com/forum/?fromgroups#
|
||||
# !topic/web2py/O4c4Jfr18tM
|
||||
#
|
||||
# There are lots of subtleties of ownership, and one
|
||||
# has to take care when installing python 2.6 not to
|
||||
# stop the systems python2.4 from working.
|
||||
#
|
||||
# NOTE: The only thing that should need changing for
|
||||
# each installation is the $BASEARCH (base
|
||||
# architecture)
|
||||
# of the machine. This is determined by doing uname -i.
|
||||
# This is needed for the nginx installation.
|
||||
#
|
||||
# File : setup-web2py-nginx-uwsgi-on-centos.sh
|
||||
# Author : Peter Hutchinson
|
||||
# Modified by : Alan Etkin
|
||||
# Email : spametki@gmail.com
|
||||
# Copyright : web2py
|
||||
# Date : 2013-01-28
|
||||
# Disclaimers : This script is provided "as is", without warranty of
|
||||
# any kind.
|
||||
# Licence : BSD
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# Retrieve base architecture
|
||||
BASEARCH=$(uname -i)
|
||||
@@ -32,8 +62,8 @@ bzip2-devel sqlite-devel db4-devel openssl-devel tk-devel bluez-libs-devel
|
||||
# to fit your deployment needs.
|
||||
|
||||
# Python options
|
||||
PREFIX=2.7
|
||||
VERSION=2.7.3
|
||||
PREFIX=2.6
|
||||
VERSION=2.6.8
|
||||
|
||||
# uWSGI options
|
||||
version=uwsgi-1.2.4
|
||||
@@ -47,8 +77,10 @@ wget http://www.python.org/ftp/python/$VERSION/Python-$VERSION.tgz
|
||||
tar xvfz Python-$VERSION.tgz
|
||||
cd Python-$VERSION
|
||||
|
||||
echo "Applying patch for sqlite3 bug from post http://bugs.python.org/msg161076"
|
||||
curl -sk https://raw.github.com/gist/2727063/ | patch -p1
|
||||
if [ "$VERSION" == "2.7.3" ]; then
|
||||
echo "Applying patch for sqlite3 bug from post http://bugs.python.org/msg161076"
|
||||
curl -sk https://raw.github.com/gist/2727063/ | patch -p1
|
||||
fi
|
||||
|
||||
./configure --prefix=/opt/python$PREFIX --with-threads --enable-shared
|
||||
make
|
||||
|
||||
@@ -19,19 +19,50 @@ apt-get -y install build-essential python-dev libxml2-dev python-pip unzip
|
||||
pip install --upgrade pip
|
||||
PIPPATH=`which pip`
|
||||
$PIPPATH install --upgrade uwsgi
|
||||
# Create common nginx sections
|
||||
mkdir /etc/nginx/conf.d/web2py
|
||||
echo '
|
||||
gzip_static on;
|
||||
gzip_http_version 1.1;
|
||||
gzip_proxied expired no-cache no-store private auth;
|
||||
gzip_disable "MSIE [1-6]\.";
|
||||
gzip_vary on;
|
||||
' > /etc/nginx/conf.d/web2py/gzip_static.conf
|
||||
echo '
|
||||
gzip on;
|
||||
gzip_disable "msie6";
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
' > /etc/nginx/conf.d/web2py/gzip.conf
|
||||
# Create configuration file /etc/nginx/sites-available/web2py
|
||||
echo 'server {
|
||||
listen 80;
|
||||
server_name $hostname;
|
||||
#to enable correct use of response.static_version
|
||||
###to enable correct use of response.static_version
|
||||
#location ~* /(\w+)/static(?:/_[\d]+\.[\d]+\.[\d]+)?/(.*)$ {
|
||||
# alias /home/www-data/web2py/applications/$1/static/$2;
|
||||
# expires max;
|
||||
#}
|
||||
###
|
||||
|
||||
###if you use something like myapp = dict(languages=['en', 'it', 'jp'], default_language='en') in your routes.py
|
||||
#location ~* /(\w+)/(en|it|jp)/static/(.*)$ {
|
||||
# alias /home/www-data/web2py/applications/$1/;
|
||||
# try_files static/$2/$3 static/$3 =404;
|
||||
#}
|
||||
###
|
||||
location ~* /(\w+)/static/ {
|
||||
root /home/www-data/web2py/applications/;
|
||||
#remove next comment on production
|
||||
#expires max;
|
||||
### if you want to use pre-gzipped static files (recommended)
|
||||
### check scripts/zip_static_files.py and remove the comments
|
||||
# include /etc/nginx/conf.d/web2py/gzip_static.conf;
|
||||
###
|
||||
}
|
||||
location / {
|
||||
#uwsgi_pass 127.0.0.1:9001;
|
||||
@@ -39,6 +70,14 @@ echo 'server {
|
||||
include uwsgi_params;
|
||||
uwsgi_param UWSGI_SCHEME $scheme;
|
||||
uwsgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||
|
||||
###remove the comments to turn on if you want gzip compression of your pages
|
||||
# include /etc/nginx/conf.d/web2py/gzip.conf;
|
||||
### end gzip section
|
||||
|
||||
### remove the comments if you use uploads (max 10 MB)
|
||||
#client_max_body_size 10m;
|
||||
###
|
||||
}
|
||||
}
|
||||
server {
|
||||
@@ -58,7 +97,15 @@ server {
|
||||
include uwsgi_params;
|
||||
uwsgi_param UWSGI_SCHEME $scheme;
|
||||
uwsgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||
###remove the comments to turn on if you want gzip compression of your pages
|
||||
# include /etc/nginx/conf.d/web2py/gzip.conf;
|
||||
### end gzip section
|
||||
### remove the comments if you want to enable uploads (max 10 MB)
|
||||
#client_max_body_size 10m;
|
||||
###
|
||||
}
|
||||
## if you serve static files through https, copy here the section
|
||||
## from the previous server instance to manage static files
|
||||
|
||||
}' >/etc/nginx/sites-available/web2py
|
||||
|
||||
@@ -78,25 +125,26 @@ sudo mkdir /etc/uwsgi
|
||||
sudo mkdir /var/log/uwsgi
|
||||
|
||||
# Create configuration file /etc/uwsgi/web2py.xml
|
||||
echo '<uwsgi>
|
||||
<socket>/tmp/web2py.socket</socket>
|
||||
<pythonpath>/home/www-data/web2py/</pythonpath>
|
||||
<mount>/=wsgihandler:application</mount>
|
||||
<master/>
|
||||
<processes>4</processes>
|
||||
<harakiri>60</harakiri>
|
||||
<reload-mercy>8</reload-mercy>
|
||||
<cpu-affinity>1</cpu-affinity>
|
||||
<stats>/tmp/stats.socket</stats>
|
||||
<max-requests>2000</max-requests>
|
||||
<limit-as>512</limit-as>
|
||||
<reload-on-as>256</reload-on-as>
|
||||
<reload-on-rss>192</reload-on-rss>
|
||||
<uid>www-data</uid>
|
||||
<gid>www-data</gid>
|
||||
<cron>0 0 -1 -1 -1 python /home/www-data/web2py/web2py.py -Q -S welcome -M -R scripts/sessions2trash.py -A -o</cron>
|
||||
<no-orphans/>
|
||||
</uwsgi>' >/etc/uwsgi/web2py.xml
|
||||
echo '[uwsgi]
|
||||
|
||||
socket = /tmp/web2py.socket
|
||||
pythonpath = /home/www-data/web2py/
|
||||
mount = /=wsgihandler:application
|
||||
processes = 4
|
||||
master = true
|
||||
harakiri = 60
|
||||
reload-mercy = 8
|
||||
cpu-affinity = 1
|
||||
stats = /tmp/stats.socket
|
||||
max-requests = 2000
|
||||
limit-as = 512
|
||||
reload-on-as = 256
|
||||
reload-on-rss = 192
|
||||
uid = www-data
|
||||
gid = www-data
|
||||
cron = 0 0 -1 -1 -1 python /home/www-data/web2py/web2py.py -Q -S welcome -M -R scripts/sessions2trash.py -A -o
|
||||
no-orphans = true
|
||||
' >/etc/uwsgi/web2py.ini
|
||||
|
||||
#Create a configuration file for uwsgi in emperor-mode
|
||||
#for Upstart in /etc/init/uwsgi-emperor.conf
|
||||
@@ -135,4 +183,4 @@ start uwsgi-emperor
|
||||
## and stop it with
|
||||
# stop uwsgi-emperor
|
||||
## to reload web2py only (without restarting uwsgi)
|
||||
# touch /etc/uwsgi/web2py.xml
|
||||
# touch /etc/uwsgi/web2py.xml
|
||||
|
||||
Reference in New Issue
Block a user