def register(self): self.auth.db.auth_user.password.writable = True fields = [field for field in self.auth.db.auth_user if field.writable] for k, field in enumerate(fields): if field.type == "password": fields.insert(k + 1, Field("password_again", "password")) break form = Form(fields, submit_value="Sign Up", formstyle=self.formstyle) user = None if form.submitted: res = self.auth.register(form.vars) form.accepted = not res.get("errors") form.errors = res.get("errors") if not form.errors: self.auth.flash.set("User registered succesfully") self._postprocessing("register", form, user) form.param.sidecar.append( A("Sign In", _href="../auth/login", _class="info", _role="button")) form.param.sidecar.append( A( "Lost Password", _href="../auth/request_reset_password", _class="info", _role="button", )) return form
def render_action_button(self, url, button_text, icon, size='small', row_id=None, user_signature=None, page=None): separator = '?' if row_id: url += '/%s' % (row_id) if user_signature: url += '%suser_signature=%s' % (separator, user_signature) separator = '&' if page: url += '%spage=%s' % (separator, page) if self.include_action_button_text: _a = A(_href=url, _class='button is-%s' % size, _title=button_text) _span = SPAN(_class='icon is-%s' % size) _span.append(I(_class='fas %s' % icon)) _a.append(_span) _a.append(SPAN(button_text)) else: _a = A(I(_class='fas %s' % icon), _href=url, _class='button is-%s' % size, _title=button_text) return _a
def script(self): js = ( '<script type="text/javascript">' ' $(document).ready(function() {' ' $(\'#datatables_table\').DataTable( {' ' dom: "lfrtip", ' ' processing: true, ' ' serverSide: true, ' ' lengthMenu: [ [10, 15, 20, -1], [10, 15, 20, \'All\'] ], ' ' pageLength: %s, ' ' pagingType: "numbers", ' ' ajax: "%s", ' ' columns: [' % (self.page_length, self.data_url)) # add the field values for field in self.fields: js += ( '{' ' "data": "%s", ' ' "name": "%s", ' ' "visible": %s, ' '},' % (field.name, field.name, 'true' if field.visible else 'false')) # add the row buttons js += ( '{' ' data: null,' ' render: function ( data, type, row ) {' ' var edit_url=\'%s\'; ' ' edit_url = edit_url.replace("record_id", row.DT_RowId); ' ' var delete_url=\'%s\'; ' ' delete_url = delete_url.replace("record_id", row.DT_RowId); ' ' return edit_url + " " + delete_url ' ' }, ' ' orderable: false, ' '}' % (A(I(_class='fas fa-edit'), _href=self.edit_url if self.edit_url else '#', _class='button is-small'), A(I(_class='fas fa-trash'), _href=self.delete_url if self.delete_url else '#', _class='button is-small', _message='Delete Record'))) js += '], columnDefs: [' for index, field in enumerate(self.fields): if not field.visible: js += '{"visible": false, "targets": %s},' % index js += '{className: "has-text-centered", "targets": %s}' % (index + 1) js += ('],' 'order: ') for sort in self.sort_sequence: js += '[ %s, "%s" ]' % (sort[0], sort[1]) js += (',' ' stateSave: true, ' ' select: true, ' ' });' ' $(".dataTables_filter input").focus().select();' '});' '</script>') return str(js)
def request_reset_password(self): form = Form( [ Field( "email", label=self.auth.param.messages["labels"].get("username_or_email"), requires=IS_NOT_EMPTY(), ) ], submit_value=self.auth.param.messages["buttons"]["request"], formstyle=self.formstyle, ) if form.accepted: email = form.vars.get("email", "") self.auth.request_reset_password(email, send=True, next="") self._set_flash("password-reset-link-sent") self._postprocessing("request_reset_password", form, None) form.param.sidecar.append( A( self.auth.param.messages["buttons"]["sign-in"], _href="../auth/login", _class=self.auth.param.button_classes["sign-in"], _role="button", ) ) if self.auth.allows("register"): form.param.sidecar.append( A( self.auth.param.messages["buttons"]["sign-up"], _href="../auth/register", _class=self.auth.param.button_classes["sign-up"], _role="button", ) ) return form
def login(self): fields = [ Field( "username", ), Field( "login_password", type="password", label=self.auth.param.messages["labels"].get("password"), ), ] if self.auth.use_username: fields[0].label = self.auth.db.auth_user.username.label else: fields[0].label = self.auth.db.auth_user.email.label fields[1].label = self.auth.db.auth_user.password.label button_name = self.auth.param.messages["buttons"]["sign-in"] form = Form( fields, submit_value=button_name, formstyle=self.formstyle, ) user = None self.auth.next["login"] = request.query.get("next") if form.submitted: user, error = self.auth.login( form.vars.get("username"), form.vars.get("login_password") ) form.accepted = not error form.errors["username"] = error if user: self.auth.store_user_in_session(user["id"]) self._postprocessing("login", form, user) top_buttons = [] for name, plugin in self.auth.plugins.items(): url = "../auth/plugin/" + name + "/login" if self.auth.next["login"]: url = url + "?next=" + self.auth.next["login"] top_buttons.append(A(plugin.label + " Login", _href=url, _role="button")) if self.auth.allows("register"): form.param.sidecar.append( A(self.auth.param.messages["buttons"]["sign-up"], _href="../auth/register", _class=self.auth.param.button_classes["sign-up"], _role="button") ) if self.auth.allows("request_reset_password"): form.param.sidecar.append( A( self.auth.param.messages["buttons"]["lost-password"], _href="../auth/request_reset_password", _class=self.auth.param.button_classes["lost-password"], _role="button", ) ) form.structure.insert(0, DIV(DIV(*top_buttons))) return form
def render_table_header(self): up = I(**self.param.grid_class_style.get("grid-sorter-icon-up")) dw = I(**self.param.grid_class_style.get("grid-sorter-icon-down")) columns = [] sort_order = request.query.get("orderby", "") for index, field in enumerate(self.param.fields): if field.readable and (field.type != "id" or self.param.show_id): key = "%s.%s" % (field.tablename, field.name) heading = ( self.param.headings[index] if index < len(self.param.headings) else field.label ) heading = title(heading) # add the sort order query parm sort_query_parms = dict(self.query_parms) attrs = {} if isinstance(field, FieldVirtual): col = DIV(heading) elif key == sort_order: sort_query_parms["orderby"] = "~" + key url = URL(self.endpoint, vars=sort_query_parms) attrs = self.attributes_plugin.link(url=url) col = A(heading, up, **attrs) else: sort_query_parms["orderby"] = key url = URL(self.endpoint, vars=sort_query_parms) attrs = self.attributes_plugin.link(url=url) col = A(heading, dw if "~" + key == sort_order else "", **attrs) columns.append((key, col)) thead = THEAD(_class=self.param.grid_class_style.classes.get("grid-thead", "")) for key, col in columns: col_class = " grid-col-%s" % key thead.append( TH( col, _class=self.param.grid_class_style.classes.get("grid-th", "") + col_class, _style=self.param.grid_class_style.styles.get("grid-th"), ) ) if ( self.param.details or self.param.editable or self.param.deletable or self.param.pre_action_buttons or self.param.post_action_buttons ): thead.append( TH("", **self.param.grid_class_style.get("grid-th-action-button")) ) return thead
def login(self): top_buttons = self.login_buttons() # if we do not allow we only display the plugin login buttons if not self.auth.param.default_login_enabled: return DIV(*top_buttons) fields = [ Field("username", ), Field( "login_password", type="password", label=self.auth.param.messages["labels"].get("password"), ), ] if self.auth.use_username: fields[0].label = self.auth.db.auth_user.username.label else: fields[0].label = self.auth.db.auth_user.email.label fields[1].label = self.auth.db.auth_user.password.label button_name = self.auth.param.messages["buttons"]["sign-in"] form = Form( fields, submit_value=button_name, formstyle=self.formstyle, ) user = None self.auth.next["login"] = request.query.get("next") if form.submitted: user, error = self.auth.login(form.vars.get("username", ""), form.vars.get("login_password", "")) form.accepted = not error form.errors["username"] = error if user: self.auth.store_user_in_session(user["id"]) self._postprocessing("login", form, user) if self.auth.allows("register"): form.param.sidecar.append( A( self.auth.param.messages["buttons"]["sign-up"], _href="../auth/register", _class=self.auth.param.button_classes["sign-up"], _role="button", )) if self.auth.allows("request_reset_password"): form.param.sidecar.append( A( self.auth.param.messages["buttons"]["lost-password"], _href="../auth/request_reset_password", _class=self.auth.param.button_classes["lost-password"], _role="button", )) form.structure.insert(0, DIV(DIV(*top_buttons))) return form
def api(self, id=None): """Returns data according to the API request.""" # Builds the header. header = dict( is_header=True, cells=[ dict(text="First Name", sortable=True), dict(text="Last Name", sortable=True), dict(text="Arrival Time", sortable=True), dict(text="", sortable=False), # Icons ], ) # Gets the request parameters, and copies the sort order in the header. req = self._get_request_params(header) timezone = request.query.get("timezone") q = request.query.get("q", "") # Query string # Forms the query. if q: query = db( db.vue_form_table.first_name.contains(q) | db.vue_form_table.last_name.contains(q)) else: query = db.vue_form_table # Forms the select. rows = db(query).select(**req.search_args) # Builds the result rows. result_rows = [] for r in rows: cells = [] cells.append(dict(text=r.first_name)) cells.append(dict(text=r.last_name)) cells.append(dict(text=r.arrival_time.isoformat(), type="date")) cells.append( dict(raw_html=SPAN( A( I(_class="fa fa-eye"), _href=URL("vue_view_form", r.id, signer=self.signer), ), " ", A( I(_class="fa fa-pen"), _href=URL("vue_edit_form", r.id, signer=self.signer), ), ).xml())) result_rows.append( dict(cells=cells, delete=URL("delete_row", r.id, signer=self.signer))) has_more, result_rows = self._has_more(result_rows) return dict( page=req.page, has_search=True, has_delete=True, search_placeholder="", has_more=has_more, rows=[header] + result_rows, )
def register(self): self.auth.db.auth_user.password.writable = True fields = [field for field in self.auth.db.auth_user if field.writable] for k, field in enumerate(fields): if field.type == "password": fields.insert( k + 1, Field( "password_again", "password", requires=IS_EQUAL_TO(request.forms.get("password")), label=self.auth.param.messages["labels"].get("password_again"), ), ) break button_name = self.auth.param.messages["buttons"]["sign-up"] # if the form is submitted, before any validation # delete any unverified account with the same email if request.method == "POST": email = request.forms.get("email") if email: self.auth.get_or_delete_existing_unverified_account(email) form = Form(fields, submit_value=button_name, formstyle=self.formstyle) user = None if form.accepted: # notice that here the form is alrealdy validated res = self.auth.register(form.vars, validate=False) form.errors.update(**res.get("errors", {})) form.accepted = not form.errors if form.accepted: self._set_flash("user-registered") self._postprocessing("register", form, user) if self.auth.param.login_after_registration: redirect("login") form.param.sidecar.append( A( self.auth.param.messages["buttons"]["sign-in"], _href="../auth/login", _class=self.auth.param.button_classes["sign-in"], _role="button", ) ) if self.auth.allows("request_reset_password"): form.param.sidecar.append( A( self.auth.param.messages["buttons"]["lost-password"], _href="../auth/request_reset_password", _class=self.auth.param.button_classes["lost-password"], _role="button", ) ) return form
def edit_phone(contact_id=None, phone_id=None): assert contact_id is not None assert phone_id is not None # match the phone owner's id as well as the number's id row = db( (db.phone.contact_id == contact_id) & (db.phone.id == phone_id) ).select().first() if row is None: redirect(URL('edit_phones', contact_id)) contact_inf = db(db.contact.id == contact_id).select().first() if contact_inf.user_email != get_user_email(): abort(403) form = Form([Field('Phone', 'string'), Field('Kind', 'string')], record=dict(Phone=row.number, Kind=row.phone_type), deletable=False, csrf_session=session, formstyle=FormStyleBulma) form.param.sidecar.append(SPAN(" ", A("Back", _class="button", _href=URL('edit_phones', contact_id)))) if form.accepted: # insert the number for that contact number = form.vars['Phone'] phone_type = form.vars['Kind'] db((db.phone.contact_id == contact_id) & (db.phone.id == phone_id)).update( number=number, phone_type=phone_type ) redirect(URL('edit_phones', contact_id)) contact_inf = db.contact[contact_id] first_name = contact_inf.first_name last_name = contact_inf.last_name name = f"{first_name} {last_name}" return dict(form=form, name=name)
def render_table_pager(self): pager = DIV(**self.param.grid_class_style.get("grid-pagination")) previous_page_number = None for page_number in self.iter_pages( self.current_page_number, self.number_of_pages ): pager_query_parms = dict(self.query_parms) pager_query_parms["page"] = page_number # if there is a gat add a spacer if previous_page_number and page_number - previous_page_number > 1: pager.append(SPAN("...", _style="margin:0 10px;")) is_current = self.current_page_number == page_number page_name = ( "grid-pagination-button-current" if is_current else "grid-pagination-button" ) attrs = self.attributes_plugin.link( url=URL(self.endpoint, vars=pager_query_parms) ) pager.append( A( page_number, **self.param.grid_class_style.get(page_name), _role="button", **attrs, ) ) previous_page_number = page_number return pager
def example_html_grid(path=None): # controllers and used for all grids in the app grid_param = dict(rows_per_page=5, include_action_button_text=True, search_button_text="Filter", formstyle=FormStyleDefault, grid_class_style=GridClassStyle) search_queries = [ ['By Name', lambda value: db.thing.name.contains(value)], ['By Color', lambda value: db.thing.color == value], [ 'By Name or Color', lambda value: db.thing.name.contains(value) | (db.thing.color == value) ], ] query = db.thing.id > 0 orderby = [db.thing.name] columns = [field for field in db.thing if field.readable] columns.insert(0, Column("Custom", lambda row: A("click me"))) grid = Grid(path, query, columns=columns, search_queries=search_queries, orderby=orderby, show_id=False, T=T, **grid_param) grid.formatters['thing.color'] = lambda color: I(_class="fa fa-circle", _style="color:" + color) return dict(grid=grid)
def make(self, field, value, error, title, placeholder="", readonly=False): field_id = to_id(field) control = DIV() if value and not error: download_div = DIV() if not readonly: download_div.append(LABEL("Currently: ", )) url = getattr(field, "download_url", lambda value: "#")(value) download_div.append(A(" download ", _href=url)) if not readonly: download_div.append( INPUT( _type="checkbox", _value="ON", _name="_delete_" + field.name, _title=title, )) download_div.append(" (check to remove)") control.append(download_div) control.append(LABEL("Change: ")) control.append(INPUT(_type="file", _id=field_id, _name=field.name)) return control
def table(self): _html = DIV() if self.create_url and self.create_url != '': _a = A('', _href=self.create_url, _class='button', _style='margin-bottom: 1rem;') _span = SPAN(_class='icon is-small') _span.append(I(_class='fas fa-plus')) _a.append(_span) _a.append(SPAN('New')) _html.append(_a) _table = TABLE(_id='datatables_table', _class='compact stripe hover cell-border order-column', _style='padding-top: 1rem;') _thead = THEAD() _tr = TR() for field in self.fields: _tr.append(TH(field.label, _class='datatables-header')) _tr.append( TH('ACTIONS', _class='datatables-header has-text-centered', _style='color: black; width: 1px; white-space: nowrap;')) _thead.append(_tr) _table.append(_thead) _table.append(TBODY()) _html.append(_table) return str(_html)
def render_table_pager(self): pager = DIV(**self.param.grid_class_style.get("grid-pagination")) previous_page_number = None for page_number in self.iter_pages(self.current_page_number, self.number_of_pages): pager_query_parms = dict(self.query_parms) pager_query_parms["page"] = page_number # if there is a gat add a spacer if previous_page_number and page_number - previous_page_number > 1: pager.append(SPAN("...", _style="margin:0 10px;")) is_current = self.current_page_number == page_number page_name = ("grid-pagination-button-current" if is_current else "grid-pagination-button") attrs = ({ "_hx-get": URL(self.endpoint, vars=pager_query_parms), "_hx-target": self.param.htmx_target, "_hx-swap": "innertHTML", } if self.param.htmx_target else { "_href": URL(self.endpoint, vars=pager_query_parms) }) pager.append( A( page_number, **self.param.grid_class_style.get(page_name), _role="button", **attrs, )) previous_page_number = page_number return pager
def render_action_button( self, url, button_text, icon, icon_size="small", additional_classes=None, additional_styles=None, override_classes=None, override_styles=None, message=None, onclick=None, row_id=None, name="grid-button", row=None, **attr, ): separator = "?" if row_id: url += "/%s" % row_id classes = self.param.grid_class_style.classes.get(name, "") styles = self.param.grid_class_style.styles.get(name, "") def join(items): return ( " ".join(items) if isinstance(items, (list, tuple)) else " %s" % items ) if override_classes: classes = join(override_classes) elif additional_classes: classes += join(additional_classes) if override_styles: styles = join(override_styles) elif additional_styles: styles += join(additional_styles) if callable(url): url = url(row) link = A( I(_class="fa %s" % icon), _href=url, _role="button", _class=classes, _message=message, _title=button_text, _style=styles, **attr, ) if self.param.include_action_button_text: link.append( XML( '<span class="grid-action-button-text"> %s</span>' % button_text ) ) return link
def render_table_pager(self): _pager = DIV(_class='is-pulled-right') for page_number in self.iter_pages(): if page_number: pager_query_parms = dict(self.query_parms) pager_query_parms['page'] = page_number pager_query_parms['user_signature'] = self.user_signature if self.current_page_number == page_number: _pager.append(A(page_number, _class='button is-primary is-small', _href=URL(self.endpoint, vars=pager_query_parms))) else: _pager.append(A(page_number, _class='button is-small', _href=URL(self.endpoint, vars=pager_query_parms))) else: _pager.append('...') return _pager
def render_table_header(self): up = I(**self.param.grid_class_style.get("grid-sorter-icon-up")) dw = I(**self.param.grid_class_style.get("grid-sorter-icon-down")) columns = [] sort_order = request.query.get("orderby", "") for index, field in enumerate(self.param.fields): if field.readable and (field.type != "id" or self.param.show_id): key = "%s.%s" % (field.tablename, field.name) heading = (self.param.headings[index] if index < len(self.param.headings) else field.label) heading = title(heading) # add the sort order query parm sort_query_parms = dict(self.query_parms) if key == sort_order: sort_query_parms["orderby"] = "~" + key href = URL(self.endpoint, vars=sort_query_parms) col = A(heading, up, _href=href) else: sort_query_parms["orderby"] = key href = URL(self.endpoint, vars=sort_query_parms) col = A(heading, dw if "~" + key == sort_order else "", _href=href) columns.append((key, col)) thead = THEAD() for key, col in columns: col_class = "grid-col-%s" % key thead.append( TH( col, _class=self.param.grid_class_style.classes.get( "grid-th", "") + col_class, _style=self.param.grid_class_style.styles.get("grid-th"), )) if self.param.details or self.param.editable or self.param.deletable: thead.append( TH("", **self.param.grid_class_style.get('grid-th-action-button'))) return thead
def render_table_header(self): _thead = THEAD() for index, field in enumerate(self.fields): if field.name not in [x.name for x in self.hidden_fields] and ( field.name != 'id' or (field.name == 'id' and self.show_id)): try: heading = self.headings[index] except: if field.table == self.tablename: heading = field.label else: heading = str(field.table) # add the sort order query parm sort_query_parms = dict(self.query_parms) sort_query_parms['sort'] = index current_sort_dir = 'asc' if '%s.%s' % (field.tablename, field.name) in self.storage_values['orderby']: sort_query_parms['sort'] = -index _h = A(heading.replace('_', ' ').upper(), _href=URL(self.endpoint, vars=sort_query_parms)) _h.append(SPAN(I(_class='fas fa-sort-up'), _class='is-pulled-right')) elif '~%s.%s' % (field.tablename, field.name) in self.storage_values['orderby']: _h = A(heading.replace('_', ' ').upper(), _href=URL(self.endpoint, vars=sort_query_parms)) _h.append(SPAN(I(_class='fas fa-sort-down'), _class='is-pulled-right')) else: _h = A(heading.replace('_', ' ').upper(), _href=URL(self.endpoint, vars=sort_query_parms)) if 'sort_dir' in sort_query_parms: current_sort_dir = sort_query_parms['sort_dir'] del sort_query_parms['sort_dir'] if index == int(request.query.get('sort', 0)) and current_sort_dir == 'asc': sort_query_parms['sort_dir'] = 'desc' _th = TH() _th.append(_h) _thead.append(_th) if self.editable or self.deletable: _thead.append(TH('ACTIONS', _style='text-align: center; width: 1px; white-space: nowrap;')) return _thead
def footer(self, count, offset, len_items): """generates the footer""" T = self.T page = int(offset / self.limit) last_page = math.floor(count / self.limit) return DIV( A("First", _href=self.url(0), _class="button") if page > 1 else "", A("Previous", _href=self.url(page - 1), _class="button") if page > 0 else "", SPAN( T("page %s/%s (%s/%s records)") % (page + 1, last_page + 1, len_items, count)), A("Next", _href=self.url(page + 1), _class="button") if page < last_page else "", A("Last", _href=self.url(last_page - 1), _class="button") if page < last_page - 1 else "", SCRIPT(Grid.script), _class="py4web-grid-footer", )
def request_reset_password(self): form = Form( [Field("email", label="Username of Email")], submit_value="Request", formstyle=self.formstyle, ) if form.submitted: email = form.vars.get("email") self.auth.request_reset_password(email, send=True, next="") self.auth.flash.set("Password reset link sent") self._postprocessing("request_reset_password", form, None) form.param.sidecar.append( A("Sign In", _href="../auth/login", _class="info", _role="button")) form.param.sidecar.append( A("Sign Up", _href="../auth/register", _class="info", _role="button")) return form
def header(self, id, message=""): """generates the header""" T = self.T div = DIV(_class="py4web-grid-header") if message: div.append(DIV(message, _class="py4web-grid-message")) if self.create and id is None: div.append( A( T("Create Record"), _class="button", _href=request.url.split("?")[0] + "?id=0", ) ) elif (self.create and id == 0) or (self.editable and id and id > 0): query = dict(request.query) query.pop("id", None) url = request.url.split("?")[0] + "?" + urllib.parse.urlencode(query) div.append(A(T("Back"), _class="button", _href=url)) div.append(T("New Record") if id == 0 else T("Edit Record")) return div
def login_buttons(self): top_buttons = [] for name, plugin in self.auth.plugins.items(): url = "../auth/plugin/" + name + "/login" if request.query.get("next"): url = url + "?next=" + request.query.get("next") if (name != "email_auth" ): # do not add the top button for the email auth plugin top_buttons.append( A(plugin.label + " Login", _href=url, _role="button")) return top_buttons
def sortlink(self, key): """returns the link to sort by key""" label = self.labels.get(key, key) if not self.is_sortable(key): return label order = request.query.get("@order") if order == key: new_order, caret = "~" + key, "▼" elif order == "~" + key: new_order, caret = key, "▲" else: new_order, caret = key, "" return A(label + caret, _href=self.url(page=0, order=new_order))
def login(self): form = Form( [Field("username"), Field("login_password", type="password")], submit_value="Sign In", formstyle=self.formstyle, ) user = None if form.submitted: user, error = self.auth.login(form.vars.get("username"), form.vars.get("login_password")) form.accepted = not error form.errors["username"] = error if user: self.auth.store_user_in_session(user["id"]) self._postprocessing("login", form, user) top_buttons = [] next = request.query.get("next") for name, plugin in self.auth.plugins.items(): url = "../auth/plugin/" + name + "/login" if next: url = url + "?next=" + next top_buttons.append( A(plugin.name + " Login", _href=url, _role="button")) form.param.sidecar.append( A("Sign Up", _href="../auth/register", _class="info", _role="button")) form.param.sidecar.append( A( "Lost Password", _href="../auth/request_reset_password", _class="info", _role="button", )) return DIV(DIV(*top_buttons), form)
def add_phone(contact_id): assert contact_id is not None # contact_id is the id of the contact for which we are inserting an additional number form = Form([Field('Phone', 'string'), Field('Kind', 'string')], csrf_session=session, formstyle=FormStyleBulma) form.param.sidecar.append(SPAN(" ", A("Back", _class="button", _href=URL('edit_phones', contact_id)))) if form.accepted: # insert the number for that contact number = form.vars['Phone'] phone_type = form.vars['Kind'] db.phone.insert( contact_id=contact_id, number=number, phone_type=phone_type ) redirect(URL('edit_phones', contact_id)) contact_inf = db.contact[contact_id] first_name = contact_inf.first_name last_name = contact_inf.last_name name = f"{first_name} {last_name}" return dict(form=form, name=name)
def produce(self, table, vars, errors, readonly, deletable, classes=None): self.classes.update(classes or {}) form = FORM( _method="POST", _action=request.url, _enctype="multipart/form-data" ) controls = dict( widgets=dict(), hidden_widgets=dict(), errors=dict(), begin=XML(form.xml().split("</form>")[0]), end=XML("</form>"), ) class_label = self.classes["label"] class_outer = self.classes["outer"] class_inner = self.classes["inner"] class_error = self.classes["error"] class_info = self.classes["info"] for field in table: input_id = "%s_%s" % (field.tablename, field.name) value = vars.get(field.name, field.default) error = errors.get(field.name) field_class = field.type.split()[0].replace(":", "-") if not field.readable and not field.writable: continue if not readonly and not field.writable: continue if field.type == "blob": # never display blobs (mistake?) continue if field.type == "id" and value is None: field.writable = False continue if readonly or field.type == "id": control = DIV(field.represent and field.represent(value) or value or "") elif field.widget: control = field.widget(table, value) elif field.type == "text": control = TEXTAREA(value or "", _id=input_id, _name=field.name) elif field.type == "date": control = INPUT( _value=value, _type="date", _id=input_id, _name=field.name ) elif field.type == "datetime": if isinstance(value, str): value = value.replace(" ", "T") control = INPUT( _value=value, _type="datetime-local", _id=input_id, _name=field.name ) elif field.type == "time": control = INPUT( _value=value, _type="time", _id=input_id, _name=field.name ) elif field.type == "boolean": control = INPUT( _type="checkbox", _id=input_id, _name=field.name, _value="ON", _checked=value, ) elif field.type == "upload": control = DIV(INPUT(_type="file", _id=input_id, _name=field.name)) if value: control.append(A("download", _href=field.download_url(value))) control.append( INPUT( _type="checkbox", _value="ON", _name="_delete_" + field.name ) ) control.append("(check to remove)") elif get_options(field.requires) is not None: multiple = field.type.startswith("list:") value = list(map(str, value if isinstance(value, list) else [value])) option_tags = [ OPTION(v, _value=k, _selected=(not k is None and k in value)) for k, v in get_options(field.requires) ] control = SELECT( *option_tags, _id=input_id, _name=field.name, _multiple=multiple ) else: field_type = "password" if field.type == "password" else "text" control = INPUT( _type=field_type, _id=input_id, _name=field.name, _value=value, _class=field_class, ) key = control.name.rstrip("/") if key == "input": key += "[type=%s]" % (control["_type"] or "text") control["_class"] = self.classes.get(key, "") controls["widgets"][field.name] = control if error: controls["errors"][field.name] = error form.append( DIV( LABEL(field.label, _for=input_id, _class=class_label), DIV(control, _class=class_inner), P(error, _class=class_error) if error else "", P(field.comment or "", _class=class_info), _class=class_outer, ) ) if 'id' in vars: form.append(INPUT(_name='id',_value=vars['id'],_hidden=True)) if deletable: controls["delete"] = INPUT( _type="checkbox", _value="ON", _name="_delete", _class=self.classes["input[type=checkbox]"], ) form.append( DIV( DIV(controls["delete"], _class=class_inner,), P("check to delete", _class="help"), _class=class_outer, ) ) controls["submit"] = INPUT( _type="submit", _value="Submit", _class=self.classes["input[type=submit]"], ) submit = DIV(DIV(controls["submit"], _class=class_inner,), _class=class_outer,) form.append(submit) return dict(form=form, controls=controls)
def produce(self, table, vars, errors, readonly, deletable, classes=None): self.classes.update(classes or {}) form = FORM(_method="POST", _action=request.url, _enctype="multipart/form-data") controls = Param( labels=dict(), widgets=dict(), comments=dict(), hidden_widgets=dict(), placeholders=dict(), titles=dict(), errors=dict(), begin=XML(form.xml().split("</form>")[0]), submit="", delete="", end=XML("</form>"), ) class_label = self.classes["label"] class_outer = self.classes["outer"] class_inner = self.classes["inner"] class_error = self.classes["error"] class_info = self.classes["info"] for field in table: input_id = "%s_%s" % (field.tablename, field.name) value = vars.get(field.name, field.default) error = errors.get(field.name) field_class = "type-" + field.type.split()[0].replace(":", "-") placeholder = ( field._placeholder if "_placeholder" in field.__dict__ else None ) title = field._title if "_title" in field.__dict__ else None # only diplay field if readable or writable if not field.readable and not field.writable: continue # if this is a reaonly field only show readable fields if readonly: if not field.readable: continue # if this is an create form (unkown id) then only show writable fields elif not vars.get("id"): if not field.writable: continue # ignore blob fields if field.type == "blob": # never display blobs (mistake?) continue # ignore fields of type id its value is equal to None if field.type == "id" and value is None: field.writable = False continue # if the form is readonly or this is an id type field, display it as readonly if readonly or not field.writable or field.type == "id": if field.type == "boolean": control = INPUT( _type="checkbox", _id=input_id, _name=field.name, _value="ON", _disabled="", _checked=value, _title=title, ) else: control = DIV( field.represent and field.represent(value) or value or "" ) # if we have a widget for the field use it elif field.widget: control = field.widget(table, value) # else pick the proper default widget elif field.type == "text": control = TEXTAREA( value or "", _id=input_id, _name=field.name, _placeholder=placeholder, _title=title, ) elif field.type == "date": control = INPUT( _value=value, _type="date", _id=input_id, _name=field.name, _placeholder=placeholder, _title=title, ) elif field.type == "datetime": if isinstance(value, str): value = value.replace(" ", "T") control = INPUT( _value=value, _type="datetime-local", _id=input_id, _name=field.name, _placeholder=placeholder, _title=title, ) elif field.type == "time": control = INPUT( _value=value, _type="time", _id=input_id, _name=field.name, _placeholder=placeholder, _title=title, ) elif field.type == "boolean": control = INPUT( _type="checkbox", _id=input_id, _name=field.name, _value="ON", _checked=value, _title=title, ) elif field.type == "upload": control = DIV() if value and field.download_url is not None and not error: download_div = DIV() download_div.append( LABEL( "Currently: ", ) ) download_div.append( A(" download ", _href=field.download_url(value)) ) download_div.append( INPUT( _type="checkbox", _value="ON", _name="_delete_" + field.name, _title=title, ) ) download_div.append(" (check to remove)") control.append(download_div) control.append(LABEL("Change: ")) control.append(INPUT(_type="file", _id=input_id, _name=field.name)) elif get_options(field.requires) is not None and field.writable == True: multiple = field.type.startswith("list:") value = list(map(str, value if isinstance(value, list) else [value])) option_tags = [ OPTION(v, _value=k, _selected=(not k is None and k in value)) for k, v in get_options(field.requires) ] control = SELECT( *option_tags, _id=input_id, _name=field.name, _multiple=multiple, _title=title ) else: field_type = "password" if field.type == "password" else "text" if field.type.startswith("list:"): value = json.dumps(value or []) control = INPUT( _type=field_type, _id=input_id, _name=field.name, _value=None if field.type == "password" else value, _class=field_class, _placeholder=placeholder, _title=title, _autocomplete="off" if field_type == "password" else "on", ) key = control.name.rstrip("/") if key == "input": key += "[type=%s]" % (control["_type"] or "text") control["_class"] = ( control.attributes.get("_class", "") + " " + self.classes.get(key, "") ).strip() controls["labels"][field.name] = field.label controls["widgets"][field.name] = control controls["comments"][field.name] = field.comment if field.comment else "" controls["titles"][field.name] = title controls["placeholders"][field.name] = placeholder if error: controls["errors"][field.name] = error if field.type == "boolean": form.append( DIV( SPAN(control, _class=class_inner), LABEL( " " + field.label, _for=input_id, _class=class_label, _style="display: inline !important", ), P(error, _class=class_error) if error else "", P(field.comment or "", _class=class_info), _class=class_outer, ) ) else: form.append( DIV( LABEL(field.label, _for=input_id, _class=class_label), DIV(control, _class=class_inner), P(error, _class=class_error) if error else "", P(field.comment or "", _class=class_info), _class=class_outer, ) ) if vars.get("id"): form.append(INPUT(_name="id", _value=vars["id"], _hidden=True)) if deletable: controls["delete"] = INPUT( _type="checkbox", _value="ON", _name="_delete", _class=self.classes["input[type=checkbox]"], ) form.append( DIV( SPAN( controls["delete"], _class=class_inner, _stye="vertical-align: middle;", ), P( " check to delete", _class="help", _style="display: inline !important", ), _class=class_outer, ) ) controls["submit"] = INPUT( _type="submit", _value="Submit", _class=self.classes["input[type=submit]"], ) submit = DIV( DIV( controls["submit"], _class=class_inner, ), _class=class_outer, ) form.append(submit) return dict(form=form, controls=controls)
def count(number=1): message = T("Cat").format(n=number) link = A(T("more"), _href=URL("count/%s" % (number + 1))) return HTML(BODY(H1(message, " ", link))).xml()
def FormStyleDefault(table, vars, errors, readonly, deletable, classes=None): form = FORM(_method='POST',_action='#',_enctype='multipart/form-data') classes = classes or {} class_label = classes.get('label', '') class_outer = classes.get('outer', '') class_inner = classes.get('inner', '') class_error = classes.get('error', '') class_info = classes.get('info', '') for field in table: input_id = '%s_%s' % (field.tablename, field.name) value = field.formatter(vars.get(field.name)) error = errors.get(field.name) field_class = field.type.split()[0].replace(':','-') if field.type == 'blob': # never display blobs (mistake?) continue elif readonly or field.type=='id': if not field.readable: continue else: control = DIV(field.represent and field.represent(value) or value or '') elif not field.writable: continue elif field.widget: control = field.widget(table, value) elif field.type == 'text': control = TEXTAREA(value or '', _id=input_id,_name=field.name) elif field.type == 'boolean': control = INPUT(_type='checkbox', _id=input_id, _name=field.name, _value='ON', _checked = value) elif field.type == 'upload': control = DIV(INPUT(_type='file', _id=input_id, _name=field.name)) if value: control.append(A('download', _href=donwload_url(value))) control.append(INPUT(_type='checkbox',_value='ON', _name='_delete_'+field.name)) control.append('(check to remove)') elif hasattr(field.requires, 'options'): multiple = field.type.startswith('list:') value = list(map(str, value if isinstance(value, list) else [value])) options = [OPTION(v,_value=k,_selected=(k in value)) for k,v in field.requires.options()] control = SELECT(*options, _id=input_id, _name=field.name, _multiple=multiple) else: field_type = 'password' if field.type == 'password' else 'text' control = INPUT(_type=field_type, _id=input_id, _name=field.name, _value=value, _class=field_class) key = control.name.rstrip('/') if key == 'input': key += '[type=%s]' % (control['_type'] or 'text') control['_class'] = classes.get(key, '') form.append(DIV( LABEL(field.label, _for=input_id, _class=class_label), DIV(control, _class=class_inner), P(error, _class=class_error) if error else '', P(field.comment or '', _class=class_info), _class=class_outer)) if deletable: form.append(DIV(DIV(INPUT(_type='checkbox',_value='ON',_name='_delete', _class=classes.get('input[type=checkbox]')), _class=class_inner), P('check to delete',_class="help"), _class=class_outer)) submit = DIV(DIV(INPUT(_type='submit',_value='Submit', _class=classes.get('input[type=submit]')), _class=class_inner), _class=class_outer) form.append(submit) return form