diff --git a/VERSION b/VERSION index f187878e..e5b81f93 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.9.5-trunk+timestamp.2014.08.09.00.34.28 +Version 2.9.5-trunk+timestamp.2014.08.09.01.51.50 diff --git a/applications/admin/views/appadmin.html b/applications/admin/views/appadmin.html index d8bea300..054efd49 100644 --- a/applications/admin/views/appadmin.html +++ b/applications/admin/views/appadmin.html @@ -65,7 +65,7 @@ {{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 stop +
{{linkto = lambda f, t, r: URL('update', args=[request.args[0], r, f]) if f else "#"}} {{upload=URL('download',args=request.args[0])}} {{=SQLTABLE(rows,linkto,upload,orderby=True,_class='sortable')}} diff --git a/applications/examples/views/appadmin.html b/applications/examples/views/appadmin.html index d8bea300..054efd49 100644 --- a/applications/examples/views/appadmin.html +++ b/applications/examples/views/appadmin.html @@ -65,7 +65,7 @@ {{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 stop +
{{linkto = lambda f, t, r: URL('update', args=[request.args[0], r, f]) if f else "#"}} {{upload=URL('download',args=request.args[0])}} {{=SQLTABLE(rows,linkto,upload,orderby=True,_class='sortable')}} diff --git a/applications/welcome/controllers/default.py b/applications/welcome/controllers/default.py index b4b4e1f7..b2f2a5dd 100644 --- a/applications/welcome/controllers/default.py +++ b/applications/welcome/controllers/default.py @@ -9,10 +9,6 @@ ## - api is an example of Hypermedia API support and access control ######################################################################### -@auth.requires_login() -def manage(): - return dict(grid = SQLFORM.grid(db.auth_user)) - def index(): """ example action using the internationalization operator T and flash diff --git a/applications/welcome/static/css/web2py.css b/applications/welcome/static/css/web2py.css index dfba3fdd..4a89ae9f 100644 --- a/applications/welcome/static/css/web2py.css +++ b/applications/welcome/static/css/web2py.css @@ -125,17 +125,9 @@ div.flash:hover { opacity:0.25; } div.error_wrapper {display:block} div.error { - width: 298px; - background:red; - border: 2px solid #d00; - color:white; + color:red; padding:5px; display:inline-block; - background-image: -webkit-linear-gradient(left,#f00,#fdd); - background-image: -o-linear-gradient(left,#f00,#fdd); - background-image: -moz-linear-gradient(0deg, #f00, #fdd); - background-image: linear-gradient(left,#f00,#fdd); - background-repeat: repeat-y; } .topbar { @@ -315,3 +307,7 @@ li.w2p_grid_breadcrumb_elem { .ie-lte8 div.flash{ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#222222', endColorstr='#000000', GradientType=0 ); } .ie-lte8 div.flash:hover {filter:alpha(opacity=25);} .ie9 #w2p_query_panel {padding-bottom:2px} + +.web2py_console .form-control {width: 20%; display: inline;} +.web2py_console #w2p_keywords {width: 50%;} +.web2py_search_actions a, .web2py_console input[type=submit], .web2py_console input[type=button], .web2py_console button { padding: 6px 12px; } diff --git a/gluon/dal.py b/gluon/dal.py index 537a7d76..68ec7179 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -10119,7 +10119,7 @@ class Field(Expression): self.custom_qualifier = custom_qualifier self.label = (label if label is not None else fieldname.replace('_', ' ').title()) - self.requires = requires if requires is not None else [] + self.requires = requires if not requires in (None, DEFAULT) else [] self.map_none = map_none self._rname = rname diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 60b822ea..a693529a 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -1017,6 +1017,7 @@ class SQLFORM(FORM): formstyle='table3cols', buttons=['submit'], separator=': ', + extra_fields=None, **attributes ): T = current.T @@ -1080,11 +1081,19 @@ class SQLFORM(FORM): sep = separator or '' + extra_fields = extra_fields or [] + self.extra_fields = {} + for extra_field in extra_fields: + extra_field.tablename = '_extra' + self.fields.append(extra_field.name) + self.extra_fields[extra_field.name] = extra_field + for fieldname in self.fields: if fieldname.find('.') >= 0: continue - field = self.table[fieldname] + field = (self.table[fieldname] if fieldname in self.table.fields + else self.extra_fields[fieldname]) comment = None if comments: @@ -1385,7 +1394,8 @@ class SQLFORM(FORM): # ## THIS IS FOR UNIQUE RECORDS, read IS_NOT_IN_DB for fieldname in self.fields: - field = self.table[fieldname] + field = (self.table[fieldname] if fieldname in self.table.fields + else self.extra_fields[fieldname]) requires = field.requires or [] if not isinstance(requires, (list, tuple)): requires = [requires] @@ -1430,7 +1440,10 @@ class SQLFORM(FORM): # auch is true when user tries to delete a record # that does not pass validation, yet it should be deleted for fieldname in self.fields: - field = self.table[fieldname] + + field = (self.table[fieldname] + if fieldname in self.table.fields + else self.extra_fields[fieldname]) ### this is a workaround! widgets should always have default not None! if not field.widget and field.type.startswith('list:') and \ not OptionsWidget.has_options(field): @@ -1441,7 +1454,7 @@ class SQLFORM(FORM): elif self.record: value = self.record[fieldname] else: - value = self.table[fieldname].default + value = field.default row_id = '%s_%s%s' % ( self.table, fieldname, SQLFORM.ID_ROW_SUFFIX) widget = field.widget(field, value) @@ -1588,6 +1601,10 @@ class SQLFORM(FORM): fields[fieldname] = self.vars[fieldname] if dbio: + print fields + for fieldname in fields: + if fieldname in self.extra_fields: + del fields[fieldname] if 'delete_this_record' in fields: # this should never happen but seems to happen to some del fields['delete_this_record'] @@ -1756,35 +1773,37 @@ class SQLFORM(FORM): label = isinstance( field.label, str) and T(field.label) or field.label selectfields.append(OPTION(label, _value=str(field))) - operators = SELECT(*[OPTION(T(option), _value=option) for option in options]) + operators = SELECT(*[OPTION(T(option), _value=option) for option in options],_class='form-control') _id = "%s_%s" % (value_id, name) if field.type == 'boolean': - value_input = SQLFORM.widgets.boolean.widget(field, field.default, _id=_id) + value_input = SQLFORM.widgets.boolean.widget(field, field.default, _id=_id,_class='form-control') elif field.type == 'double': - value_input = SQLFORM.widgets.double.widget(field, field.default, _id=_id) + value_input = SQLFORM.widgets.double.widget(field, field.default, _id=_id,_class='form-control') elif field.type == 'time': - value_input = SQLFORM.widgets.time.widget(field, field.default, _id=_id) + value_input = SQLFORM.widgets.time.widget(field, field.default, _id=_id,_class='form-control') elif field.type == 'date': iso_format = {'_data-w2p_date_format' : '%Y-%m-%d'} - value_input = SQLFORM.widgets.date.widget(field, field.default, _id=_id, **iso_format) + value_input = SQLFORM.widgets.date.widget(field, field.default, _id=_id,_class='form-control', **iso_format) elif field.type == 'datetime': iso_format = {'_data-w2p_datetime_format' : '%Y-%m-%d %H:%M:%S'} - value_input = SQLFORM.widgets.datetime.widget(field, field.default, _id=_id, **iso_format) + value_input = SQLFORM.widgets.datetime.widget(field, field.default, _id=_id,_class='form-control', **iso_format) elif (field.type.startswith('reference ') or field.type.startswith('list:reference ')) and \ hasattr(field.requires, 'options'): value_input = SELECT( *[OPTION(v, _value=k) for k, v in field.requires.options()], + _class='form-control', **dict(_id=_id)) elif field.type == 'integer' or \ field.type.startswith('reference ') or \ field.type.startswith('list:integer') or \ field.type.startswith('list:reference '): - value_input = SQLFORM.widgets.integer.widget(field, field.default, _id=_id) + value_input = SQLFORM.widgets.integer.widget(field, field.default, _id=_id,_class='form-control') else: value_input = INPUT( - _type='text', _id=_id, _class=field.type) + _type='text', _id=_id, + _class=(field.type or '')+' form-control') new_button = INPUT( _type="button", _value=T('New Search'), _class="btn btn-default", _title=T('Start building a new search'), @@ -1803,13 +1822,13 @@ class SQLFORM(FORM): operators, value_input, new_button, and_button, or_button, close_button, _id='%s_%s' % (field_id, name), - _class='w2p_query_row hidden', + _class='w2p_query_row', _style='display:inline')) criteria.insert(0, SELECT( _id=fields_id, _onchange="jQuery('.w2p_query_row').hide();jQuery('#%s_'+jQuery('#%s').val().replace('.','-')).show();" % (field_id, fields_id), - _style='float:left', + _style='float:left',_class='form-control', *selectfields)) fadd = SCRIPT(""" @@ -2299,7 +2318,7 @@ class SQLFORM(FORM): skeywords_id = '%s_keywords' % prefix search_widget = lambda sfield, url: CAT(FORM( INPUT(_name='keywords', _value=request.vars.keywords, - _id=skeywords_id, + _id=skeywords_id,_class='form-control', _onfocus="jQuery('#%s').change();jQuery('#%s').slideDown();" % (spanel_id, sfields_id) if advanced_search else '' ), INPUT(_type='submit', _value=T('Search'), _class="btn btn-default"), diff --git a/gluon/tools.py b/gluon/tools.py index 0fef93e3..5e128209 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -15,6 +15,7 @@ import cPickle import datetime import thread import logging +import copy import sys import glob import os @@ -948,7 +949,7 @@ class Recaptcha(DIV): captcha.append(DIV(self.errors['captcha'], _class='error')) return XML(captcha).xml() - +# this should only be used for catcha and perhaps not even for that def addrow(form, a, b, c, style, _id, position=-1): if style == "divs": form[0].insert(position, DIV(DIV(LABEL(a), _class='w2p_fl'), @@ -1360,7 +1361,7 @@ class Auth(object): # for "remember me" option response = current.response - if auth and auth.remember: + if auth and auth.remember_me: # when user wants to be logged in for longer response.session_cookie_expires = auth.expiration if signature: @@ -2400,6 +2401,14 @@ class Auth(object): # previous version of this file, other than for indentation inside # to put it inside the if-block if session.auth_two_factor_user is None: + + if settings.remember_me_form: + extra_fields = [ + Field('remember_me','boolean',default=False, + label = self.messages.label_remember_me)] + else: + extra_fields = [] + # do we use our own login form, or from a central source? if settings.login_form == self: form = SQLFORM( @@ -2410,38 +2419,10 @@ class Auth(object): submit_button=self.messages.login_button, delete_label=self.messages.delete_label, formstyle=settings.formstyle, - separator=settings.label_separator + separator=settings.label_separator, + extra_fields = extra_fields, ) - if settings.remember_me_form: - ## adds a new input checkbox "remember me for longer" - if settings.formstyle != 'bootstrap': - addrow(form, XML(" "), - DIV(XML(" "), - INPUT(_type='checkbox', - _class='checkbox', - _id="auth_user_remember", - _name="remember", - ), - XML("  "), - LABEL( - self.messages.label_remember_me, - _for="auth_user_remember", - )), "", - settings.formstyle, - 'auth_user_remember__row') - elif settings.formstyle == 'bootstrap': - addrow(form, - "", - LABEL( - INPUT(_type='checkbox', - _id="auth_user_remember", - _name="remember"), - self.messages.label_remember_me, - _class="checkbox"), - "", - settings.formstyle, - 'auth_user_remember__row') captcha = settings.login_captcha or \ (settings.login_captcha != False and settings.captcha) @@ -2615,10 +2596,10 @@ class Auth(object): # user wants to be logged in for longer self.login_user(user) session.auth.expiration = \ - request.vars.get('remember', False) and \ + request.post_vars.remember_me and \ settings.long_expiration or \ settings.expiration - session.auth.remember = 'remember' in request.vars + session.auth.remember_me = 'remember_me' in request.post_vars self.log_event(log, user) session.flash = self.messages.logged_in @@ -2724,6 +2705,13 @@ class Auth(object): passfield = self.settings.password_field formstyle = self.settings.formstyle + if self.settings.register_verify_password: + extra_fields = [ + Field("password_two", "password", requires=IS_EQUAL_TO( + request.post_vars.get(passfield,None), + error_message=self.messages.mismatched_password))] + else: + extra_fields = [] form = SQLFORM(table_user, fields=self.settings.register_fields, hidden=dict(_next=next), @@ -2731,33 +2719,10 @@ class Auth(object): submit_button=self.messages.register_button, delete_label=self.messages.delete_label, formstyle=formstyle, - separator=self.settings.label_separator + separator=self.settings.label_separator, + extra_fields = extra_fields ) - if self.settings.register_verify_password: - for i, row in enumerate(form[0].components): - item = row.element('input', _name=passfield) - if item: - form.custom.widget.password_two = \ - INPUT(_name="password_two", _type="password", - _class="password", - requires=IS_EXPR( - 'value==%s' % - repr(request.vars.get(passfield, None)), - error_message=self.messages.mismatched_password)) - if formstyle == 'bootstrap': - form.custom.widget.password_two[ - '_class'] = 'span4' - - addrow( - form, self.messages.verify_password + - self.settings.label_separator, - form.custom.widget.password_two, - self.messages.verify_password_comment, - formstyle, - '%s_%s__row' % (table_user, 'password_two'), - position=i + 1) - break captcha = self.settings.register_captcha or self.settings.captcha if captcha: addrow(form, captcha.label, captcha, @@ -3220,8 +3185,11 @@ class Auth(object): if log is DEFAULT: log = self.messages['change_password_log'] passfield = self.settings.password_field + is_crypt = copy.copy([t for t in table_user[passfield].requires + if isinstance(t,CRYPT)][0]) + is_crypt.min_length = 0 form = SQLFORM.factory( - Field('old_password', 'password', + Field('old_password', 'password', requires=[is_crypt], label=self.messages.old_password), Field('new_password', 'password', label=self.messages.new_password,