Compare commits

...
44 Commits
Author SHA1 Message Date
mdipierro 58533954dc R-2.10.4 2015-04-26 09:07:07 -05:00
mdipierro 236dc4b943 fixed submodule tracking 2015-04-26 09:04:55 -05:00
mdipierro 6612fd1cfe told git to track the right submodule branch 2015-04-26 08:50:16 -05:00
mdipierro 520950ba74 track track 15.03-maintenance (19/04/15) 2015-04-26 08:44:45 -05:00
mdipierro e943aa9c25 minor compatibility fix in ldap_auth 2015-04-26 08:39:47 -05:00
mdipierro 756aec7206 Merge pull request #944 from stephenrauch/ldap_auth-fix-nosql
Fix ldap_auth for NoSQL databases
2015-04-26 08:29:26 -05:00
mdipierro 970e2ed35c Merge pull request #939 from smorrison/slack_webhook_support
add support for reporting web2py errors via slack.com
2015-04-26 08:29:00 -05:00
mdipierro 1388c39636 Merge pull request #935 from niphlod/docs/dal
extend underline for proper sphinx formatting
2015-04-26 08:27:48 -05:00
mdipierro 6e84737924 Merge pull request #934 from niphlod/fix/931
fixes #931 . Thanks @butsyk for spotting the bug
2015-04-26 08:27:35 -05:00
stephenrauch 0ad50630f2 Fix ldap_auth for NoSQL databases
Unroll query with join to two queries when working with DB's which don't
support joins.
2015-04-24 11:02:18 -07:00
Sean Morrison f42ee15f5f add support for reporting web2py errors via slack.com 2015-04-22 19:00:21 -05:00
niphlod 2b0bfba649 extend underline for proper sphinx formatting 2015-04-21 23:59:07 +02:00
niphlod 9f1edf267d fixes #931 . Thanks @butsyk for spotting the bug 2015-04-21 21:59:42 +02:00
mdipierro f3bda9ad02 changed version 2015-04-20 18:02:17 -05:00
mdipierro f8afc76263 Merge pull request #930 from gi0baro/master
pydal -> track 15.03-maintenance (20/04/15)
2015-04-20 10:39:29 -05:00
gi0baro 65b4aaf842 pydal -> track 15.03-maintenance (20/04/15) 2015-04-20 17:29:22 +02:00
mdipierro 1b729cfbfc Merge pull request #928 from niphlod/fix/920
small typo. Fixes #920
2015-04-19 15:32:28 -05:00
mdipierro 1aa5f30091 Merge pull request #926 from niphlod/enhancement/web2py_on_iis
added web.config to deploy web2py on IIS
2015-04-19 15:32:11 -05:00
mdipierro b17174c04c Merge pull request #925 from gi0baro/master
pydal -> track 15.03-maintenance (19/04/15)
2015-04-19 15:31:41 -05:00
niphlod ac80adc9b4 small typo. Fixes #920 2015-04-19 19:49:25 +02:00
niphlod 888fa3dfc8 added setup script 2015-04-19 19:00:59 +02:00
niphlod f3d815e84b added web.config to deploy web2py on IIS 2015-04-19 15:51:17 +02:00
gi0baro 9915fdf093 pydal -> track 15.03-maintenance (19/04/15) 2015-04-19 14:45:19 +02:00
mdipierro ef8f802df9 R-2.10.4.beta 2015-04-18 15:44:04 -05:00
mdipierro f7bf1020df reverted gluon/packages/dal 2015-04-18 15:28:12 -05:00
mdipierro 75b8ceb022 Merge pull request #923 from gi0baro/validators
Fix serializers injection over new pydal
2015-04-18 15:17:06 -05:00
mdipierro b4f3784136 Merge pull request #922 from dokime7/patch-7
Fix crash with list:reference field
2015-04-18 15:16:25 -05:00
mdipierro f1297bb827 Merge pull request #918 from niphlod/enhancement/anyserver
added waitress to anyserver
2015-04-18 15:15:02 -05:00
mdipierro 435ebeaae4 more consulting companies 2015-04-18 15:13:03 -05:00
gi0baro 537045082c Updated dal test due to new serializers 2015-04-18 15:08:39 +02:00
gi0baro 4bea52a7b5 Fix serializers injection over new pydal 2015-04-18 15:04:01 +02:00
Jeremie Dokime 33295e516f Fix crash with list:reference field
When using validate_and_update() or validate_and_insert() on a table with a list:reference field, the request crashes after timeout with:
  File "web2py/gluon/globals.py", line 270, in body
    raise HTTP(400, "Bad Request - HTTP body is incomplete")

