def sync_help_modal(): return bread.layout.modal.Modal( _("Help"), _("The button below is currently the only way of getting new subcribers from the mailer into our system. Is it also the only way of getting updates for subscribers that we already have in our system. This is what happens when the button is pressed:" ), hg.DIV( hg.UL( hg.LI( _("For all the Subscriptions that are in the relevant segment in the Mailer, we check whether the email address is already in BasxConnect." ), _class="bx--list__item", ), hg.LI( _("If an email address is already in BasxConnect, the downloaded subscription will be attached to the email address and override the current values in case there are any." ), _class="bx--list__item", ), hg.LI( _("If an email address is not yet in BasxConnect, a new person will be created with that email address." ), _class="bx--list__item", ), _class="bx--list--unordered", ), style="padding-left: 1rem;", ), width=8, )
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 __init__( self, links, menuiconname="overflow-menu--vertical", menuname=None, direction="bottom", flip=False, item_attributes={}, **attributes, ): attributes["data-overflow-menu"] = True attributes["_class"] = attributes.get("_class", "") + " bx--overflow-menu" item_attributes["_class"] = (item_attributes.get("_class", "") + " bx--overflow-menu-options__option") menuid = hg.F(lambda c: OverflowMenu.MENUID_TEMPLATE % hg.html_id( c.get("row", self))) triggerid = hg.F(lambda c: (OverflowMenu.MENUID_TEMPLATE % hg.html_id( c.get("row", self))) + "-trigger") super().__init__( hg.BUTTON( Icon(menuiconname, size=16), _class="bx--overflow-menu__trigger" + (" bx--tooltip__trigger bx--tooltip--a11y bx--tooltip--right bx--tooltip--align-start" if menuname is not None else ""), aria_haspopup="true", aria_expanded="false", aria_controls=menuid, type="button", id=triggerid, ), hg.DIV( hg.UL( hg.Iterator( links, "link", hg.LI( hg.F(asoverflowbutton), **item_attributes, ), ), _class="bx--overflow-menu-options__content", ), _class="bx--overflow-menu-options" + (" bx--overflow-menu--flip" if flip else ""), tabindex="-1", role="menu", aria_labelledby=triggerid, data_floating_menu_direction=direction, id=menuid, ), **attributes, ) if menuname is not None: self[0].insert(0, hg.SPAN(menuname, _class="bx--assistive-text"))
def __init__(self, errors): super().__init__( errors, hg.DIV( hg.UL(hg.Iterator(errors or (), "error", hg.LI(hg.C("error")))), _class="bx--form-requirement", ), )
def _display_results(query_set, highlight, onclick): if query_set.count() == 0: return _("No results") def _display_as_list_item(person): if person is None: # this happens if we have entries in the search-backend which have been deleted return hg.BaseElement() return hg.LI( hg.SPAN( mark_safe(highlight.highlight(person.personnumber)), style="width: 48px; display: inline-block", _class=ITEM_VALUE_CLASS, ), hg.SPAN( person.name, _class=ITEM_LABEL_CLASS, style="dispay:none;", ), " ", mark_safe(highlight.highlight(person.search_index_snippet())), style="cursor: pointer; padding: 8px 0;", onclick=onclick(person), onmouseenter="this.style.backgroundColor = 'lightgray'", onmouseleave="this.style.backgroundColor = 'initial'", _class=ITEM_CLASS, ) result_list = [ _display_as_list_item(search_result.object) for search_result in query_set[:25] if search_result and search_result.object ] return hg.UL( hg.LI(_("%s items found") % len(query_set), style="margin-bottom: 20px"), *result_list, )
def _display_as_list_item(person): if person is None: # this happens if we have entries in the search-backend which have been deleted return hg.BaseElement() return hg.LI( hg.SPAN( mark_safe(highlight.highlight(person.personnumber)), style="width: 48px; display: inline-block", _class=ITEM_VALUE_CLASS, ), hg.SPAN( person.name, _class=ITEM_LABEL_CLASS, style="dispay:none;", ), " ", mark_safe(highlight.highlight(person.search_index_snippet())), style="cursor: pointer; padding: 8px 0;", onclick=onclick(person), onmouseenter="this.style.backgroundColor = 'lightgray'", onmouseleave="this.style.backgroundColor = 'initial'", _class=ITEM_CLASS, )
class PersonBrowseView(BrowseView): columns = [ DataTableColumn( layout.ObjectFieldLabel("personnumber", models.Person), hg.DIV( hg.C("row.personnumber"), style=hg.If(hg.C("row.deleted"), "text-decoration:line-through"), ), "personnumber__int", ), DataTableColumn( hg.DIV( layout.tooltip.IconTooltip( hg.UL( hg.LI(hg.SPAN("●", style="color: green"), " ", _("Active")), hg.LI(hg.SPAN("●", style="color: red"), " ", _("Inactive")), ), position="top", )), hg.DIV( "●", style=hg.format("color: {}", hg.If(hg.C("row.active"), "green", "red")), ), "active", ), DataTableColumn(layout.ObjectFieldLabel("_type", models.Person), hg.C("row._type"), "_type"), DataTableColumn( _("Name"), hg.DIV( hg.If( hg.F(lambda context: type(context["row"]) == models. NaturalPerson), hg.C("row.last_name"), hg.C("row.name"), ), style=hg.If(hg.C("row.deleted"), "text-decoration:line-through"), ), "default_sorting_name", ), DataTableColumn( layout.ObjectFieldLabel("first_name", models.NaturalPerson), hg.DIV( hg.If( hg.F(lambda context: type(context["row"]) == models. NaturalPerson), hg.C("row.first_name"), "", ), style=hg.If(hg.C("row.deleted"), "text-decoration:line-through"), ), "naturalperson__first_name", ), "primary_postal_address.address", "primary_postal_address.postcode", "primary_postal_address.city", "primary_postal_address.country", DataTableColumn( _("Email"), hg.C("row.primary_email_address.asbutton", ), "primary_email_address__email", False, ), DataTableColumn( layout.ObjectFieldLabel("tags", models.Person), hg.UL( hg.Iterator(hg.C("row.tags.all"), "tag", layout.tag.Tag(hg.C("tag")))), ), ] bulkactions = ( BulkAction( "add-tag", label=_("Add tag"), iconname="add", action=bulkaddtag, ), BulkAction( "remove-tag", label=_("Remove tag"), iconname="subtract", action=bulkremovetag, ), BulkAction( "delete", label=_("Delete"), iconname="trash-can", action=bulkdelete, ), BulkAction( "restore", label=_("Restore"), iconname="restart", action=bulkrestore, ), BulkAction( "excel", label=_("Excel"), iconname="download", action=export, ), ) search_backend = layout.search.SearchBackendConfig(url=reverse( "basxconnect.core.views.person.search_person_view.searchperson")) rowclickaction = BrowseView.gen_rowclickaction("read") viewstate_sessionkey = "personbrowseview" class FilterForm(forms.Form): naturalperson = forms.BooleanField(required=False, label=_("Natural Person")) legalperson = forms.BooleanField(required=False, label=_("Legal Person")) personassociation = forms.BooleanField(required=False, label=_("Person Association")) naturalperson_subtypes = forms.ModelMultipleChoiceField( queryset=models.Term.objects.filter( vocabulary__slug="naturaltype"), widget=forms.CheckboxSelectMultiple, required=False, label="", ) legalperson_subtypes = forms.ModelMultipleChoiceField( queryset=models.Term.objects.filter(vocabulary__slug="legaltype"), widget=forms.CheckboxSelectMultiple, required=False, label="", ) personassociation_subtypes = forms.ModelMultipleChoiceField( queryset=models.Term.objects.filter( vocabulary__slug="associationtype"), widget=forms.CheckboxSelectMultiple, required=False, label="", ) tags = forms.ModelMultipleChoiceField( queryset=models.Term.objects.filter(vocabulary__slug="tag"), widget=forms.CheckboxSelectMultiple, required=False, ) preferred_language = forms.MultipleChoiceField( choices=settings.PREFERRED_LANGUAGES, widget=forms.CheckboxSelectMultiple, required=False, ) status = forms.MultipleChoiceField( choices=[("active", _("Active")), ("inactive", _("Inactive"))], widget=forms.CheckboxSelectMultiple, required=False, ) trash = forms.BooleanField(required=False, label=_("Trash")) def get_layout(self): self.checkboxcounterid = hg.html_id(self, "checkbox-counter") ret = super().get_layout() toolbar = list( ret.filter(lambda e, a: getattr(e, "attributes", {}).get( "_class", "") == "bx--toolbar-content"))[0] nfilters = self._checkbox_count() toolbar.insert( -2, hg.DIV( hg.SPAN(nfilters, id=self.checkboxcounterid), layout.icon.Icon( "close", focusable="false", size=15, role="img", onclick= f"document.location = '{self.request.path}?reset=1'", ), role="button", _class= "bx--list-box__selection bx--list-box__selection--multi bx--tag--filter", style="margin: auto 0.5rem;" + (" display: none;" if nfilters == 0 else ""), tabindex="0", title=("Reset"), ), ) return ret def _filterform(self): return self.FilterForm({"status": ["active"], **self.request.GET}) def _checkbox_count(self): counter = 0 form = self._filterform() if form.is_valid(): counter += 1 if form.cleaned_data["naturalperson"] else 0 counter += 1 if form.cleaned_data["legalperson"] else 0 counter += 1 if form.cleaned_data["personassociation"] else 0 counter += form.cleaned_data["naturalperson_subtypes"].count() counter += form.cleaned_data["legalperson_subtypes"].count() counter += form.cleaned_data["personassociation_subtypes"].count() counter += form.cleaned_data["tags"].count() counter += len(form.cleaned_data["preferred_language"]) counter += ( 1 if "inactive" in form.cleaned_data["status"] else 0 ) # don't count the "active" checkbox, it is a permanent default counter += 1 if form.cleaned_data["trash"] else 0 return counter def get_queryset(self): form = self._filterform() qs = super().get_queryset() if form.is_valid(): ret = ((qs.filter( deleted=form.cleaned_data.get("trash", False))).select_related( "primary_email_address", "primary_postal_address", "_type").prefetch_related("tags")) if any([ form.cleaned_data[i] for i in ( "naturalperson", "legalperson", "personassociation", "naturalperson_subtypes", "legalperson_subtypes", "personassociation_subtypes", ) ]): q = Q() for i in ("naturalperson", "legalperson", "personassociation"): # setup some logic descriptors maintype_selected = bool(form.cleaned_data[i]) subtype_selected = bool(form.cleaned_data[f"{i}_subtypes"]) all_subtypes_selected = bool( form.cleaned_data[f"{i}_subtypes"].count() == form.fields[f"{i}_subtypes"].queryset.count()) # the semantics for this filter are not 100% clear # there are also cases where a subtype has the wrong maintype # This code tries to make the selection consistent to what a user # would expect, but these expectations can still vary... if maintype_selected: typeq = Q(_maintype=i) if subtype_selected: if not all_subtypes_selected: typeq &= Q(_type__in=form. cleaned_data[f"{i}_subtypes"]) else: typeq &= ~Q(_type__in=form.fields[f"{i}_subtypes"]. queryset) q |= typeq else: q |= Q(_type__in=form.cleaned_data[f"{i}_subtypes"]) ret = ret.filter(q) if form.cleaned_data.get("tags"): ret = ret.filter(tags__in=form.cleaned_data["tags"]) if form.cleaned_data.get("preferred_language"): ret = ret.filter(preferred_language__in=form. cleaned_data["preferred_language"]) if len(form.cleaned_data.get("status") ) == 1 and not form.cleaned_data.get("trash", False): ret = ret.filter( active=form.cleaned_data.get("status")[0] == "active") return ret return qs def get_settingspanel(self): return hg.DIV( layout.forms.Form( self._filterform(), hg.DIV( hg.DIV( hg.DIV(layout.helpers.Label(_("Person Type"))), hg.DIV( hg.DIV( hg.DIV( layout.forms.FormField( "naturalperson", onclick= "updateCheckboxGroupItems(this.parentElement.parentElement)", ), hg.DIV( layout.forms.FormField( "naturalperson_subtypes", style="padding-left: 1rem", ), style="margin-top: -2rem", ), ), layout.forms.FormField( "personassociation", onclick= "updateCheckboxGroupItems(this.parentElement.parentElement)", ), hg.DIV( layout.forms.FormField( "personassociation_subtypes", style="padding-left: 1rem", ), style="margin-top: -2rem", ), style="margin-right: 16px", ), hg.DIV( layout.forms.FormField( "legalperson", onclick= "updateCheckboxGroupItems(this.parentElement.parentElement)", ), hg.DIV( layout.forms.FormField( "legalperson_subtypes", style="padding-left: 1rem", ), style="margin-top: -2rem", ), style="margin-right: 16px", ), style="display: flex", ), style= "border-right: #ccc solid 1px; margin: 0 16px 0 0", ), hg.DIV( hg.DIV( layout.forms.FormField("tags"), style="margin-right: 16px", ), style= "border-right: #ccc solid 1px; margin: 0 16px 0 0; overflow-y: scroll", ), hg.DIV( hg.DIV( layout.forms.FormField("preferred_language"), style="margin-right: 16px", ), style= "border-right: #ccc solid 1px; margin: 0 16px 0 0", ), hg.DIV( hg.DIV(layout.forms.FormField("status"), style="flex-grow: 0"), hg.DIV(style="flex-grow: 1"), hg.DIV( layout.forms.FormField("trash"), style="max-height: 2rem", ), style="display: flex; flex-direction: column", ), style= "display: flex; max-height: 50vh; padding: 24px 32px 0 32px", ), hg.DIV( layout.button.Button( _("Cancel"), buttontype="ghost", onclick= "this.parentElement.parentElement.parentElement.parentElement.parentElement.style.display = 'none'", ), layout.button.Button.from_link( Link( label=_("Reset"), href=self.request.path + "?reset=1", iconname=None, ), buttontype="secondary", ), layout.button.Button( pgettext_lazy("apply filter", "Filter"), type="submit", ), style= "display: flex; justify-content: flex-end; margin-top: 24px", _class="bx--modal-footer", ), method="GET", ), hg.SCRIPT( mark_safe(""" function updateCheckboxGroupItems(group) { var items = $$('input[type=checkbox]', group); var value = items[0].getAttribute('aria-checked'); value = value == 'true' ? 'true' : 'false'; for(var i = 1; i < items.length; ++i) { new CarbonComponents.Checkbox(items[i]).setState(value); } } function updateCheckboxCounter(group) { var items = $$('input[type=checkbox]', group); var count = 0; for(item of items) { if(!(item.name === 'status' && item.value === 'active')) count += item.getAttribute('aria-checked') == 'true' ? 1 : 0 } $('#%s').innerHTML = count; $('#%s').closest('div[role=button]').style.display = count === 0 ? "none" : "flex"; } """ % (self.checkboxcounterid, self.checkboxcounterid))), style="background-color: #fff", onclick="updateCheckboxCounter(this)", )
def as_list(iterable): return hg.UL(hg.Iterator(iterable, "item", hg.LI(hg.C("item"))))
def get_attribute_description_modal(obj): from . import datatable, modal columns = [] fields = {f.name: f for f in obj._meta.get_fields()} for i in set(dir(obj) + list(vars(obj))): try: desc = _get_attribute_description(obj, i, fields) if desc is not None and desc[3]: f = desc[3]._meta.get_fields() additional_attrs = list( filter( None, ( _get_attribute_description(desc[3], a, f) for a in set(dir(desc[3]) + list(vars(desc[3]))) ), ) ) desc = ( desc[0], desc[1], desc[2], hg.BaseElement( hg.UL( hg.Iterator( additional_attrs, "attr", hg.LI( hg.format("{}.{}", i, hg.C("attr.0")), style="font-weight: 700", ), ) ), ), ) if desc is not None: columns.append(desc) except Exception as e: columns.append((i, _("Unknown"), e)) return modal.Modal( _("Available columns"), hg.DIV( hg.DIV(_("Bold text marks valid column names")), datatable.DataTable( columns=[ datatable.DataTableColumn( _("Column name"), hg.SPAN(hg.C("row.0"), style="font-weight: 700"), ), datatable.DataTableColumn( _("Description"), hg.F(lambda c: c["row"][2]) ), datatable.DataTableColumn(_("Type"), hg.F(lambda c: c["row"][1])), datatable.DataTableColumn(_("Extended columns"), hg.C("row.3")), ], row_iterator=sorted(columns), ), ), size="lg", )
def __init__(self, errors): super().__init__( htmlgenerator.UL(*[htmlgenerator.LI(e) for e in errors]), _class="bx--form-requirement", )
def __init__(self, menu: "bread.menu.Menu", **kwargs): kwargs["_class"] = hg.BaseElement( kwargs.get("_class", ""), " bx--side-nav bx--side-nav--rail", hg.If( HasBreadCookieValue("sidenav-hidden", "true"), "", " bx--side-nav--expanded", ), ) kwargs["data_side_nav"] = True super().__init__( hg.NAV( hg.UL( hg.Iterator( hg.F(lambda c: (i for i in sorted( hg.resolve_lazy(menu, c)._registry.values()) if i.has_permission(c["request"]))), "menugroup", hg.LI( hg.If( hg.F(lambda c: len(c["menugroup"].items) > 1 or c["menugroup"].force_show), hg.BaseElement( hg.BUTTON( hg.DIV( Icon(hg.C("menugroup.iconname"), size=16), _class="bx--side-nav__icon", ), hg.SPAN( hg.C("menugroup.label"), _class= "bx--side-nav__submenu-title", ), hg.DIV( Icon("chevron--down", size=16), _class= "bx--side-nav__icon bx--side-nav__submenu-chevron", ), _class="bx--side-nav__submenu", type="button", aria_haspopup="true", aria_expanded=hg.If( isactive("menugroup"), "true"), ), hg.UL( hg.Iterator( hg.F(lambda c: (i for i in sorted( c["menugroup"].items) if i. has_permission(c[ "request"]))), "menuitem", hg.LI( hg.A( hg.SPAN( hg. C("menuitem.link.label" ), _class= "bx--side-nav__link-text", ), _class=hg.BaseElement( "bx--side-nav__link", hg.If( isactive( "menuitem"), " bx--side-nav__link--current", ), ), href=hg.C( "menuitem.link.href"), ), _class=hg.BaseElement( "bx--side-nav__menu-item", hg.If( isactive("menuitem"), " bx--side-nav__menu-item--current", ), ), ), ), _class="bx--side-nav__menu", ), ), hg.A( hg.DIV( Icon( hg. C("menugroup.items.0.link.iconname" ), size=16, ), _class="bx--side-nav__icon", ), hg.SPAN( hg.C("menugroup.items.0.link.label"), _class="bx--side-nav__link-text", ), _class=hg.BaseElement( "bx--side-nav__link", hg.If( isactive("menugroup"), " bx--side-nav__link--current", ), ), href=hg.C("menugroup.items.0.link.href"), ), ), _class=hg.BaseElement( "bx--side-nav__item", hg.If(isactive("menugroup"), " bx--side-nav__item--active"), ), ), ), _class="bx--side-nav__items", ), hg.FOOTER( hg.BUTTON( hg.DIV( Icon( "close", size=20, _class= "bx--side-nav__icon--collapse bx--side-nav-collapse-icon", aria_hidden="true", ), Icon( "chevron--right", size=20, _class= "bx--side-nav__icon--expand bx--side-nav-expand-icon", aria_hidden="true", ), _class="bx--side-nav__icon", ), hg.SPAN( "Toggle the expansion state of the navigation", _class="bx--assistive-text", ), _class="bx--side-nav__toggle", onclick= "setBreadCookie('sidenav-hidden', getBreadCookie('sidenav-hidden', 'false') != 'true');", ), _class="bx--side-nav__footer", ), _class="bx--side-nav__navigation", ), **kwargs, )