Compare commits

..

19 Commits

Author SHA1 Message Date
mdipierro
81d0291ce2 R-2.14.5 2016-04-13 22:22:39 -05:00
mdipierro
894ff3c140 Merge pull request #1294 from leonelcamara/fix_1274
fixes #1274
2016-04-13 22:16:46 -05:00
mdipierro
216ce5507c Merge pull request #1293 from leonelcamara/fix_1266
fixes #1266 and adds tests to make sure it doesn't happen again
2016-04-13 22:15:53 -05:00
mdipierro
068aecff93 Merge pull request #1292 from BuhtigithuB/add-more-html-test
Some more coverage for XML() and DIV()
2016-04-13 22:15:22 -05:00
Leonel Câmara
59cbe99347 pep8 and force travis to rebuild 2016-04-14 04:13:18 +01:00
Leonel Câmara
bdbc053285 fixes #1274 2016-04-14 03:57:20 +01:00
Leonel Câmara
d746d43be5 pep8 and make travis run again 2016-04-14 01:21:23 +01:00
Leonel Câmara
9a3e73031b fixes #1266 and adds tests to make sure it doesn't happen again 2016-04-14 01:02:25 +01:00
Richard Vézina
0468c16bc2 Some more coverage for XML() and DIV() 2016-04-13 11:44:16 -04:00
mdipierro
8c5858b6b7 fixed merge issue 2016-04-13 08:41:09 -05:00
mdipierro
6400e28a85 fixed stupid 2016-04-13 08:40:19 -05:00
mdipierro
3e46d50aa1 Merge pull request #1290 from carpaIdea/patch-3
box-sizing best practice
2016-04-13 08:33:29 -05:00
mdipierro
cb94fde80b Merge pull request #1289 from carpaIdea/patch-2
<main> semantic element IE fix
2016-04-13 08:30:20 -05:00
mdipierro
98593eefce Merge pull request #1291 from leonelcamara/test_week6
Test emails with alternative text and html
2016-04-13 08:27:33 -05:00
Leonel Câmara
4bbfe70927 install a more modern pypy version 2016-04-13 14:17:30 +01:00
Leonel Câmara
02a0d1c9b0 minor reorder 2016-04-13 14:01:36 +01:00
Leonel Câmara
2bf0ad9268 test emails with alternative text and html 2016-04-13 14:00:33 +01:00
carpaIdea
2e63b7637a box-sizing best practice
This code was updated to match new box-sizing best practices. Also prefixes are pretty much dead. (quote from http://www.paulirish.com/2012/box-sizing-border-box-ftw/).

More info on https://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/
2016-04-13 00:53:42 +02:00
carpaIdea
a9c5cf3072 <main> semantic element IE fix
Some versions of Internet Explorer consider the <main> semantic element as "unknown". So its initial value is "inline".If we want the behavior of the <main> element to be the same as in other browsers, we must set it explicitly as a "block."
2016-04-12 23:07:27 +02:00
10 changed files with 96 additions and 39 deletions

View File

@@ -12,8 +12,22 @@ python:
- 'pypy'
install:
- |
if [ "$TRAVIS_PYTHON_VERSION" = "pypy" ]; then
export PYENV_ROOT="$HOME/.pyenv"
if [ -f "$PYENV_ROOT/bin/pyenv" ]; then
pushd "$PYENV_ROOT" && git pull && popd
else
rm -rf "$PYENV_ROOT" && git clone --depth 1 https://github.com/yyuu/pyenv.git "$PYENV_ROOT"
fi
export PYPY_VERSION="5.0.1"
"$PYENV_ROOT/bin/pyenv" install --skip-existing "pypy-$PYPY_VERSION"
virtualenv --python="$PYENV_ROOT/versions/pypy-$PYPY_VERSION/bin/python" "$HOME/virtualenvs/pypy-$PYPY_VERSION"
source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate"
fi
- pip install -e .
before_script:
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install --download-cache $HOME/.pip-cache unittest2; fi
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --download-cache $HOME/.pip-cache coverage; fi;

View File

@@ -1,4 +1,4 @@
## 2.14.1-4
## 2.14.1-5
- fixed two major security issues that caused the examples app to leak information
- new Auth(…,host_names=[…]) to prevent host header injection

View File

@@ -32,7 +32,7 @@ update:
echo "remember that pymysql was tweaked"
src:
### Use semantic versioning
echo 'Version 2.14.4-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
echo 'Version 2.14.5-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
### rm -f all junk files
make clean
### clean up baisc apps

View File

@@ -1 +1 @@
Version 2.14.4-stable+timestamp.2016.04.12.15.44.54
Version 2.14.5-stable+timestamp.2016.04.13.22.22.13

View File

@@ -5,7 +5,8 @@
************/
/*** basic styles ***/
*, *:after, *:before {border:0; margin:0; padding:0; -webkit-box-sizing:border-box; -moz-box-sizing:border-box; box-sizing:border-box}
html {box-sizing:border-box;}
*, *:after, *:before {border:0; margin:0; padding:0; box-sizing:inherit;}
html, body {max-width: 100vw; overflow-x: hidden}
body {font-family:"HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif}
p, li {margin-bottom:0.5em}
@@ -27,7 +28,7 @@ thead tr {background-color:#f1f1f1}
tbody tr {border-bottom:2px solid #f1f1f1}
td, th {padding: 5px; text-align: left; vertical-align:top}
thead th {vertical-align:bottom}
header, footer {with:100%}
header, main, footer {display:block; with:100%} /* IE fix */
@media all and (max-width:599px) {
h1{font-size:2em}

View File

@@ -74,12 +74,12 @@ def _default_validators(db, field):
return requires
# does not get here for reference and list:reference
if field.unique:
requires.insert(0,validators.IS_NOT_IN_DB(db, field))
excluded_fields = ['string','upload','text','password','boolean']
requires.insert(0, validators.IS_NOT_IN_DB(db, field))
excluded_fields = ['string', 'upload', 'text', 'password', 'boolean']
if (field.notnull or field.unique) and not field_type in excluded_fields:
requires.insert(0,validators.IS_NOT_EMPTY())
requires.insert(0, validators.IS_NOT_EMPTY())
elif not field.notnull and not field.unique and requires:
requires[0] = validators.IS_EMPTY_OR(requires[0])
requires[0] = validators.IS_EMPTY_OR(requires[0], null='' if field in ('string', 'text', 'password') else None)
return requires
from gluon.serializers import custom_json, xml
@@ -93,7 +93,7 @@ DAL.uuid = lambda x: web2py_uuid()
DAL.representers = {
'rows_render': sqlhtml.represent,
'rows_xml': sqlhtml.SQLTABLE
}
}
DAL.Field = Field
DAL.Table = Table

View File

@@ -173,6 +173,9 @@ class TestBareHelpers(unittest.TestCase):
# bug check for the sanitizer for closing no-close tags
self.assertEqual(XML('<p>Test</p><br/><p>Test</p><br/>', sanitize=True),
XML('<p>Test</p><br /><p>Test</p><br />'))
# basic flatten test
self.assertEqual(XML('<p>Test</p>').flatten(), '<p>Test</p>')
self.assertEqual(XML('<p>Test</p>').flatten(render=lambda text, tag, attr: text), '<p>Test</p>')
def test_XML_pickle_unpickle(self):
# weird test
@@ -236,6 +239,9 @@ class TestBareHelpers(unittest.TestCase):
# DIV(BR('<>')).xml()
# self.assertEqual(cm.exception[0], '<br/> tags cannot have components')
# test .get('attrib')
self.assertEqual(DIV('<p>Test</p>', _class="class_test").get('_class'), 'class_test')
def test_CAT(self):
# Empty CAT()
self.assertEqual(CAT().xml(), '')

View File

@@ -36,10 +36,19 @@ class TestMail(unittest.TestCase):
"""
class Message(object):
def __init__(self, sender, to, payload):
self.sender = sender
self.to = to
self.payload = payload
self._parsed_payload = None
@property
def parsed_payload(self):
if self._parsed_payload is None:
import email
self._parsed_payload = email.message_from_string(self.payload)
return self._parsed_payload
class DummySMTP(object):
"""
@@ -137,6 +146,19 @@ class TestMail(unittest.TestCase):
message = TestMail.DummySMTP.inbox.pop()
self.assertTrue('Content-Type: text/html' in message.payload)
def test_alternative(self):
mail = Mail()
mail.settings.server = 'smtp.example.com:25'
mail.settings.sender = 'you@example.com'
self.assertTrue(mail.send(to=['somebody@example.com'],
message=('Text only', '<html><pre>HTML Only</pre></html>')))
message = TestMail.DummySMTP.inbox.pop()
self.assertTrue(message.parsed_payload.is_multipart())
self.assertTrue(message.parsed_payload.get_content_type() == 'multipart/alternative')
parts = message.parsed_payload.get_payload()
self.assertTrue('Text only' in parts[0].as_string())
self.assertTrue('<html><pre>HTML Only</pre></html>' in parts[1].as_string())
def test_ssl(self):
mail = Mail()
mail.settings.server = 'smtp.example.com:25'
@@ -171,9 +193,7 @@ class TestMail(unittest.TestCase):
message='world',
attachments=Mail.Attachment(module_file)))
message = TestMail.DummySMTP.inbox.pop()
import email
parsed_msg = email.message_from_string(message.payload)
attachment = parsed_msg.get_payload(1).get_payload(decode=True)
attachment = message.parsed_payload.get_payload(1).get_payload(decode=True)
with open(module_file, 'rb') as mf:
self.assertEqual(attachment.decode('utf-8'), mf.read().decode('utf-8'))
# Test missing attachment name error

View File

@@ -232,7 +232,19 @@ class TestValidators(unittest.TestCase):
self.assertEqual(sorted(rtn), [('%d' % george_id, 'george'), ('%d' % costanza_id, 'costanza')])
rtn = IS_IN_DB(db, db.person.id, db.person.name, error_message='oops', sort=True).options(zero=True)
self.assertEqual(rtn, [('', ''), ('%d' % costanza_id, 'costanza'), ('%d' % george_id, 'george')])
# Test it works with self reference
db.define_table('category',
Field('parent_id', 'reference category', requires=IS_EMPTY_OR(IS_IN_DB(db, 'category.id', '%(name)s'))),
Field('name')
)
ret = db.category.validate_and_insert(name='seinfeld')
self.assertFalse(list(ret.errors))
ret = db.category.validate_and_insert(name='characters', parent_id=ret.id)
self.assertFalse(list(ret.errors))
rtn = IS_IN_DB(db, 'category.id', '%(name)s')(ret.id)
self.assertEqual(rtn, (ret.id, None))
db.person.drop()
db.category.drop()
def test_IS_NOT_IN_DB(self):
from gluon.dal import DAL, Field

View File

@@ -376,8 +376,8 @@ class IS_JSON(Validator):
def __call__(self, value):
try:
if self.native_json:
simplejson.loads(value) # raises error in case of malformed json
return (value, None) # the serialized value is not passed
simplejson.loads(value) # raises error in case of malformed json
return (value, None) # the serialized value is not passed
else:
return (simplejson.loads(value), None)
except JSONErrors:
@@ -459,7 +459,7 @@ class IS_IN_SET(Validator):
def __call__(self, value):
if self.multiple:
### if below was values = re.compile("[\w\-:]+").findall(str(value))
# if below was values = re.compile("[\w\-:]+").findall(str(value))
if not value:
values = []
elif isinstance(value, (tuple, list)):
@@ -523,8 +523,8 @@ class IS_IN_DB(Validator):
field = field._id
elif isinstance(field, str):
items = field.split('.')
if len(items)==1: items+=['id']
field = self.dbset.db[items[0]][items[1]]
if len(items) == 1:
field = items[0] + '.id'
(ktable, kfield) = str(field).split('.')
if not label:
@@ -534,16 +534,16 @@ class IS_IN_DB(Validator):
label = '%%(%s)s' % str(label).split('.')[-1]
fieldnames = regex2.findall(label)
if kfield not in fieldnames:
fieldnames.append(kfield) # kfield must be last
fieldnames.append(kfield) # kfield must be last
elif isinstance(label, Field):
fieldnames = [label.name, kfield] # kfield must be last
fieldnames = [label.name, kfield] # kfield must be last
label = '%%(%s)s' % label.name
elif callable(label):
fieldnames = '*'
else:
raise NotImplementedError
self.field = field # the lookup field
self.fieldnames = fieldnames # fields requires to build the formatting
self.fieldnames = fieldnames # fields requires to build the formatting
self.label = label
self.ktable = ktable
self.kfield = kfield
@@ -621,16 +621,16 @@ class IS_IN_DB(Validator):
if isinstance(value, list):
values = value
elif self.delimiter:
values = value.split(self.delimiter) # because of autocomplete
values = value.split(self.delimiter) # because of autocomplete
elif value:
values = [value]
else:
values = []
if self.field.type in ('id','integer'):
if field.type in ('id', 'integer'):
new_values = []
for value in values:
if not (isinstance(value,(int,long)) or value.isdigit()):
if not (isinstance(value, (int, long)) or value.isdigit()):
if self.auto_add:
value = str(self.maybe_add(table, self.fieldnames[0], value))
else:
@@ -657,8 +657,8 @@ class IS_IN_DB(Validator):
elif count(values) == len(values):
return (values, None)
else:
if self.field.type in ('id','integer'):
if isinstance(value,(int,long)) or value.isdigit():
if field.type in ('id', 'integer'):
if isinstance(value, (int, long)) or value.isdigit():
value = int(value)
elif self.auto_add:
value = self.maybe_add(table, self.fieldnames[0], value)
@@ -818,7 +818,7 @@ class IS_INT_IN_RANGE(Validator):
if regex_isint.match(str(value)):
v = int(value)
if ((self.minimum is None or v >= self.minimum) and
(self.maximum is None or v < self.maximum)):
(self.maximum is None or v < self.maximum)):
return (v, None)
return (value, self.error_message)
@@ -892,7 +892,7 @@ class IS_FLOAT_IN_RANGE(Validator):
else:
v = float(str(value).replace(self.dot, '.'))
if ((self.minimum is None or v >= self.minimum) and
(self.maximum is None or v <= self.maximum)):
(self.maximum is None or v <= self.maximum)):
return (v, None)
except (ValueError, TypeError):
pass
@@ -978,7 +978,7 @@ class IS_DECIMAL_IN_RANGE(Validator):
else:
v = decimal.Decimal(str(value).replace(self.dot, '.'))
if ((self.minimum is None or v >= self.minimum) and
(self.maximum is None or v <= self.maximum)):
(self.maximum is None or v <= self.maximum)):
return (v, None)
except (ValueError, TypeError, decimal.InvalidOperation):
pass
@@ -2248,7 +2248,7 @@ class IS_DATE(Validator):
y = '%.4i' % year
format = format.replace('%y', y[-2:])
format = format.replace('%Y', y)
if year < 1900:
if year < 1900:
year = 2000
d = datetime.date(year, value.month, value.day)
return d.strftime(format)
@@ -2347,6 +2347,7 @@ class IS_DATE_IN_RANGE(IS_DATE):
(datetime.date(2010, 3, 3), 'oops')
"""
def __init__(self,
minimum=None,
maximum=None,
@@ -2400,6 +2401,7 @@ class IS_DATETIME_IN_RANGE(IS_DATETIME):
(datetime.datetime(2010, 3, 3, 0, 0), 'oops')
"""
def __init__(self,
minimum=None,
maximum=None,
@@ -2513,7 +2515,7 @@ def urlify(s, maxlen=80, keep_underscores=False):
if keep_underscores:
s = re.sub('\s+', '-', s) # whitespace to hyphens
s = re.sub('[^\w\-]', '', s)
# strip all but alphanumeric/underscore/hyphen
# strip all but alphanumeric/underscore/hyphen
else:
s = re.sub('[\s_]+', '-', s) # whitespace & underscores to hyphens
s = re.sub('[^a-z0-9\-]', '', s) # strip all but alphanumeric/hyphen
@@ -2705,6 +2707,7 @@ class LazyCrypt(object):
"""
Stores a lazy password hash
"""
def __init__(self, crypt, password):
"""
crypt is an instance of the CRYPT validator,
@@ -2760,8 +2763,8 @@ class LazyCrypt(object):
# LazyCrypt objects comparison
if isinstance(stored_password, self.__class__):
return ((self is stored_password) or
((self.crypt.key == stored_password.crypt.key) and
(self.password == stored_password.password)))
((self.crypt.key == stored_password.crypt.key) and
(self.password == stored_password.password)))
if self.crypt.key:
if ':' in self.crypt.key:
@@ -3404,14 +3407,14 @@ class IS_IPV4(Validator):
ok = True
if not (self.is_localhost is None or self.is_localhost ==
(number == self.localhost)):
ok = False
ok = False
if not (self.is_private is None or self.is_private ==
(sum([private_number[0] <= number <= private_number[1]
for private_number in self.private]) > 0)):
ok = False
ok = False
if not (self.is_automatic is None or self.is_automatic ==
(self.automatic[0] <= number <= self.automatic[1])):
ok = False
ok = False
if ok:
return (value, None)
return (value, translate(self.error_message))
@@ -3695,6 +3698,7 @@ class IS_IPADDRESS(Validator):
>>> IS_IPADDRESS(subnets='invalidsubnet')('2001::8ffa:fe22:b3af')
('2001::8ffa:fe22:b3af', 'invalid subnet provided')
"""
def __init__(
self,
minip='0.0.0.0',
@@ -3757,7 +3761,7 @@ class IS_IPADDRESS(Validator):
is_private=self.is_private,
is_automatic=self.is_automatic,
error_message=self.error_message
)(value)
)(value)
elif self.is_ipv6 or isinstance(ip, IPv6Address):
retval = IS_IPV6(
is_private=self.is_private,
@@ -3769,7 +3773,7 @@ class IS_IPADDRESS(Validator):
is_teredo=self.is_teredo,
subnets=self.subnets,
error_message=self.error_message
)(value)
)(value)
else:
retval = (value, translate(self.error_message))