def __init__( self, session, db, define_tables=True, sender=None, use_username=True, use_phone_number=False, registration_requires_confirmation=True, registration_requires_approval=False, inject=True, extra_fields=[], login_expiration_time=3600, # seconds password_complexity={"entropy": 50}, block_previous_password_num=None, allowed_actions=["all"], use_appname_in_redirects=None, ): self.param = Param( registration_requires_confirmation=registration_requires_confirmation, registration_requires_approval=registration_requires_approval, login_after_registration=False, login_expiration_time=login_expiration_time, # seconds password_complexity=password_complexity, block_previous_password_num=block_previous_password_num, allowed_actions=allowed_actions, use_appname_in_redirects=use_appname_in_redirects, formstyle=FormStyleDefault, messages=copy.deepcopy(self.MESSAGES), button_classes=copy.deepcopy(self.BUTTON_CLASSES), ) """Creates and Auth object responsible for handling authentication and authorization""" self.__prerequisites__ = [] self.inject = inject if session: self.__prerequisites__.append(session) if db: self.__prerequisites__.append(db) self.onsuccess = {} self.next = {} self.db = db self.session = session self.sender = sender self.route = None self.use_username = use_username # if False, uses email only self.use_phone_number = use_phone_number # The self._link variable is not thread safe (only intended for testing) self.extra_auth_user_fields = extra_fields self._link = None self.extra_auth_user_fields = extra_fields if db and define_tables: self.define_tables() self.plugins = {} self.form_source = DefaultAuthForms(self) self.flash = Flash()
def __init__(self, table, record=None, readonly=False, deletable=True, noncreate=True, formstyle=FormStyleDefault, dbio=True, keep_values=False, form_name=None, hidden=None, validation=None, csrf_session=None, csrf_protection=True, lifespan=None, signing_info=None, submit_value="Submit", show_id=True, **kwargs): self.param = Param( formstyle=formstyle, hidden=hidden, submit_value=submit_value, sidecar=[], ) if isinstance(table, list): dbio = False # Mimic a table from a list of fields without calling define_table form_name = form_name or "none" for field in table: field.tablename = getattr(field, "tablename", form_name) if isinstance(record, (int, str)): record_id = int(str(record)) self.record = table[record_id] if not self.record: raise HTTP(404) else: self.record = record # computed from input and not changed self.table = table self.deletable = self.record and deletable and not readonly self.dbio = dbio self.keep_values = True if keep_values or self.record else False self.form_name = form_name or table._tablename self.csrf_session = csrf_session self.signing_info = signing_info self.validation = validation self.lifespan = lifespan self.csrf_protection = csrf_protection self.show_id = show_id # initialized and can change self.vars = {} self.errors = {} self.readonly = readonly self.noncreate = noncreate self.submitted = False self.deleted = False self.accepted = False self.formkey = None self.cached_helper = None self.action = None self.kwargs = kwargs if kwargs else {} if self.record: self.vars = self._read_vars_from_record(table) if not readonly and request.method != "GET": post_vars = request.POST form_vars = copy.deepcopy(request.forms) for k in form_vars: self.vars[k] = form_vars[k] self.submitted = True process = False # We only a process a form if it is POST and the formkey matches (correct formname and crsf) # Notice: we never expose the crsf uuid, we only use to sign the form uuid if request.method == "POST": if not self.csrf_protection or self._verify_form(post_vars): process = True if process: record_id = self.record and self.record.get("id") if not post_vars.get("_delete"): validated_vars = {} uploaded_files = [] for field in self.table: if field.writable and field.type != "id": original_value = post_vars.get(field.name) if isinstance(original_value, list): if len(original_value) == 1: original_value = original_value[0] elif len(original_value) == 0: original_value = None if field.type.startswith("list:") and isinstance( original_value, str): try: original_value = json.loads(original_value or "[]") except json.decoder.JSONDecodeError: # this happens if posting a single value pass (value, error) = field.validate(original_value, record_id) if field.type == "password" and record_id and value is None: continue if field.type == "upload": value = request.files.get(field.name) print(str(value)[:100]) delete = post_vars.get("_delete_" + field.name) if value is not None: if field.uploadfolder: uploaded_files.append( tuple((field, value))) validated_vars[field.name] = value elif self.record: if not delete: validated_vars[ field.name] = self.record.get( field.name) else: validated_vars[ field.name] = value = None elif field.type == "boolean": validated_vars[field.name] = value is not None else: validated_vars[field.name] = value if error: self.errors[field.name] = error self.vars.update(validated_vars) if validation: validation(self) if self.record and dbio: self.vars["id"] = self.record.id if not self.errors: for file in uploaded_files: field, value = file value = field.store(value.file, value.filename, field.uploadfolder) if value is not None: validated_vars[field.name] = value self.accepted = True self.vars.update(validated_vars) if dbio: self.update_or_insert(validated_vars) elif dbio: self.deleted = True self.record.delete_record() elif self.record: # This form should not be processed. We return the same as for GET. self.vars = self._read_vars_from_record(table) if self.csrf_protection: self._sign_form()
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 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 __init__( self, table, record=None, readonly=False, deletable=True, formstyle=FormStyleDefault, dbio=True, keep_values=False, form_name=None, hidden=None, validation=None, csrf_session=None, lifespan=None, signing_info=None, submit_value="Submit", ): self.param = Param( formstyle=formstyle, hidden=hidden, submit_value=submit_value, sidecar=[], ) if isinstance(table, list): dbio = False # Mimic a table from a list of fields without calling define_table form_name = form_name or "none" for field in table: field.tablename = getattr(field, "tablename", form_name) if isinstance(record, (int, str)): record_id = int(str(record)) self.record = table[record_id] else: self.record = record # computed from input and not changed self.table = table self.deletable = deletable and not readonly and self.record self.dbio = dbio self.keep_values = True if keep_values or self.record else False self.form_name = form_name or table._tablename self.csrf_session = csrf_session self.signing_info = signing_info self.validation = validation self.lifespan = lifespan # initialized and can change self.vars = {} self.errors = {} self.readonly = readonly self.submitted = False self.deleted = False self.accepted = False self.formkey = None self.cached_helper = None self.action = None if readonly or request.method == "GET": if self.record: self.vars = self._read_vars_from_record(table) else: post_vars = request.forms self.vars = copy.deepcopy(request.forms) self.submitted = True process = False # We only a process a form if it is POST and the formkey matches (correct formname and crsf) # Notice: we never expose the crsf uuid, we only use to sign the form uuid if request.method == "POST": if self._verify_form(post_vars): process = True if process: record_id = self.record and self.record.get("id") if not post_vars.get("_delete"): validated_vars = {} for field in self.table: if field.writable and field.readable and field.type != "id": original_value = post_vars.getall(field.name) if ( isinstance(original_value, list) and len(original_value) == 1 ): original_value = original_value[0] if field.type.startswith("list:") and isinstance(original_value, str): original_value = json.loads(original_value or "[]") (value, error) = field.validate(original_value, record_id) if field.type == "password" and record_id and value is None: continue if field.type == "upload": value = request.files.get(field.name) delete = post_vars.get("_delete_" + field.name) if value is not None and hasattr(value, "file"): value = field.store( value.file, value.filename, field.uploadfolder ) elif self.record and not delete: value = self.record.get(field.name) else: value = None validated_vars[field.name] = value if error: self.errors[field.name] = error self.vars.update(validated_vars) if validation: validation(self) if self.record and dbio: self.vars["id"] = self.record.id if not self.errors: self.accepted = True if dbio: self.update_or_insert(validated_vars) elif dbio: self.deleted = True self.record.delete_record() elif self.record: # This form should not be processed. We return the same as for GET. self.vars = self._read_vars_from_record(table) self._sign_form()
def __init__( self, path, query, search_form=None, search_queries=None, fields=None, show_id=False, orderby=None, left=None, headings=None, create=True, details=True, editable=True, deletable=True, pre_action_buttons=None, post_action_buttons=None, auto_process=True, rows_per_page=15, include_action_button_text=True, search_button_text="Filter", formstyle=FormStyleDefault, grid_class_style=GridClassStyle, ): """ Grid is a searchable/sortable/pageable grid :param path: The part of the URL to be parsed by this grid :param query: the query used to filter the data :param search_form: py4web FORM to be included as the search form :param search_queries: future use - pass a dict of name and a search query :param fields: list of fields to display on the list page, if blank, glean tablename from first query : and use all fields of that table :param show_id: show the record id field on list page - default = False :param orderby: pydal orderby field or list of fields :param left: if joining other tables, specify the pydal left expression here :param headings: list of headings to be used for list page - if not provided use the field label :param create: URL to redirect to for creating records - set to False to not display the button :param editable: URL to redirect to for editing records - set to False to not display the button :param deletable: URL to redirect to for deleting records - set to False to not display the button :param pre_action_buttons: list of action_button instances to include before the standard action buttons :param post_action_buttons: list of action_button instances to include after the standard action buttons :param auto_process: True/False - automatically process the sql for the form - if False, user is responsible for calling process(). """ # in case the query is a Table instead if isinstance(query, query._db.Table): query = query._id != None self.path = path or "" self.db = query._db self.param = Param( query=query, fields=fields, show_id=show_id, orderby=orderby, left=left, search_form=search_form, search_queries=search_queries, headings=headings or [], create=create, details=details, editable=editable, deletable=deletable, pre_action_buttons=pre_action_buttons, post_action_buttons=post_action_buttons, rows_per_page=rows_per_page, include_action_button_text=include_action_button_text, search_button_text=search_button_text, formstyle=formstyle, grid_class_style=grid_class_style, new_sidecar=None, new_submit_value=None, new_action_button_text="New", details_sidecar=None, details_submit_value=None, details_action_button_text="Details", edit_sidecar=None, edit_submit_value=None, edit_action_button_text="Edit", delete_action_button_text="Delete", htmx_target=None, ) # instance variables that will be computed self.action = None self.current_page_number = None self.endpoint = request.fullpath if self.path: self.endpoint = self.endpoint[:-len(self.path)].rstrip("/") self.hidden_fields = None self.form = None self.number_of_pages = None self.page_end = None self.page_start = None self.query_parms = request.params self.readonly_fields = None self.record_id = None self.rows = None self.tablename = None self.total_number_of_rows = None self.use_tablename = self.is_join() self.formatters = {} self.formatters_by_type = copy.copy(Grid.FORMATTERS_BY_TYPE) if auto_process: self.process()
def produce( self, table, vars, errors, readonly, noncreate, deletable, classes=None, class_inner_exceptions=None, kwargs=None, ): self.classes.update(classes or {}) self.class_inner_exceptions.update(class_inner_exceptions or {}) kwargs = kwargs if kwargs else {} form_method = "POST" form_action = request.url form_enctype = "multipart/form-data" form = FORM( _method=form_method, _action=form_action, _enctype=form_enctype, **kwargs ) 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>"), ) 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"] for field in table: # 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) 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 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. 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": if field.type == "boolean": field_value = value field_type = "checkbox" control = INPUT( _type=field_type, _id=input_id, _name=field_name, _value="ON", _disabled="", _checked=value, _title=title, ) 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 widget for the field use it elif field.widget: control = field.widget(table, value) # Grab the custom widget attributes. field_attributes = control.attributes field_type = "widget" field_value = value # else pick the proper default widget elif field.type == "text": field_value = value or "" field_type = "text" control = TEXTAREA( field_value, _id=input_id, _name=field_name, _placeholder=placeholder, _title=title, ) elif field.type == "date": field_value = value field_type = "date" control = INPUT( _value=field_value, _type="date", _id=input_id, _name=field_name, _placeholder=placeholder, _title=title, ) elif field.type == "datetime": helpervalue = str(value) helpervalue = helpervalue.replace(" ", "T") field_value = helpervalue field_type = "datetime-local" control = INPUT( _value=helpervalue, _type="datetime-local", _id=input_id, _name=field_name, _placeholder=placeholder, _title=title, ) elif field.type == "time": field_value = value field_type = "time" control = INPUT( _value=value, _type="time", _id=input_id, _name=field_name, _placeholder=placeholder, _title=title, ) elif field.type == "boolean": field_value = value field_type = "checkbox" field_attributes["_value"] = "ON" control = INPUT( _type="checkbox", _id=input_id, _name=field_name, _value=field_attributes["_value"], _checked=value, _title=title, ) elif field.type == "upload": control = DIV() if value and not error: download_div = DIV() download_div.append( LABEL( "Currently: ", ) ) if getattr(field, "download_url", None): url = field.download_url(value) else: url = "#" download_div.append(A(" download ", _href=url)) # 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 ) delete_checkbox_name = "_delete_" + field_name download_div.append( INPUT( _type="checkbox", _value="ON", _name=delete_checkbox_name, _title=title, ) ) download_div.append(" (check to remove)") delete_field_attributes = dict() delete_field_attributes["_label"] = "Remove" delete_field_attributes["_value"] = "ON" delete_field_attributes["_type"] = "checkbox" delete_field_attributes["_name"] = delete_checkbox_name json_controls["form_fields"] += [delete_field_attributes] json_controls["form_values"][delete_checkbox_name] = None control.append(download_div) control.append(LABEL("Change: ")) control.append(INPUT(_type="file", _id=input_id, _name=field_name)) field_value = None field_type = "file" 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])) field_options = [ [k, v, (not k is None and k in value)] for k, v in get_options(field.requires) ] option_tags = [ OPTION(v, _value=k, _selected=_selected) for (k, v, _selected) in field_options ] control = SELECT( *option_tags, _id=input_id, _name=field_name, _multiple=multiple, _title=title ) field_value = value field_type = "options" field_attributes["_multiple"] = multiple field_attributes["_options"] = field_options else: field_type = "password" if field.type == "password" else "text" if field.type.startswith("list:"): value = json.dumps(value or []) field_value = None if field.type == "password" else value field_autocomplete = "off" if field_type == "password" else "on" control = INPUT( _type=field_type, _id=input_id, _name=field_name, _value=field_value, _class=field_class, _placeholder=placeholder, _title=title, _autocomplete=field_autocomplete, ) field_attributes["_autocomplete"] = field_autocomplete 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() # 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"] = input_id 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": 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=self.class_inner_exceptions.get( control.name, 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: 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)