The request crashes because there is an hidden exception with the isinstance function:
isinstance() arg 2 must be a class, type, or tuple of classes and types

When no using GAE, the GoogleDatastoreAdapter variable is None, so isinstance crash.
See the last line of pydal/adapters/__init__.py

This is a regression intruduced after the v2.9.12.
2015-04-18 02:15:45 +02:00
mdipierro f33ccf3366 experimental fix for represent 2015-04-16 16:53:12 -05:00
niphlod 0784680c90 added waitress to anyserver 2015-04-15 23:55:44 +02:00
mdipierro e940228eaf Merge pull request #912 from ilvalle/sqlcustomtype
extend sqlcustomform to support widget/represent
2015-04-15 13:01:50 -05:00
mdipierro 50769a627a Merge pull request #916 from gi0baro/issue-904
Fixes #904
2015-04-15 13:01:19 -05:00
mdipierro e68ecaa131 Merge pull request #915 from BuhtigithuB/update/gluon-contrib-pypyodbc
Update gluon/contrib/pypyodbc.py 1.3.0 -> 1.3.3
2015-04-15 13:01:01 -05:00
gi0baro 95e6e8577b Fixes #904 2015-04-14 15:25:26 +02:00
Hardirc 19c83d4ad6 Update gluon/contrib/pypyodbc.py 1.3.0 -> 1.3.3 2015-04-13 18:43:48 -04:00
ilvalle 9c92bd1050 extend sqlcustomform to support widget/represent (possible fix to web2py/pydal#127) 2015-04-12 21:04:05 +02:00
mdipierro b3b95ccf5f Merge pull request #906 from niphlod/fix/895
file is already open at this point... fixes #895
2015-04-08 23:24:16 -05:00
niphlod cefa30841b file is already open at this point... fixes #895 2015-04-08 21:47:04 +02:00
mdipierro 15ff8669cb fixed dal tracking, thanks Niphlod 2015-04-06 16:24:49 -05:00
mdipierro a921751e8e stripe form bs3 compatible, disabled cache.ram max utilization 2015-04-05 18:17:27 -05:00
19 changed files with 824 additions and 482 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ update:
echo "remember that pymysql was tweaked" echo "remember that pymysql was tweaked"
src: src:
### Use semantic versioning ### Use semantic versioning
echo 'Version 2.10.3-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION echo 'Version 2.10.4-beta+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
### rm -f all junk files ### rm -f all junk files
make clean make clean
### clean up baisc apps ### clean up baisc apps
+1 -1
View File
@@ -1 +1 @@
Version 2.10.3-stable+timestamp.2015.04.02.16.28.49 Version 2.10.4-stable+timestamp.2015.04.26.09.05.21
+5
View File
@@ -180,6 +180,11 @@ class Servers:
s = wsgi.WSGIServer(callable=app, bind="%s:%d" % address) s = wsgi.WSGIServer(callable=app, bind="%s:%d" % address)
s.start() s.start()
@staticmethod
def waitress(app, address, **options):
from waitress import serve
serve(app, host=address[0], port=address[1], _quiet=True)
def mongrel2_handler(application, conn, debug=False): def mongrel2_handler(application, conn, debug=False):
""" """
+6 -6
View File
@@ -18,7 +18,7 @@
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane active" id="alltables"> <div class="tab-pane active" id="alltables">
<table> <table class="table">
{{for db in sorted(databases):}} {{for db in sorted(databases):}}
{{for table in databases[db].tables:}} {{for table in databases[db].tables:}}
{{qry='%s.%s.id>0'%(db,table)}} {{qry='%s.%s.id>0'%(db,table)}}
@@ -40,7 +40,7 @@
{{=A("%s.%s" % (db,table),_href=URL('select',args=[db],vars=dict(query=qry)))}} {{=A("%s.%s" % (db,table),_href=URL('select',args=[db],vars=dict(query=qry)))}}
</th> </th>
<td> <td>
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}} {{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn btn-default")}}
</td> </td>
</tr> </tr>
{{pass}} {{pass}}
@@ -61,7 +61,7 @@
</pre> </pre>
{{pass}} {{pass}}
{{if table:}} {{if table:}}
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/> {{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn btn-default")}}<br/><br/>
<h3>{{=T("Rows in Table")}}</h3><br/> <h3>{{=T("Rows in Table")}}</h3><br/>
{{else:}} {{else:}}
<h3>{{=T("Rows selected")}}</h3><br/> <h3>{{=T("Rows selected")}}</h3><br/>
@@ -72,8 +72,8 @@
{{=T('"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN')}}</p> {{=T('"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN')}}</p>
<br/><br/> <br/><br/>
<h4>{{=T("%s selected", nrows)}}</h4> <h4>{{=T("%s selected", nrows)}}</h4>
{{if start>0:}}{{=A(T('previous %s rows') % step,_href=URL('select',args=request.args[0],vars=dict(start=start-step)),_class="btn")}}{{pass}} {{if start>0:}}{{=A(T('previous %s rows') % step,_href=URL('select',args=request.args[0],vars=dict(start=start-step)),_class="btn btn-default")}}{{pass}}
{{if stop<nrows:}}{{=A(T('next %s rows') % step,_href=URL('select',args=request.args[0],vars=dict(start=start+step)),_class="btn")}}{{pass}} {{if stop<nrows:}}{{=A(T('next %s rows') % step,_href=URL('select',args=request.args[0],vars=dict(start=start+step)),_class="btn btn-default")}}{{pass}}
{{if rows:}} {{if rows:}}
<div style="overflow:auto; width:80%;"> <div style="overflow:auto; width:80%;">
{{linkto = lambda f, t, r: URL('update', args=[request.args[0], r, f]) if f else "#"}} {{linkto = lambda f, t, r: URL('update', args=[request.args[0], r, f]) if f else "#"}}
@@ -82,7 +82,7 @@
</div> </div>
{{pass}} {{pass}}
<br/><br/><h3>{{=T("Import/Export")}}</h3><br/> <br/><br/><h3>{{=T("Import/Export")}}</h3><br/>
<a href="{{=URL('csv',args=request.args[0],vars=dict(query=query))}}" class="btn">{{=T("export as csv file")}}</a> <a href="{{=URL('csv',args=request.args[0],vars=dict(query=query))}}" class="btn btn-default">{{=T("export as csv file")}}</a>
{{=formcsv or ''}} {{=formcsv or ''}}
{{elif request.function=='insert':}} {{elif request.function=='insert':}}
@@ -33,6 +33,9 @@
<li><a target="_blank" href="https://loadinfo-net.appspot.com">LoadInfo</a> (Bulgaria)</li> <li><a target="_blank" href="https://loadinfo-net.appspot.com">LoadInfo</a> (Bulgaria)</li>
<li><a target="_blank" href="http://www.appliedobjects.com">Applied Objects</a> (New Zealand)</li> <li><a target="_blank" href="http://www.appliedobjects.com">Applied Objects</a> (New Zealand)</li>
<li><a target="_blank" href="http://www.sistemasagiles.com.ar/">Sistemas Ágiles</a> ("Agile Systems") (Argentina)</li> <li><a target="_blank" href="http://www.sistemasagiles.com.ar/">Sistemas Ágiles</a> ("Agile Systems") (Argentina)</li>
<li><a target="_blank" href="http://www.definescope.com/en/services/consulting/">DefineScope</a> (Portugal)</li>
<li><a target="_blank" href="http://10Biosystems.com">10BioSystems</a></li>
<li><a target="_blank" href="http://www.dutveul.nl">Dutveul</a> (Netherlands)</li>
</ul> </ul>
</div> </div>
+1 -1
View File
@@ -62,7 +62,7 @@ auth.define_tables(username=False, signature=False)
## configure email ## configure email
mail = auth.settings.mailer mail = auth.settings.mailer
mail.settings.server = 'logging' if request.is_local else myconf.take('smtp.sender') mail.settings.server = 'logging' if request.is_local else myconf.take('smtp.server')
mail.settings.sender = myconf.take('smtp.sender') mail.settings.sender = myconf.take('smtp.sender')
mail.settings.login = myconf.take('smtp.login') mail.settings.login = myconf.take('smtp.login')
+46
View File
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!-- app configuration for web2py on IIS -->
<configuration>
<appSettings>
<add key="WSGI_HANDLER" value="gluon.main.wsgibase" />
<add key="WSGI_RESTART_FILE_REGEX" value=".*((routes\.py)|(\.config))$" />
</appSettings>
<system.webServer>
<rewrite>
<rules>
<clear />
<rule name="static" enabled="true" stopProcessing="true">
<match url="^(\w+)/static(?:/_[\d]+\.[\d]+\.[\d]+)?/(.*)$" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="Rewrite" url="applications/{R:1}/static/{R:2}" logRewrittenUrl="false" />
</rule>
<rule name="web2py_app" enabled="true" stopProcessing="true">
<match url="(.*)" ignoreCase="false" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="Rewrite" url="handler.web2py/{R:1}" appendQueryString="true" />
</rule>
</rules>
<outboundRules>
<rule name="static_version_cache_control" preCondition="static_version">
<match serverVariable="RESPONSE_Cache-Control" pattern=".*" />
<action type="Rewrite" value="max-age=315360000" />
<conditions>
</conditions>
</rule>
<rule name="static_version_Expires" preCondition="static_version">
<match serverVariable="RESPONSE_Expires" pattern=".*" />
<action type="Rewrite" value="Thu, 31 Dec 2037 23:59:59 GMT" />
</rule>
<preConditions>
<preCondition name="static_version">
<add input="{REQUEST_URI}" pattern="(\w+)/static(?:/_[\d]+\.[\d]+\.[\d]+)?/(.*)$" />
</preCondition>
</preConditions>
</outboundRules>
</rewrite>
<handlers>
<!-- replace SCRIPT_PROCESSOR with the configured handler for python -->
<add name="Python_via_FastCGI" path="handler.web2py" verb="*" modules="FastCgiModule" scriptProcessor="SCRIPT_PROCESSOR" resourceType="Unspecified" requireAccess="Script" />
</handlers>
</system.webServer>
</configuration>
+2 -2
View File
@@ -99,7 +99,7 @@ class CacheAbstract(object):
""" """
cache_stats_name = 'web2py_cache_statistics' cache_stats_name = 'web2py_cache_statistics'
max_ram_utilization = 90 # percent max_ram_utilization = None # percent
def __init__(self, request=None): def __init__(self, request=None):
"""Initializes the object """Initializes the object
@@ -353,7 +353,7 @@ class CacheOnDisk(CacheAbstract):
raise KeyError raise KeyError
self.wait_portalock(val_file) self.wait_portalock(val_file)
value = pickle.load(recfile.open(key, 'rb', path=self.folder)) value = pickle.load(val_file)
val_file.close() val_file.close()
return value return value
+13 -3
View File
@@ -521,9 +521,19 @@ def ldap_auth(server='ldap', port=None,
logging.error( logging.error(
'There is no username or email for %s!' % username) 'There is no username or email for %s!' % username)
raise raise
db_group_search = db((db.auth_membership.user_id == db_user_id) & # if old pydal version, assume this is a relational database which can do joins
(db.auth_user.id == db.auth_membership.user_id) & db_can_join = db.can_join() if hasattr(db, 'can_join') else True
(db.auth_group.id == db.auth_membership.group_id)) if db_can_join:
db_group_search = db(
(db.auth_membership.user_id == db_user_id) &
(db.auth_user.id == db.auth_membership.user_id) &
(db.auth_group.id == db.auth_membership.group_id))
else:
# no joins on NoSQL databases, perform two queries
db_group_search = db(db.auth_membership.user_id == db_user_id)
group_ids = [x.group_id for x in db_group_search.select(
db.auth_membership.group_id, distinct=True)]
db_group_search = db(db.auth_group.id.belongs(group_ids))
db_groups_of_the_user = list() db_groups_of_the_user = list()
db_group_id = dict() db_group_id = dict()
+439 -421
View File
File diff suppressed because it is too large Load Diff
+35 -35
View File
@@ -4,7 +4,24 @@ from hashlib import sha1
class Stripe: class Stripe:
""" """
Usage: Use in WEB2PY (guaranteed PCI compliant)
def pay():
from gluon.contrib.stripe import StripeForm
form = StripeForm(
pk=STRIPE_PUBLISHABLE_KEY,
sk=STRIPE_SECRET_KEY,
amount=150, # $1.5 (amount is in cents)
description="Nothing").process()
if form.accepted:
payment_id = form.response['id']
redirect(URL('thank_you'))
elif form.errors:
redirect(URL('pay_error'))
return dict(form=form)
Low level API:
key='<api key>' key='<api key>'
d = Stripe(key).charge( d = Stripe(key).charge(
amount=100, # 1 dollar!!!! amount=100, # 1 dollar!!!!
@@ -22,22 +39,6 @@ class Stripe:
{u'fee': 0, u'description': u'test charge', u'created': 1321242072, u'refunded': False, u'livemode': False, u'object': u'charge', u'currency': u'usd', u'amount': 100, u'paid': True, u'id': u'ch_sdjasgfga83asf', u'card': {u'exp_month': 5, u'country': u'US', u'object': u'card', u'last4': u'4242', u'exp_year': 2012, u'type': u'Visa'}} {u'fee': 0, u'description': u'test charge', u'created': 1321242072, u'refunded': False, u'livemode': False, u'object': u'charge', u'currency': u'usd', u'amount': 100, u'paid': True, u'id': u'ch_sdjasgfga83asf', u'card': {u'exp_month': 5, u'country': u'US', u'object': u'card', u'last4': u'4242', u'exp_year': 2012, u'type': u'Visa'}}
if paid is True than transaction was processed if paid is True than transaction was processed
Use in WEB2PY (guaranteed PCI compliant)
def pay():
from gluon.contrib.stripe import StripeForm
form = StripeForm(
pk=STRIPE_PUBLISHABLE_KEY,
sk=STRIPE_SECRET_KEY,
amount=150, # $1.5 (amount is in cents)
description="Nothing").process()
if form.accepted:
payment_id = form.response['id']
redirect(URL('thank_you'))
elif form.errors:
redirect(URL('pay_error'))
return dict(form=form)
""" """
URL_CHARGE = 'https://%s:@api.stripe.com/v1/charges' URL_CHARGE = 'https://%s:@api.stripe.com/v1/charges'
@@ -188,37 +189,36 @@ jQuery(function(){
<h3>Payment Amount: {{=currency_symbol}} {{="%.2f" % (0.01*amount)}}</h3> <h3>Payment Amount: {{=currency_symbol}} {{="%.2f" % (0.01*amount)}}</h3>
<form action="" method="POST" id="payment-form" class="form-horizontal"> <form action="" method="POST" id="payment-form" class="form-horizontal">
<div class="form-row control-group"> <div class="form-row form-group">
<label class="control-label">Card Number</label> <label class="col-sm-2 control-label">Card Number</label>
<div class="controls"> <div class="controls col-sm-10">
<input type="text" size="20" data-stripe="number" <input type="text" size="20" data-stripe="number"
placeholder="4242424242424242"/> placeholder="4242424242424242" class="form-control"/>
</div> </div>
</div> </div>
<div class="form-row control-group"> <div class="form-row form-group">
<label class="control-label">CVC</label> <label class="col-sm-2 control-label">CVC</label>
<div class="controls"> <div class="controls col-sm-10">
<input type="text" size="4" style="width:80px" data-stripe="cvc" <input type="text" size="4" style="width:80px" data-stripe="cvc"
placeholder="XXX"/> placeholder="XXX" class="form-control"/>
<a href="http://en.wikipedia.org/wiki/Card_Verification_Code" target="_blank">What is this?</a> <a href="http://en.wikipedia.org/wiki/Card_Verification_Code" target="_blank">What is this?</a>
</div> </div>
</div> </div>
<div class="form-row control-group"> <div class="form-row form-group">
<label class="control-label">Expiration</label> <label class="col-sm-2 control-label">Expiration</label>
<div class="controls"> <div class="controls col-sm-10">
<input type="text" size="2" style="width:40px" data-stripe="exp-month" <input type="text" size="2" style="width:40px; display:inline-block"
placeholder="MM"/> data-stripe="exp-month" placeholder="MM" class="form-control"/>
/ /
<input type="text" size="4" style="width:80px" data-stripe="exp-year" <input type="text" size="4" style="width:80px; display:inline-block"
placeholder="YYYY"/> data-stripe="exp-year" placeholder="YYYY" class="form-control"/>
</div> </div>
</div> </div>
<div class="form-row form-group">
<div class="control-group"> <div class="controls col-sm-offset-2 col-sm-10">
<div class="controls">
<button type="submit" class="btn btn-primary">Submit Payment</button> <button type="submit" class="btn btn-primary">Submit Payment</button>
<div class="payment-errors error hidden"></div> <div class="payment-errors error hidden"></div>
</div> </div>
+4 -4
View File
@@ -7,14 +7,14 @@
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
Takes care of adapting pyDAL to web2py's needs Takes care of adapting pyDAL to web2py's needs
-------------------------------------------- -----------------------------------------------
""" """
from pydal import DAL as DAL from pydal import DAL as DAL
from pydal import Field from pydal import Field
from pydal.objects import Row, Rows, Table, Query, Expression from pydal.objects import Row, Rows, Table, Query, Expression
from pydal import SQLCustomType, geoPoint, geoLine, geoPolygon from pydal import SQLCustomType, geoPoint, geoLine, geoPolygon
import copy_reg as copyreg
def _default_validators(db, field): def _default_validators(db, field):
""" """
@@ -81,12 +81,12 @@ def _default_validators(db, field):
requires[0] = validators.IS_EMPTY_OR(requires[0]) requires[0] = validators.IS_EMPTY_OR(requires[0])
return requires return requires
from gluon import serializers as w2p_serializers from gluon.serializers import custom_json, xml
from gluon.utils import web2py_uuid from gluon.utils import web2py_uuid
from gluon import sqlhtml from gluon import sqlhtml
DAL.serializers = w2p_serializers DAL.serializers = {'json': custom_json, 'xml': xml}
DAL.validators_method = _default_validators DAL.validators_method = _default_validators
DAL.uuid = lambda x: web2py_uuid() DAL.uuid = lambda x: web2py_uuid()
DAL.representers = { DAL.representers = {
+15 -3
View File
@@ -58,9 +58,12 @@ def represent(field, value, record):
f = field.represent f = field.represent
if not callable(f): if not callable(f):
return str(value) return str(value)
n = f.func_code.co_argcount - len(f.func_defaults or []) if hasattr(f,'func_code'):
if getattr(f, 'im_self', None): n = f.func_code.co_argcount - len(f.func_defaults or [])
n -= 1 if getattr(f, 'im_self', None):
n -= 1
else:
n = 1
if n == 1: if n == 1:
return f(value) return f(value)
elif n == 2: elif n == 2:
@@ -1205,6 +1208,9 @@ class SQLFORM(FORM):
elif field.type == 'boolean': elif field.type == 'boolean':
inp = self.widgets.boolean.widget( inp = self.widgets.boolean.widget(
field, default, _disabled=True) field, default, _disabled=True)
elif isinstance(field.type, SQLCustomType) and callable(field.type.represent):
# SQLCustomType has a represent, use it
inp = field.type.represent(default, record)
else: else:
inp = field.formatter(default) inp = field.formatter(default)
if getattr(field, 'show_if', None): if getattr(field, 'show_if', None):
@@ -1246,6 +1252,9 @@ class SQLFORM(FORM):
dspval = '' dspval = ''
elif field.type == 'blob': elif field.type == 'blob':
continue continue
elif isinstance(field.type, SQLCustomType) and callable(field.type.widget):
# SQLCustomType has a widget, use it
inp = field.type.widget(field, default)
else: else:
field_type = widget_class.match(str(field.type)).group() field_type = widget_class.match(str(field.type)).group()
field_type = field_type in self.widgets and field_type or 'string' field_type = field_type in self.widgets and field_type or 'string'
@@ -2708,6 +2717,9 @@ class SQLFORM(FORM):
_href='%s/%s' % (upload, value)) _href='%s/%s' % (upload, value))
else: else:
value = '' value = ''
elif isinstance(field.type, SQLCustomType) and callable(field.type.represent):
# SQLCustomType has a represent, use it
value = field.type.represent(value, row)
if isinstance(value, str): if isinstance(value, str):
value = truncate_string(value, maxlength) value = truncate_string(value, maxlength)
elif not isinstance(value, XmlComponent): elif not isinstance(value, XmlComponent):
+3 -2
View File
@@ -15,10 +15,11 @@ from gluon.dal import DAL, Field
class TestDALSubclass(unittest.TestCase): class TestDALSubclass(unittest.TestCase):
def testRun(self): def testRun(self):
import gluon.serializers as mserializers from gluon.serializers import custom_json, xml
from gluon import sqlhtml from gluon import sqlhtml
db = DAL(check_reserved=['all']) db = DAL(check_reserved=['all'])
self.assertEqual(db.serializers, mserializers) self.assertEqual(db.serializers['json'], custom_json)
self.assertEqual(db.serializers['xml'], xml)
self.assertEqual(db.representers['rows_render'], sqlhtml.represent) self.assertEqual(db.representers['rows_render'], sqlhtml.represent)
self.assertEqual(db.representers['rows_xml'], sqlhtml.SQLTABLE) self.assertEqual(db.representers['rows_xml'], sqlhtml.SQLTABLE)
+1 -1
View File
@@ -5362,7 +5362,7 @@ class Expose(object):
if current.request.raw_args: if current.request.raw_args:
self.args = [arg for arg in current.request.raw_args.split('/') if arg] self.args = [arg for arg in current.request.raw_args.split('/') if arg]
else: else:
self.args = [arg for arg in current.request.args if args] self.args = [arg for arg in current.request.args if arg]
filename = os.path.join(base, *self.args) filename = os.path.join(base, *self.args)
if not os.path.exists(filename): if not os.path.exists(filename):
raise HTTP(404, "FILE NOT FOUND") raise HTTP(404, "FILE NOT FOUND")
+1 -1
View File
@@ -611,7 +611,7 @@ class IS_IN_DB(Validator):
def count(values, s=self.dbset, f=field): def count(values, s=self.dbset, f=field):
return s(f.belongs(map(int, values))).count() return s(f.belongs(map(int, values))).count()
if isinstance(self.dbset.db._adapter, GoogleDatastoreAdapter): if GoogleDatastoreAdapter is not None and isinstance(self.dbset.db._adapter, GoogleDatastoreAdapter):
range_ids = range(0, len(values), 30) range_ids = range(0, len(values), 30)
total = sum(count(values[i:i + 30]) for i in range_ids) total = sum(count(values[i:i + 30]) for i in range_ids)
if total == len(values): if total == len(values):
+169
View File
@@ -0,0 +1,169 @@
"This script will work fine for a few cases 'by default':"
" - completely CLEAN WS2012R2 host"
" - python 2.7 installed in the default path"
" - wfasctgi installed on the default path"
"It'll install web2py under the default website "
" You can use it as a boilerplate to automate your deployments"
" but it still is released AS IT IS. "
"BIG FAT WARNING: It will install a bunch of dependecies
Inspect the source before executing it"
""
""
$ErrorActionPreference = 'stop'
$REALLY_SURE = Read-Host "Do you want to start with web2py deployment? [y/N]"
if (!@('y', 'Y') -contains $REALLY_SURE) {
"Ok, Exiting without doing anything"
exit 1
}
#setting root folder
$rootfolder = $pwd
### utilities - start
function ask_a_question($question) {
$response = Read-Host "$question [Y/n]"
if (@('Y', 'y', '', $null) -contains $response) {
return $true
} else {
return $false
}
}
function unzip_me {
#Load the assembly
[System.Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem") | Out-Null
#Unzip the file
[System.IO.Compression.ZipFile]::ExtractToDirectory($pathToZip, $targetDir)
}
### utilities - end
#install 4.5 that is needed for a bunch of things anyway
Install-WindowsFeature Net-Framework-45-Core
#fetch web2py
$web2py_url = 'http://www.web2py.com/examples/static/web2py_src.zip'
$web2py_file = "$pwd\web2py_src.zip"
if (!(Test-Path $web2py_file)) {
(new-object net.webclient).DownloadFile($web2py_url, $web2py_file)
}
#Load the assembly
[System.Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem") | Out-Null
#Unzip the file
[System.IO.Compression.ZipFile]::ExtractToDirectory($web2py_file, $pwd)
#features installation (IIS, needed modules, python, chocolatey, etc)
$installfeatures = ask_a_question('Do you want to install needed features?')
if ($installfeatures) {
Install-WindowsFeature Web-Server,Web-Default-Doc,Web-Static-Content,Web-Http-Redirect,Web-Http-Logging,Web-Request-Monitor,`
Web-Http-Tracing,Web-Stat-Compression,Web-Dyn-Compression,Web-Filtering,Web-Basic-Auth,Web-Windows-Auth,Web-AppInit,`
Web-CGI,Web-WebSockets,Web-Mgmt-Console,Web-Net-Ext45
}
$copy_web2py = ask_a_question("Copy web2py to the default website root?")
if ($copy_web2py) {
Import-Module WebAdministration
$available_websites = Get-Website
if ($available_websites[0] -eq $null) {
$default_one = $available_websites
} else {
$default_one = $available_websites[0]
}
$iis_root = [System.Environment]::ExpandEnvironmentVariables($default_one.PhysicalPath)
Copy-Item "$rootfolder\web2py\*" $iis_root -Recurse
$rootfolder = $iis_root
$acl = (Get-Item $rootfolder).GetAccessControl('Access')
$identity = "BUILTIN\IIS_IUSRS"
$fileSystemRights = "Modify"
$inheritanceFlags = "ContainerInherit, ObjectInherit"
$propagationFlags = "None"
$accessControlType = "Allow"
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule($identity, $fileSystemRights, $inheritanceFlags, $propagationFlags, $accessControlType)
$acl.SetAccessRule($rule)
Set-Acl $rootfolder $acl
}
$create_cert = ask_a_question("Do you want to create a self-signed SSL cert?")
if ($create_cert) {
$cert = New-SelfSignedCertificate -DnsName ("localtest.me","*.localtest.me") -CertStoreLocation cert:\LocalMachine\My
$rootStore = Get-Item cert:\LocalMachine\Root
$rootStore.Open("ReadWrite")
$rootStore.Add($cert)
$rootStore.Close();
Import-Module WebAdministration
Set-Location IIS:\SslBindings
New-WebBinding -Name "Default Web Site" -IP "*" -Port 443 -Protocol https
$cert | New-Item 0.0.0.0!443
Set-Location $pwd
}
"checking for chocolatey"
if (Get-Command "choco.exe" -ErrorAction SilentlyContinue)
{
"chocolatey found"
} else {
"installing chocolatey"
(new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1') | iex
}
"installing url-rewrite"
choco install UrlRewrite
$pythonexe = Read-Host 'Python.exe path [C:\Python27\python.exe]'
if (($pythonexe -eq '') -or ($pythonexe -eq $null)) {
$pythonexe = 'C:\Python27\python.exe'
}
if (!(Test-Path $pythonexe)) {
"ERROR: python executable not found"
$pythonwanted = ask_a_question("do you want to install it automatically?")
if ($pythonwanted) {
choco install webpicmd
WebpiCmd.exe /Install /Products:WFastCgi_21_279
$pythonexe = 'C:\Python27\python.exe'
}
else {
exit 1
}
}
$wfastcgipath = Read-Host 'wfastcgi.py path [C:\Python27\Scripts\wfastcgi.py]'
if (($wfastcgipath -eq '') -or ($wfastcgipath -eq $null)) {
$wfastcgipath = 'C:\Python27\Scripts\wfastcgi.py'
}
if (-not (Test-Path $wfastcgipath)) {
"ERROR: wfastcgi.py not found"
$wfastcgiwanted = ask_a_question("do you want to install it automatically?")
if ($wfastcgiwanted) {
choco install webpicmd
WebpiCmd.exe /Install /Products:WFastCgi_21_279
} else {
exit 1
}
}
$pythondir = Split-Path c:\python27\python.exe
#installing dependencies
$env:Path = $env:Path + ";$pythondir;$pythondir\Scripts"
pip install pypiwin32
$PW = Read-Host 'Web2py Admin Password'
$appcmdpath = "$env:windir\system32\inetsrv\appcmd.exe"
& $appcmdpath set config /section:system.webServer/fastCGI "/+[fullPath='$pythonexe', arguments='$wfastcgipath']"
& $appcmdpath unlock config -section:system.webServer/handlers
& cd $rootfolder
& $pythonexe -c "from gluon.main import save_password; save_password('$PW',443)"
$webconfig_template = Join-Path $rootfolder "examples\web.config"
$destination = Join-Path $rootfolder "web.config"
$scriptprocessor = 'scriptProcessor="{0}|{1}"' -f $pythonexe, $wfastcgipath
(Get-Content $webconfig_template) | Foreach-Object {$_ -replace 'scriptProcessor="SCRIPT_PROCESSOR"', $scriptprocessor} | where {$_ -ne ""} | Set-Content $destination
""
"Installation finished. Web2py is available either on http://localhost/ or at https://localtest.me/"
""
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Post error tickets to slack on a 5 minute schedule.
#
# Proper use depends on having created a web-hook through Slack, and having set
# that value in your app's model as the value of global_settings.slack_hook.
# Details on creating web-hooks can be found at https://slack.com/integrations
#
# requires the Requests module for posting to slack, other requirements are
# standard or provided by web2py
#
# Usage (on Unices), replace myapp with the name of your application and run:
# nohup python web2py.py -S myapp -M -R scripts/tickets2slack.py &
import sys
import os
import time
import pickle
import json
try:
import requests
except ImportError as e:
print "missing module 'Requests', aborting."
sys.exit(1)
from gluon import URL
from gluon.utils import md5_hash
from gluon.restricted import RestrictedError
from gluon.settings import global_settings
path = os.path.join(request.folder, 'errors')
sent_errors_file = os.path.join(path, 'slack_errors.pickle')
hashes = {}
if os.path.exists(sent_errors_file):
try:
with open(sent_errors_file, 'rb') as f:
hashes = pickle.load(f)
except Exception as _:
pass
# ## CONFIGURE HERE
SLEEP_MINUTES = 5
ALLOW_DUPLICATES = False
global_settings.slack_hook = global_settings.slack_hook or \
'https://hooks.slack.com/services/your_service'
# ## END CONFIGURATION
while 1:
for file_name in os.listdir(path):
if file_name == 'slack_errors.pickle':
continue
if not ALLOW_DUPLICATES:
key = md5_hash(file_name)
if key in hashes:
continue
hashes[key] = 1
error = RestrictedError()
try:
error.load(request, request.application, file_name)
except Exception as _:
continue # not an exception file?
url = URL(a='admin', f='ticket', args=[request.application, file],
scheme=True)
payload = json.dumps(dict(text="Error in %(app)s.\n%(url)s" %
dict(app=request.application, url=url)))
requests.post(global_settings.slack_hook, data=dict(payload=payload))
with open(sent_errors_file, 'wb') as f:
pickle.dump(hashes, f)
time.sleep(SLEEP_MINUTES * 60)