def systeminformation(request): git_status = "" try: git_status = ( subprocess. run( # nosec because we have no user input to subprocess ["git", "log", "-n", "5", "--oneline"], capture_output=True, check=True).stdout.decode()) except subprocess.SubprocessError as e: git_status = hg.BaseElement( "ERROR", hg.BR(), str(e), hg.BR(), getattr(e, "stdout", b"").decode(), hg.BR(), getattr(e, "stderr", b"").decode(), ) return hg.BaseElement( hg.H3(_("System information")), hg.H4("Git log"), hg.PRE(hg.CODE(git_status)), hg.H4("PIP packages", style="margin-top: 2rem"), hg.UL( hg.Iterator( sorted([ "%s==%s" % (i.key, i.version) for i in pkg_resources.working_set ]), "package", hg.LI(hg.C("package")), )), )
def error_layout( request, status_code: int, status_title: str, description: Union[str, hg.BaseElement], exception_detail: str = None, ): return hg.BaseElement( hg.H1(f"{status_code}: {status_title}", style="margin-bottom: 1rem;"), hg.P( description, style="margin-bottom: 1rem;", ), hg.If( bool(exception_detail), hg.BaseElement( hg.H4("Detail", style="margin-bottom: 1rem;"), hg.DIV( exception_detail, style=( "border: 1px solid grey;" "padding: 1rem;" "font-family: monospace;" "margin-bottom: 1rem;" ), ), ), ), Button.from_link( Link( label=_("Back to homepage"), href=hg.F(lambda c: c["request"].META["SCRIPT_NAME"] or "/"), ) ), )
def full(title, datatable, primary_button, helper_text=None): header = [hg.H4(title)] if helper_text: header.append( hg.P(helper_text, _class="bx--data-table-header__description")) return hg.DIV( hg.DIV(*header, _class="bx--data-table-header"), hg.SECTION( hg.DIV( hg.DIV(_class="bx--action-list"), hg.DIV( hg.P( hg.SPAN(0, data_items_selected=True), _(" items selected"), _class="bx--batch-summary__para", ), _class="bx--batch-summary", ), _class="bx--batch-actions", aria_label=_("Table Action Bar"), ), hg.DIV( hg.DIV(Search(), _class="bx--toolbar-search-container-expandable"), primary_button, _class="bx--toolbar-content", ), _class="bx--table-toolbar", ), datatable, _class="bx--data-table-container", data_table=True, )
def generalsettings(request): if request.method == "POST": form = global_preference_form_builder( preferences=["general__organizationname"] )(request.POST, request.FILES) if form.is_valid(): form.update_preferences() else: form = global_preference_form_builder( preferences=["general__organizationname"] )() return layout.grid.Grid( R(C(hg.H3(_("Settings")))), R(C(hg.H4(_("General")))), R(C(hg.H5(_("Information about our organization")))), R( C( layout.forms.Form( form, hg.BaseElement(F("general__organizationname")), layout.forms.helpers.Submit(), style="max-width: 480px", ) ) ), gutter=False, )
def get_layout(self): modelclass = self.object.model.model_class() if modelclass is None: return layout.notification.InlineNotification( "Error", f"Model '{self.object.model}' does no longer exist.", kind="error", ) column_helper = layout.get_attribute_description_modal(modelclass) F = layout.forms.FormField fieldstable = layout.forms.FormsetField.as_datatable( "columns", ["header", "column", "sortingname"], formsetfield_kwargs={ "extra": 1, "can_order": True, }, ) fieldstable[0][1].insert( 0, layout.button.Button(_("Help"), buttontype="ghost", **column_helper.openerattributes), ) fieldstable.append(hg.DIV(style="height: 1rem")) ret = hg.BaseElement( views.header(), layout.forms.Form( hg.C("form"), hg.DIV( _("Base model"), ": ", hg.C("object.model"), style="margin: 2rem 0 2rem 0", ), F("name"), F( "filter", inputelement_attrs={"rows": 1}, style="width: 100%", ), fieldstable, layout.tile.ExpandableTile( hg.H4(_("Extended settings")), hg.DIV( F("custom_queryset"), F("pagination"), ), ), layout.forms.helpers.Submit(style="margin-top: 1rem"), column_helper, ), hg.C("object.preview"), ) return ret
def tile_col_edit_modal_displayed_fields(heading, model: type, action: str, icon: Icon, displayed_fields: List): return tile_with_icon( icon, hg.BaseElement( hg.H4(heading), *displayed_fields, open_modal_popup_button(heading, model, action), ), )
def mailer_integration_tile(request): person = get_object_or_404(Person, pk=request.resolver_match.kwargs["pk"]) addresses = person.core_email_list.all() return tile_with_icon( Icon("email--new"), hg.H4(_("Email Subscriptions"), ), *[_display_subscription(email) for email in addresses] if hasattr(person, "core_email_list") and person.core_email_list.count() > 0 else C( _("Person has no email addresses")), )
def maintenancesettings(request): # Add the view's header ret = layout.grid.Grid(R(C(hg.H3(_("Maintenance")))), gutter=False) # Add the Package Information modal ret.append( R( C( hg.H4(_("Packages")), maintainance_package_layout(request), ), C( hg.H4(_("Optimize database")), maintenance_database_optimization(request), hg.H4(_("Rebuild search index"), _style="margin-top: 3rem;"), maintenance_search_reindex(request), ), )) return ret
def tags(): return tile_with_icon( Icon("tag--group"), hg.H4(_("Tags")), hg.Iterator(hg.F(lambda c: c["object"].tags.all()), "i", Tag(hg.C("i"))), open_modal_popup_button( _("Edit Tags"), hg.C("object").person_ptr, "ajax_edit_tags", ), )
def other(): return utils.tile_with_icon( Icon("add-comment"), hg.H4(_("Other")), hg.DIV(ObjectFieldLabel("remarks"), style="font-weight:bold; margin-bottom: 1rem;"), ObjectFieldValue("remarks"), utils.open_modal_popup_button( "Remarks", hg.C("object").person_ptr, "ajax_edit_remarks", ), )
def with_toolbar( self, title: Any, helper_text: Any = None, primary_button: Optional[Button] = None, bulkactions: Iterable[Link] = (), pagination_config: Optional[PaginationConfig] = None, checkbox_for_bulkaction_name: str = "_selected", search_urlparameter: Optional[str] = None, settingspanel: Any = None, ): """ wrap this datatable with title and toolbar title: table title helper_text: sub title primary_button: bread.layout.button.Button instance bulkactions: List of bread.utils.links.Link instances. Will send a post or a get (depending on its "method" attribute) to the target url the sent data will be a form with the selected checkboxes as fields if the head-checkbox has been selected only that field will be selected. """ checkboxallid = f"datatable-check-{hg.html_id(self)}" header: List[hg.BaseElement] = [ hg.H4(title, _class="bx--data-table-header__title") ] if helper_text is not None: header.append( hg.P(helper_text, _class="bx--data-table-header__description")) bulkactionlist = [] for link in bulkactions: bulkactionlist.append( Button( link.label, icon=link.iconname, onclick=hg.BaseElement( "submitbulkaction(this.closest('[data-table]'), '", link.href, "', method='GET')", ), ), ) if bulkactions: self.head.insert( 0, hg.TH( hg.INPUT( data_event="select-all", id=checkboxallid, _class="bx--checkbox", type="checkbox", name=checkbox_for_bulkaction_name, value="all", ), hg.LABEL( _for=checkboxallid, _class="bx--checkbox-label", ), _class="bx--table-column-checkbox", ), ) self.iterator[0].insert( 0, hg.TD( hg.INPUT( data_event="select", id=hg.BaseElement( checkboxallid, "-", hg.C(self.iterator.loopvariable + "_index"), ), _class="bx--checkbox", type="checkbox", name=checkbox_for_bulkaction_name, value=hg.If( hg.F(lambda c: hasattr( c[self.iterator.loopvariable], "pk")), hg.C(f"{self.iterator.loopvariable}.pk"), hg.C(f"{self.iterator.loopvariable}_index"), ), ), hg.LABEL( _for=hg.BaseElement( checkboxallid, "-", hg.C(self.iterator.loopvariable + "_index"), ), _class="bx--checkbox-label", aria_label="Label name", ), _class="bx--table-column-checkbox", ), ) return hg.DIV( hg.DIV(*header, _class="bx--data-table-header"), hg.SECTION( hg.DIV( hg.DIV( *(bulkactionlist + [ Button( _("Cancel"), data_event="action-bar-cancel", _class="bx--batch-summary__cancel", ) ]), _class="bx--action-list", ), hg.DIV( hg.P( hg.SPAN(0, data_items_selected=True), _(" items selected"), _class="bx--batch-summary__para", ), _class="bx--batch-summary", ), _class="bx--batch-actions", aria_label=_("Table Action Bar"), ), hg.DIV( searchbar(search_urlparameter) if search_urlparameter else None, Button( icon="settings--adjust", buttontype="ghost", onclick=""" let settings = this.parentElement.parentElement.parentElement.querySelector('.settingscontainer'); settings.style.display = settings.style.display == 'block' ? 'none' : 'block'; event.stopPropagation()""", ) if settingspanel else None, primary_button or None, _class="bx--toolbar-content", ), _class="bx--table-toolbar", ), hg.DIV( hg.DIV( hg.DIV( settingspanel, _class="bx--tile raised", style="margin: 0; padding: 0", ), _class="settingscontainer", style= "position: absolute; z-index: 999; right: 0; display: none", onload= "document.addEventListener('click', (e) => {this.style.display = 'none'})", ), style="position: relative", onclick="event.stopPropagation()", ), self, Pagination.from_config(pagination_config) if pagination_config else None, _class="bx--data-table-container", data_table=True, )
def get_layout(self): return hg.BaseElement( hg.H4(_("Manage Profile")), hg.DIV( layout.grid.Grid( R( C( hg.H4( layout.icon.Icon("user--profile"), style="text-align: center", ), width=1, ), C( hg.H4(_("Personal Information"), style="margin-bottom: 3rem"), profile_field("first_name"), profile_field("last_name"), R( C( hg.SPAN( _("Preferred Language"), style="font-weight: 700", ), width=4, ), C( hg.F(lambda c: get_language_info( c["request"].user.preferences.get( "general__preferred_language") or get_language())["name_translated"])), style="margin-bottom: 2rem", ), R( C( hg.SPAN( _("Timezone"), style="font-weight: 700", ), width=4, ), C( hg.F(lambda c: c["request"].user. preferences.get("general__timezone") or get_current_timezone())), style="margin-bottom: 2rem", ), layout.modal.modal_with_trigger( layout.modal.Modal.with_ajax_content( _("Personal Information"), reverse("userprofile.personal", query={"asajax": True}), submitlabel=_("Save"), ), layout.button.Button, _("Edit"), buttontype="tertiary", icon="edit", style="margin-top: 1rem", ), width=7, ), C( hg.H4( layout.icon.Icon("password"), style="text-align: center", ), width=1, ), C( hg.H4(_("Login"), style="margin-bottom: 3rem"), profile_field("username"), profile_field("email"), profile_field_password("password"), layout.modal.modal_with_trigger( layout.modal.Modal.with_ajax_content( _("Login"), reverse("userprofile.login", query={"asajax": True}), submitlabel=_("Save"), ), layout.button.Button, _("Edit"), buttontype="tertiary", icon="edit", style="margin-top: 1rem", ), width=7, ), style="margin-bottom: 2rem; margin-top: 2rem", ), R( C( hg.H4( layout.icon.Icon("virtual-column--key"), style="text-align: center", ), width=1, ), C( hg.H4(_("Permissions")), profile_field_checkbox("is_active"), profile_field_checkbox("is_superuser"), profile_field_checkbox("is_staff"), layout.modal.modal_with_trigger( layout.modal.Modal.with_ajax_content( _("Permissions"), reverse( "userprofile.permissions", query={"asajax": True}, ), submitlabel=_("Save"), ), layout.button.Button, _("Edit"), buttontype="tertiary", icon="edit", style="margin-top: 1rem", disabled=not self.request.user.is_superuser, ), width=7, ), C(width=1), C( hg.DIV( layout.toggle.Toggle( _("Developer Mode"), _("Disabled"), _("Enabled"), help_text=hg.SPAN( _( "Warning: This is a dangerous option!", ), hg.BR(), _( "Enable it only if you know what you are doing!", ), style="color: red", ), widgetattributes={ "checked": hg. C(f"request.session.{layout.DEVMODE_KEY}" ), }, onclick= f"fetch('{reverse('devmode', kwargs={'enable': not self.request.session.get(layout.DEVMODE_KEY, False)})}').then((resp) => {{}}).then(() => location.reload(true)); return false;", style="margin-bottom: 0", ), style="align-self: flex-end", ), width=7, style="display: flex; justify-content: flex-end", ), style="margin-bottom: 2rem; margin-top: 2rem", ), ), _class="bx--tile", ), )
def __init__( self, label: Any, body: Any, heading: Optional[Any] = None, link: Optional[Link] = None, button: Optional[Button] = None, icon: Union[Icon, str] = "information", menudirection: str = "bottom", **attributes, ): """ Parameters ---------- label : Any Label for a tooltip body : Any The content inside a tooltip heading : Any, optional A heading for a tooltip. link : Link, optional Bread's Link NamedTuple in case you want to bring users to a specific webpage. button : Button, optional Insert the Bread's Button onto the tooltip. icon : Icon, str, optional Specify an icon for the tooltip The default value is "information". menudirection : str Where should the tooltip appear besides the tooltip icon. It can be either top, left, right, or bottom. The default value is "bottom". """ base_class = "bx--tooltip" footer_elements: typing.List[hg.BaseElement] = [] if link: footer_elements.append(hg.A(link.label, href=link.href, _class="bx--link")) if button: footer_elements.append(button) base_id = hg.html_id(self, base_class + "-id") label_id = f"{base_id}-label" trigger_attributes = { "_class": base_class + "__trigger", "aria_controls": base_id, "aria_expanded": "false", "aria_haspopup": "true", "aria_labelledby": label_id, "data_tooltip_target": "#" + base_id, "data_tooltip_trigger": True, } tooltip_attributes = { "_class": base_class, "aria_hidden": "true", "data_floating_menu_direction": menudirection, "id": base_id, } tooltip_content_attributes = { "_class": base_class + "__content", "aria_describedby": base_id + "-body", "aria_labelledby": label_id, "role": "dialog", "tabindex": "-1", } tooltip_attributes = hg.merge_html_attrs(tooltip_attributes, attributes) icon = _get_icon(icon) super().__init__( # tooltip label hg.DIV( label, # cannot use bread's Button class because # only the class bx--tooltip__trigger can be used hg.DIV( icon, **trigger_attributes, ), id=label_id, _class=base_class + "__label", ), # the real tooltip goes here hg.DIV( hg.SPAN(_class=base_class + "__caret"), hg.DIV( hg.If( bool(heading), hg.H4( heading, id=base_id + "-heading", _class=base_class + "__heading", ), ), hg.P(body, id=base_id + "-body"), hg.If( len(footer_elements) > 0, hg.DIV( *footer_elements, _class=base_class + "__footer", ), ), **tooltip_content_attributes, ), hg.SPAN(tabindex="0"), **tooltip_attributes, ), )
def componentpreview(request): class ConfigForm(forms.Form): with_label = forms.BooleanField(required=False) with_helptext = forms.BooleanField(required=False) with_errors = forms.BooleanField(required=False) disabled = forms.BooleanField(required=False) CHOICES = ( ("choice1", "Choice 1"), ("choice2", "Choice 2"), ("choice3", "Choice 3"), ("choice4", "Choice 4"), ) widgets = { forms.TextInput: (forms.CharField, { "widget": forms.TextInput }), forms.NumberInput: (forms.DecimalField, { "widget": forms.NumberInput }), forms.EmailInput: (forms.EmailField, { "widget": forms.EmailInput }), forms.URLInput: (forms.URLField, { "widget": forms.URLInput }), forms.PasswordInput: (forms.CharField, { "widget": forms.PasswordInput }), forms.HiddenInput: (forms.CharField, { "widget": forms.HiddenInput }), forms.DateInput: (forms.DateField, { "widget": forms.DateInput }), forms.DateTimeInput: (forms.DateTimeField, { "widget": forms.DateTimeInput }), forms.TimeInput: (forms.TimeField, { "widget": forms.TimeInput }), forms.Textarea: (forms.CharField, { "widget": forms.Textarea }), forms.CheckboxInput: (forms.BooleanField, { "widget": forms.CheckboxInput }), forms.Select: (forms.ChoiceField, { "widget": forms.Select, "choices": CHOICES }), forms.NullBooleanSelect: ( forms.NullBooleanField, { "widget": forms.NullBooleanSelect }, ), forms.SelectMultiple: ( forms.MultipleChoiceField, { "widget": forms.SelectMultiple, "choices": CHOICES }, ), forms.RadioSelect: ( forms.ChoiceField, { "widget": forms.RadioSelect, "choices": CHOICES }, ), forms.CheckboxSelectMultiple: ( forms.ChoiceField, { "widget": forms.CheckboxSelectMultiple, "choices": CHOICES }, ), forms.FileInput: (forms.FileField, { "widget": forms.FileInput }), forms.ClearableFileInput: ( forms.FileField, { "widget": forms.ClearableFileInput }, ), } HELPTEXT = "This is a piece of helptext, maximized for helpfulness" ERRORS = [ "This is an example of an error", "This is a second errors, but actually none of them are real errors, so do not worry", ] def nicefieldname(cls): return re.sub(r"(?<!^)(?=[A-Z])", "_", cls.__name__) configform = ConfigForm(request.GET) if not configform.is_valid() or not request.GET: config = configform.initial config = configform.cleaned_data Form = type( "Form", (forms.Form, ), { nicefieldname(widget): field[0]( **field[1], **({ "help_text": HELPTEXT } if config["with_helptext"] else {}), disabled=config["disabled"]) for widget, field in widgets.items() }, ) return hg.BaseElement( hg.STYLE( hg.mark_safe(""" #backtotopBtn { position: fixed; right: 0; bottom: 0; z-index: 999; margin-right: 3rem; margin-bottom: 3rem; border-radius: 50%; } """)), layout.button.Button.from_link( Link(href="#", label=_("Back to top")), buttontype="secondary", icon="arrow--up", notext=True, id="backtotopBtn", ), tabs.Tabs( tabs.Tab( _("Layout"), layout.componentpreview.layout(request), ), tabs.Tab( _("Informational"), layout.componentpreview.informational(request), ), tabs.Tab( _("Interactive"), layout.componentpreview.interactive(request), ), tabs.Tab( _("Datatable"), layout.componentpreview.datatable_layout(request), ), tabs.Tab( _("Form"), hg.BaseElement( hg.H3(_("Widget preview")), layout.grid.Grid( layout.grid.Row( layout.grid.Col( hg.H4(_("Widgets")), layout.forms.Form( Form(), *[ F( nicefieldname(w), no_label=not config["with_label"], errors=ERRORS if config["with_errors"] else None, ) for w in widgets.keys() ]), ), layout.grid.Col( hg.H4(_("Configure preview")), layout.forms.Form( configform, F("with_label"), F("with_helptext"), F("with_errors"), F("disabled"), layout.forms.helpers.Submit(_("Apply")), method="GET", ), ), ), R( C( hg.H3(_("Tooltips")), hg.H4(_("Definition tooltip")), hg.DIV( layout.components.tooltip. DefinitionTooltip( "Definition tooltip (left aligned)", "Brief definition of the dotted, underlined word above.", align="left", )), hg.DIV( layout.components.tooltip. DefinitionTooltip( "Definition tooltip (center aligned)", "Brief definition of the dotted, underlined word above.", align="center", )), hg.DIV( layout.components.tooltip. DefinitionTooltip( "Definition tooltip (right aligned)", "Brief definition of the dotted, underlined word above.", align="right", )), hg.H4(_("Icon tooltip")), hg.DIV( layout.components.tooltip.IconTooltip( "Help", ), layout.components.tooltip.IconTooltip( "Filter", icon=Icon("filter"), ), layout.components.tooltip.IconTooltip( "Email", icon="email", ), ), hg.H4(_("Interactive tooltip")), hg.DIV( layout.components.tooltip. InteractiveTooltip( label="Tooltip label", body=(_( "This is some tooltip text. This box shows the maximum amount of text that should " "appear inside. If more room is needed please use a modal instead." )), heading="Heading within a Tooltip", button=(layout.components.button. Button("Button")), link=Link(href="#", label="link"), ), ), ), ), ), ), ), ), ) return hg.BaseElement()
def postals(): return tile_with_icon( Icon("map"), hg.H4(_("Address(es)")), R( C( hg.Iterator( hg.F(lambda c: getattr(c["object"], "core_postal_list"). order_by( django.db.models.F("valid_until").desc( nulls_first=True)).all() if hasattr(c["object"], "core_postal_list") else []), "i", display_postal(), ))), R( C( layout.button.Button( _("Hide inactive postals"), onclick="hideInactivePostals();", id="hideInactivePostalsButton", style="display: none;", icon="view--off", buttontype="ghost", ), layout.button.Button( _("Show inactive postals"), onclick="showInactivePostals();", id="showInactivePostalsButton", icon="view", buttontype="ghost", ), ), style="margin-top: 1.5rem;", ), R( C( modal_with_trigger( modal_add_postal(), layout.button.Button, _("Add"), buttontype="ghost", icon="add", ), ), ), hg.SCRIPT( mark_safe(""" function hideInactivePostals() { for(i of $$('.inactive_postal')) { $(i)._.style({display: "none"}); } $$('#hideInactivePostalsButton')._.style({display: "none"}) $$('#showInactivePostalsButton')._.style({display: "block"}) } function showInactivePostals() { for(i of $$('.inactive_postal')) { $(i)._.style({display: "block"}); } $$('#hideInactivePostalsButton')._.style({display: "block"}) $$('#showInactivePostalsButton')._.style({display: "none"}) } """)), )
import htmlgenerator as hg from django.urls import path from bread import layout from bread.utils import quickregister from bread.views import AddView, EditView from .models import DataChangeTrigger, DateFieldTrigger, SendEmail urlpatterns: typing.List[path] = [] quickregister( urlpatterns, DataChangeTrigger, editview=EditView._with(fields=[ hg.H4(layout.ObjectFieldLabel("model"), ": ", layout.ObjectFieldValue("model")), "action", "type", "filter", "enable", ]), addview=AddView._with(fields=["model", "type", "action"]), ) quickregister( urlpatterns, DateFieldTrigger, editview=EditView._with(fields=[ hg.H4(layout.ObjectFieldLabel("model"), ": ", layout.ObjectFieldValue("model")), "action", "offset_type",
def generate_wizard_form(wizardview, wizardtitle, steptitle, formlayout): """ title: Title of the current page steps: list of 2-tuples with (step_title, status) where status must be one of ["incomplete", "complete", "current"] """ # needs to be rendered in view of type NamedUrlSessionWizardView in order to work correctly def go_back_url(context): url = reverse( context["request"].resolver_match.view_name, kwargs={"step": context["wizard"]["steps"].prev}, ) return f"document.location='{url}'" steps = [] for i, (step, formclass) in enumerate(wizardview.get_form_list().items()): status = "incomplete" if i < wizardview.steps.index: status = "complete" if step == wizardview.steps.current: status = "current" steps.append((formclass.title, status)) return hg.BaseElement( hg.H3(wizardtitle), hg.H4(steptitle), layout.progress_indicator.ProgressIndicator( steps, style="margin-bottom: 2rem", ), layout.forms.Form( hg.C("wizard.form"), layout.forms.Form( hg.C("wizard.management_form"), layout.forms.FormField("current_step", form="wizard.management_form"), standalone=False, ), formlayout, hg.DIV( hg.DIV( hg.If( hg.C("wizard.steps.prev"), layout.button.Button( _("Back"), buttontype="secondary", onclick=hg.F(go_back_url), ), ), hg.If( hg.F(lambda c: c["wizard"]["steps"].last == c["wizard"] ["steps"].current), layout.button.Button(_("Complete"), type="submit", style="margin-left: 1rem"), layout.button.Button(_("Continue"), type="submit", style="margin-left: 1rem"), ), ), style="align-items: flex-end", _class="bx--form-item", ), ), )