def render_default_form(self): seach_type = safe_int(request.query.get("seach_type", 0), default=0) seach_string = request.query.get("seach_string") options = [ OPTION(items[0], _value=k, _selected=(k == seach_type)) for k, items in enumerate(self.param.search_queries) ] hidden_fields = [ INPUT(_name=key, _value=request.query.get(key), _type="hidden") for key in request.query if not key in ("seach_type", "seach_string") ] form = FORM(*hidden_fields, **dict(_method="GET", _action=self.endpoint)) select = SELECT(*options, **dict(_name="seach_type",)) input = INPUT(_type="text", _name="seach_string", _value=seach_string,) submit = INPUT(_type="submit", _value="Search") clear_script = "document.querySelector('[name=seach_string]').value='';" clear = INPUT(_type="submit", _value="Clear", _onclick=clear_script) div = DIV(_id="grid-search", **self.param.grid_class_style.get("grid-search")) # we do not need classes for these elements tr = TR() if len(options) > 1: tr.append(TD(select)) tr.append(TD(input)) tr.append(TD(submit, clear)) form.append(TABLE(tr)) div.append(form) return div
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 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 test_find(self): a = DIV('A', _class='a') b = SPAN('B', _id='b') div = DIV(DIV(a), DIV(b)) self.assertEqual(div.find('.a')[0], a) self.assertEqual(div.find('#b')[0], b) self.assertEqual(div.find('div.a')[0], a) self.assertEqual(div.find('span#b')[0], b) self.assertEqual(div.find('span')[0], b) self.assertEqual(len(div.find('div')), 4) self.assertEqual(len(div.find('abc')), 0)
def make(self): """makes the grid, must be called inside an action""" T, db, table, query = self.T, self.db, self.table, self.query # bypass rest API if it is a form id = safeint(request.query.get("id") or request.forms.get("id")) if id is not None: if (id == 0 and not self.create) or (id > 0 and not self.editable): raise HTTP(404) record = db(query)(table.id == id).select().first() if id else None form = Form(table, record, deletable=self.deletable, **self.form_attributes) form.action = (request.url.split("?")[0] + "?" + urllib.parse.urlencode(request.query)) del request.query["id"] if form.deleted: message = T("Record deleted") elif form.accepted: message = T("Record created") if not id else T("Record Saved") else: return DIV(self.header(id), form, _class="py4web-grid") else: message = "" if self.denormalize: lookup = [] for k, fs in self.denormalize.items(): lookup.append("%s!:%s[%s]" % (k, k, ",".join(fs))) request.query["@lookup"] = ",".join(lookup) offset = safeint(request.query.get("@offset", 0)) request.query["@count"] = "true" request.query["@limit"] = offset + self.limit id = None data = self.restapi("GET", self.table._tablename, None, request.query) items = data.get("items", []) count = data.get("count", 0) table = TABLE(_class="table") fields = (items[0].keys() if items else [f.rsplit(".", 1)[0] for f in request.query if f[:1] != "@"]) table.append(TR(*[TH(self.sortlink(key)) for key in fields])) table.append(TR(*[TH(self.filterlink(key)) for key in fields])) for item in items: table.append( TR(*[ TD(self.render(key, value)) for key, value in item.items() ])) header = self.header(id, message) footer = self.footer(count, offset, len(items)) return DIV(header, table, footer, _class="py4web-grid")
def test_invalid_atribute_name(self): i = [" ", "=", "'", '"', ">", "<", "/"] for x in i: DIV = TAG.div b = "_any%sthings" % x attr = {b: "invalid_atribute_name"} self.assertRaises(ValueError, DIV("any content", **attr).xml)
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 grid(self, table): name = 'vue%s' % str(uuid.uuid4())[:8] return DIV(self.mtable(table), TAG.SCRIPT(_src=URL('static/js/axios.min.js')), TAG.SCRIPT(_src=URL('static/js/vue.min.js')), TAG.SCRIPT(_src=URL('static/js/utils.js')), TAG.SCRIPT(_src=URL('static/components/mtable.js')), TAG.SCRIPT( XML('var app=utils.app("%s"); app.start()' % name)), _id=name)
def mycomponent(): # create a form object form = Form([Field("your_name")]) # if the form is not submmitted show a welcome flash message if not request.forms: flash.set("Welcome") # if the form is submitted and accepted display another message elif form.accepted: flash.set("Welcome " + request.forms["your_name"]) # return the form if it has not already been submitted and accepted return DIV(form if not form.accepted else "done!").xml()
def grid(self, table): name = "vue%s" % str(uuid.uuid4())[:8] return DIV( self.mtable(table), TAG.SCRIPT(_src=URL("static/js/axios.min.js")), TAG.SCRIPT(_src=URL("static/js/vue.min.js")), TAG.SCRIPT(_src=URL("static/js/utils.js")), TAG.SCRIPT(_src=URL("static/components/mtable.js")), TAG.SCRIPT(XML('var app={}; app.vue = new Vue({el:"#%s"});' % name)), _id=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 = ({ "_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_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 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 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 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 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 test_tags(self): DIV = TAG.div IMG = TAG['img/'] self.assertEqual(DIV().xml(), "<div></div>") self.assertEqual(IMG().xml(), "<img/>") self.assertEqual(DIV(_id="my_id").xml(), "<div id=\"my_id\"></div>") self.assertEqual(IMG(_src="crazy").xml(), "<img src=\"crazy\"/>") self.assertEqual( DIV(_class="my_class", _mytrueattr=True).xml(), "<div class=\"my_class\" mytrueattr=\"mytrueattr\"></div>") self.assertEqual( DIV(_id="my_id", _none=None, _false=False, without_underline="serius?").xml(), "<div id=\"my_id\"></div>") self.assertEqual( DIV("<b>xmlscapedthis</b>").xml(), "<div><b>xmlscapedthis</b></div>") self.assertEqual( DIV(XML("<b>don'txmlscapedthis</b>")).xml(), "<div><b>don'txmlscapedthis</b></div>")
def make(self, field, value, error, title, placeholder="", readonly=False): control = DIV() field_id = to_id(field) value = list(map(str, value if isinstance(value, list) else [value])) field_options = [[k, v, (not k is None and k in value)] for k, v in get_options(field.requires) if k != ""] for k, v, selected in field_options: control.append( INPUT( v, _id=field_id, _value=k, _label=v, _name=field.name, _type="radio", _checked=selected, )) control.append(LABEL(v, _for=field_id)) return control
def radio_widget(field, values_dict): control = DIV() if "_table" in dir(field): tablename = field._table else: tablename = "no_table" for k, v in get_options(field.requires)[1:]: attrs = {} if values_dict and str(values_dict["order_type"]) == k: attrs["_checked"] = "ON" control.append( INPUT( v, _id="%s_%s" % (tablename, k), _value=k, _label=v, _name=field.name, _type="radio", **attrs, )) control.append(LABEL(v, _for="%s_%s" % (tablename, k))) return control
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 mycomponent(): flash.set('Welcome') form = Form([Field("your_name")]) return DIV("Hello " + request.forms["your_name"] if form.accepted else form).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
def html_renderer(key, value, rec): if isinstance(value, str) and value[:1] == '<' and value.rstrip()[-1] == '>': return DIV(XML(value,sanitize=True), _style="height:50px;overflow:auto")
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 __call__( self, table, vars, errors, readonly, deletable, noncreate, show_id, kwargs=None, ): kwargs = kwargs if kwargs else {} form_method = "POST" form_action = request.url.split(":", 1)[1] form_enctype = "multipart/form-data" form = FORM(_method=form_method, _action=form_action, _enctype=form_enctype, **kwargs) controls = Param( labels=dict(), widgets=dict(), wrappers=dict(), comments=dict(), hidden_widgets=dict(), placeholders=dict(), titles=dict(), errors=dict(), begin=XML(form.xml().split("</form>")[0]), submit="", delete="", end=XML("</form>"), ) json_controls = dict(form_fields=[], form_values=dict(), form_buttons=[], form_method=form_method, form_action=form_action, form_enctype=form_enctype, **kwargs) 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"] all_fields = [x for x in table] if "_virtual_fields" in dir(table): all_fields += table._virtual_fields for field in all_fields: # Reset the json control fields. field_attributes = dict() field_value = None field_name = field.name field_type = field.type field_comment = field.comment if field.comment else "" field_label = field.label input_id = "%s_%s" % (field.tablename, field.name) if isinstance(field, FieldVirtual): value = None else: value = vars.get( field.name, field.default() if callable(field.default) else 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 field_disabled = False # only display field if readable or writable if not field.readable and not field.writable: continue # if this is a readonly field only show readable fields if readonly: if not field.readable: continue # do not show the id if not desired if field.type == "id" and not show_id: continue # if this is an create form (unkown id) then only show writable fields. # Some if an edit form was made from a list of fields and noncreate=True elif not vars.get("id") and noncreate: 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" or isinstance(field, FieldVirtual)): # for boolean readonly we use a readonly checbox if field.type == "boolean": control = CheckboxWidget().make(field, value, error, title, readonly=True) # for all othe readonly fields we use represent or a string else: if isinstance(field, FieldVirtual): field_value = field.f(vars) else: field_value = (field.represent and field.represent(value) or value or "") field_type = "represent" control = DIV(field_value) field_disabled = True # if we have a field.widget for the field use it but this logic is deprecated elif field.widget: control = field.widget(table, vars) # else we pick the right widget else: if field.name in self.widgets: widget = self.widgets[field.name] elif field.type == "text": widget = TextareaWidget() elif field.type == "datetime": widget = DateTimeWidget() elif field.type == "boolean": widget = CheckboxWidget() elif field.type == "upload": widget = FileUploadWidget() url = getattr(field, "download_url", lambda value: "#")(value) # Set the download url. field_attributes["_download_url"] = url # Set the flag determining whether the file is an image. field_attributes["_is_image"] = ( url != "#") and Form.is_image(value) # do we need the variables below? delete_field_attributes = dict() delete_field_attributes["_label"] = "Remove" delete_field_attributes["_value"] = "ON" delete_field_attributes["_type"] = "checkbox" delete_field_attributes["_name"] = "_delete_" + field.name json_controls["form_fields"] += [delete_field_attributes] json_controls["form_values"]["_delete_" + field.name] = None elif get_options(field.requires) is not None: widget = SelectWidget() elif field.type == "password": widget = PasswordWidget() elif field.type.startswith("list:"): widget = ListWidget() else: widget = Widget() control = widget.make(field, value, error, title, placeholder) key = control.name.rstrip("/") if key == "input": key += "[type=%s]" % (control["_type"] or "text") control["_class"] = join_classes(control.attributes.get("_class"), self.classes.get(key)) # Set the form controls. controls["labels"][field_name] = field_label controls["widgets"][field_name] = control controls["comments"][field_name] = field_comment controls["titles"][field_name] = title controls["placeholders"][field_name] = placeholder # Set the remain json field attributes. field_attributes["_title"] = title field_attributes["_label"] = field_label field_attributes["_comment"] = field_comment field_attributes["_id"] = to_id(field) field_attributes["_class"] = field_class field_attributes["_name"] = field.name field_attributes["_type"] = field.type field_attributes["_placeholder"] = placeholder field_attributes["_error"] = error field_attributes["_disabled"] = field_disabled # Add to the json controls. json_controls["form_fields"] += [field_attributes] json_controls["form_values"][field_name] = field_value if error: controls["errors"][field.name] = error if field.type == "boolean": controls.wrappers[field.name] = wrapped = SPAN( control, _class=class_inner) form.append( DIV( wrapped, 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: controls.wrappers[field.name] = wrapped = DIV( control, _class=self.class_inner_exceptions.get( control.name, class_inner), ) form.append( DIV( LABEL(field.label, _for=input_id, _class=class_label), wrapped, 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: deletable_record_attributes = dict() deletable_field_name = "_delete" deletable_field_type = "checkbox" # Set the deletable json field attributes. deletable_record_attributes["_label"] = " check to delete" deletable_record_attributes["_name"] = deletable_field_name deletable_record_attributes["_type"] = deletable_field_type deletable_record_attributes["_class"] = self.classes[ "input[type=checkbox]"] deletable_record_attributes["_value"] = "ON" # Add to the json controls. json_controls["form_fields"] += [deletable_record_attributes] json_controls["form_values"][deletable_field_name] = None controls["delete"] = INPUT( _type=deletable_field_type, _value=deletable_record_attributes["_value"], _name=deletable_field_name, _class=self.classes["input[type=checkbox]"], ) form.append( DIV( SPAN( controls["delete"], _class=class_inner, _stye="vertical-align: middle;", ), P( deletable_record_attributes["_label"], _class="help", _style="display: inline !important", ), _class=class_outer, )) submit_button_attributes = dict() submit_button_field_type = "submit" # Set the deletable json field attributes. submit_button_attributes["_label"] = "Submit" submit_button_attributes["_type"] = submit_button_field_type submit_button_attributes["_class"] = self.classes["input[type=submit]"] # Add to the json controls. json_controls["form_buttons"] += [submit_button_attributes] controls["submit"] = INPUT( _type=submit_button_field_type, _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, json_controls=json_controls)
def render_search_form(self): # TODO: Do we need this? div = DIV(_id="grid-search", **self.param.grid_class_style.get("grid-search")) div.append(self.param.search_form.custom["begin"]) tr = TR(**self.param.grid_class_style.get("search_form_tr")) for field in self.param.search_form.table: td = TD(**self.param.grid_class_style.get("search_form_td")) if field.type == "boolean": sb = DIV(**self.param.grid_class_style.get("search_boolean")) sb.append(self.param.search_form.custom["widgets"][field.name]) sb.append(field.label) td.append(sb) else: td.append(self.param.search_form.custom["widgets"][field.name]) if (field.name in self.param.search_form.custom["errors"] and self.param.search_form.custom["errors"][field.name]): td.append( DIV( self.param.search_form.custom["errors"][field.name], _style="color:#ff0000", )) tr.append(td) if self.param.search_button_text: tr.append( TD( INPUT( _class="button", _type="submit", _value=self.param.search_button_text, ), **self.param.grid_class_style.get("search_form_td"), )) else: tr.append( TD( self.param.search_form.custom["submit"], **self.param.grid_class_style.get("search_form_td"), )) div.append( TABLE(tr, **self.param.grid_class_style.get("search_form_table"))) for hidden_widget in self.param.search_form.custom[ "hidden_widgets"].keys(): div.append( self.param.search_form.custom["hidden_widgets"][hidden_widget]) div.append(self.param.search_form.custom["end"]) return div
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 FormStyleDefault(table, vars, errors, readonly, deletable, classes=None): form = FORM(_method="POST", _action=request.path, _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 = vars.get(field.name) error = errors.get(field.name) field_class = field.type.split()[0].replace(":", "-") if not field.readable: 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: 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 == "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 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=(not k is None and 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
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 render_table(self): html = DIV(**self.param.grid_class_style.get("grid-wrapper")) grid_header = DIV(**self.param.grid_class_style.get("grid-header")) # build the New button if needed if self.param.create and self.param.create != "": if isinstance(self.param.create, str): create_url = self.param.create else: create_url = self.endpoint + "/new" create_url += "?%s" % self.referrer grid_header.append( self.render_action_button( create_url, self.param.new_action_button_text, "fa-plus", icon_size="normal", override_classes=self.param.grid_class_style.classes.get( "grid-new-button", ""), override_styles=self.param.grid_class_style.get( "new_button"), )) # build the search form if provided if self.param.search_form: grid_header.append(self.render_search_form()) elif self.param.search_queries and len(self.param.search_queries) > 0: grid_header.append(self.render_default_form()) html.append(grid_header) table = TABLE(**self.param.grid_class_style.get("grid-table")) # build the header table.append(self.render_table_header()) # build the rows table.append(self.render_table_body()) # add the table to the html html.append( DIV(table, **self.param.grid_class_style.get("grid-table-wrapper"))) # add the row counter information footer = DIV(**self.param.grid_class_style.get("grid-footer")) row_count = DIV(**self.param.grid_class_style.get("grid-info")) row_count.append("Displaying rows %s thru %s of %s" % ( self.page_start + 1 if self.number_of_pages > 1 else 1, self.page_end if self.page_end < self.total_number_of_rows else self.total_number_of_rows, self.total_number_of_rows, )) if self.number_of_pages > 0 else row_count.append( "No rows to display") footer.append(row_count) # build the pager if self.number_of_pages > 1: footer.append(self.render_table_pager()) html.append(footer) return XML(html)