def fragment__render(fragment, context): if not fragment.include: return '' rendered_children = fragment.render_text_or_children(context=context) if fragment.template: return render_template(fragment.get_request(), fragment.template, dict(**context, **fragment.iommi_evaluate_parameters(), rendered_children=rendered_children)) is_void_element = fragment.tag in _void_elements if fragment.tag: if rendered_children: assert not is_void_element, f'{fragment.tag} is a void element, but it has children: {rendered_children}' return format_html( '<{tag}{attrs}>{children}</{tag}>', tag=fragment.tag, attrs=render_attrs(fragment.attrs), children=rendered_children, ) else: return format_html( '<{tag}{attrs}>' if is_void_element else '<{tag}{attrs}></{tag}>', tag=fragment.tag, attrs=render_attrs(fragment.attrs), ) else: return format_html( '{}', rendered_children, )
def render_fields(self): r = [] for field in values(self.fields): r.append(field.__html__()) # We need to preserve all other GET parameters, so we can e.g. filter in two forms on the same page, and keep sorting after filtering own_field_paths = {f.iommi_path for f in values(self.fields)} for k, v in items(self.get_request().GET): if k not in own_field_paths and not k.startswith('-'): r.append(format_html('<input type="hidden" name="{}" value="{}" />', k, v)) return format_html('{}\n' * len(r), *r)
def test_as_html(): # str case assert format_html('{}', as_html(part='foo', context={})) == 'foo' assert format_html('{}', as_html(part='<foo>bar</foo>', context={})) == '<foo>bar</foo>' assert format_html('{}', as_html(part=mark_safe('<foo>bar</foo>'), context={})) == '<foo>bar</foo>' # Template case c = RequestContext(req('get')) assert format_html('{}', as_html(part=Template('foo'), context=c)) == 'foo' assert format_html('{}', as_html(part=Template('<foo>bar</foo>'), context=c)) == '<foo>bar</foo>' # __html__ attribute case assert format_html( '{}', as_html(part=Struct(__html__=lambda context: 'foo'), context={})) == 'foo' assert format_html( '{}', as_html(part=Struct(__html__=lambda context: '<foo>bar</foo>'), context={})) == '<foo>bar</foo>' assert format_html( '{}', as_html( part=Struct(__html__=lambda context: mark_safe('<foo>bar</foo>')), context={})) == '<foo>bar</foo>'
def render_text_or_children(self, context): request = self.get_request() return format_html( '{}' * len(self.children), *[ as_html(part=x, context=context, request=request) for x in values(self.children) ])
def dunder_path__format(row, **_): if row.dunder_path is None: return '' prefix = row.dunder_path.rpartition('__')[0] return format_html( '<span class="full-path">{prefix}{separator}</span>{name}', prefix=prefix, separator='__' if prefix else '', name=row.name)
def render_fields(self): r = [] for field in self.fields.values(): r.append(field.__html__()) if self.is_full_form: r.append(format_html(AVOID_EMPTY_FORM, self.path())) # We need to preserve all other GET parameters, so we can e.g. filter in two forms on the same page, and keep sorting after filtering own_field_paths = {f.path() for f in self.fields.values()} for k, v in self.request().GET.items(): if k == self.own_target_marker(): continue # TODO: why is there a special case for '-' here? shouldn't it be self.own_target_marker or something? if k not in own_field_paths and k != '-': r.append( format_html('<input type="hidden" name="{}" value="{}" />', k, v)) return format_html('{}\n' * len(r), *r)
def icon(cls, icon, *, display_name=None, call_target=None, icon_classes=None, **kwargs): icon_classes_str = ' '.join( ['fa-' + icon_class for icon_class in icon_classes]) if icon_classes else '' if icon_classes_str: icon_classes_str = ' ' + icon_classes_str setdefaults_path( kwargs, display_name=format_html('<i class="fa fa-{}{}"></i> {}', icon, icon_classes_str, display_name), ) return call_target(**kwargs)
def test_format_html3(): assert render_template( req('get'), Template('{{foo}}'), dict(foo=format_html('{}', format_html( '<a href="foo">foo</a>')))) == '<a href="foo">foo</a>'
def test_format_html(): assert format_html('<{a}>{b}{c}', a='a', b=format_html('<b>'), c='<c>') == '<a><b><c>'
def iommi_close_tag(self): if self.tag is None: return '' else: return format_html('</{}>', self.tag)
def iommi_open_tag(self): if self.tag is None: return '' else: return format_html('<{}{}>', self.tag, self.attrs)
def test_fragment__render__simple_cases(): assert format_html('{}', html.h1('foo')) == '<h1 >foo</h1>' assert format_html('{}', Fragment('foo<foo>')) == 'foo<foo>'
class Page(Part): """ A page is used to compose iommi parts into a bigger whole. See the `howto <https://docs.iommi.rocks/en/latest/howto.html#parts-pages>`_ for example usages. """ title: str = EvaluatedRefinable() member_class: Type[Fragment] = Refinable() context = Refinable( ) # context is evaluated, but in a special way so gets no EvaluatedRefinable type class Meta: member_class = Fragment @reinvokable @dispatch( parts=EMPTY, context=EMPTY, ) def __init__(self, *, _parts_dict: Dict[str, PartType] = None, parts: dict, **kwargs): super(Page, self).__init__(**kwargs) self.parts = { } # This is just so that the repr can survive if it gets triggered before parts is set properly # First we have to up sample parts that aren't Part into Fragment def as_fragment_if_needed(k, v): if v is None: return None if not isinstance(v, (dict, Traversable)): return Fragment(children__text=v, _name=k) else: return v _parts_dict = { k: as_fragment_if_needed(k, v) for k, v in items(_parts_dict) } parts = Namespace( {k: as_fragment_if_needed(k, v) for k, v in items(parts)}) collect_members(self, name='parts', items=parts, items_dict=_parts_dict, cls=self.get_meta().member_class) def on_bind(self) -> None: bind_members(self, name='parts') if self.context and self.iommi_parent() != None: assert False, 'The context property is only valid on the root page' def own_evaluate_parameters(self): return dict(page=self) @dispatch(render=lambda rendered: format_html('{}' * len(rendered), *values(rendered))) def __html__(self, *, render=None): self.context = evaluate_strict_container( self.context or {}, **self.iommi_evaluate_parameters()) rendered = { name: as_html(request=self.get_request(), part=part, context=self.iommi_evaluate_parameters()) for name, part in items(self.parts) } return render(rendered) def as_view(self): return build_as_view_wrapper(self)