class SourceDisplayNode(GenericNode): def __init__(self, obj, display, *args, **kwargs): self.obj = Variable(obj) self.display = Variable(display) super(SourceDisplayNode, self).__init__(*args, **kwargs) def render(self, context): try: obj = self.obj.resolve(context) display = self.display.resolve(context) args, kwargs = self.resolve_template_args(context) except: return "" sep = kwargs.get("sep", None) sep = args[1] if not sep and len(args) > 1 else sep kwargs = {} if sep: kwargs.update({"sep": sep}) if not (obj.__class__ is Content or hasattr(obj, "content_ptr")): raise TemplateSyntaxError("'source_display' only renders " "Content instances") html = get_content_source(obj, display, context=context, **kwargs) return self.render_output(context, html)
class VoteFormNode(GenericNode): template = "voting/form.html" def __init__(self, obj, vote, *args, **kwargs): self.obj = Variable(obj) self.vote = Variable(vote) super(VoteFormNode, self).__init__(*args, **kwargs) def render(self, context): try: ctx = { "object": self.obj.resolve(context), "vote": self.vote.resolve(context) } args, kwargs = self.resolve_template_args(context) except: return "" next = kwargs.get("next", None) next = args[0] if not next and len(args) > 0 else next if next: ctx.update({"next": next}) html = render_to_string(self.template, ctx, context_instance=context) return self.render_output(context, html)
class ObjectDisplayNode(GenericNode): def __init__(self, obj, display, *args, **kwargs): self.obj = Variable(obj) self.display = Variable(display) super(ObjectDisplayNode, self).__init__(*args, **kwargs) def render(self, context): try: obj = self.obj.resolve(context) display = self.display.resolve(context) args, kwargs = self.resolve_template_args(context) except: return "" ctx = {"object": obj, "display": display} color = kwargs.get("color", None) color = args[0] if not color and len(args) > 0 else color if color: ctx.update({"color": color}) if obj.__class__ is Item: template = "items/_product_display.html" elif obj.__class__ is User: template = "profiles/_profile_display.html" skills = obj.get_profile().skills ctx.update({"username": user_display(obj)}) if skills.count(): ctx.update({"skills": skills}) else: raise TemplateSyntaxError("'object_display' only renders Item " "and User instances") html = render_to_string(template, ctx, context_instance=context) return self.render_output(context, html)
class MDCTNode(Node): def __init__(self,var_name,fmt_name=settings.DEFAULT_TIME_FORMAT, tz_var=None): self.var_name = Variable(var_name) if tz_var is None: self.tz_var = None else: self.tz_var = Variable(tz_var) self.fmt_name = fmt_name def render(self, context): tz_name = settings.DEFAULT_TZ if self.tz_var is None: user = Variable('user').resolve(context) if user.is_authenticated(): tz_name = user.profile.time_zone else: tz_name = self.tz_var.resolve(context) dt = self.var_name.resolve(context) utc = pytz.timezone('UTC') tz = pytz.timezone(tz_name) dt = utc.localize(dt) dt = dt.astimezone(tz) return dt.strftime(settings.TIME_FORMATS[self.fmt_name])
class FormPartNode(Node): """ Named piece of HTML layout. """ def __init__(self, parser, token): bits = token.split_contents() if len(bits) > 5: raise TemplateSyntaxError( "%r accepts at most 4 arguments (part_id, section, asvar, varname), got: {}" % (bits[0], ','.join(bits[1:]))) self.part_id = Variable(bits[1]) self.section = bits[2] if len(bits) >= 3 else None self.varname = None if len(bits) > 3: if bits[3] != 'asvar': raise TemplateSyntaxError('Forth argument should be asvar, got {}', format(bits[3])) if len(bits) < 4: raise TemplateSyntaxError('Variable name not provided') else: self.varname = Variable(bits[4]) self.nodelist = parser.parse(('end{}'.format(bits[0]),)) parser.delete_first_token() def resolve_part(self, context): part = self.part_id.resolve(context) if isinstance(part, BoundField): part = part.field return part def render(self, context): part = self.resolve_part(context) parts = context['form_parts'] if self.section in parts[part]: # already rendered if self.varname is not None: context[self.varname.resolve(context)] = parts[part][self.section] return "" else: return parts[part][self.section] # child parts children = (node for node in self.nodelist if isinstance(node, FormPartNode)) _render_parts(context, children) # render own content value = self.nodelist.render(context).strip() if self.varname is not None: context[self.varname.resolve(context)] = value return '' else: if not value: return '' return value
class ContentInfoNode(GenericNode): def __init__(self, content, display, *args, **kwargs): self.content = Variable(content) self.display = Variable(display) super(ContentInfoNode, self).__init__(*args, **kwargs) def render(self, context): try: content = self.content.resolve(context) display = self.display.resolve(context) args, kwargs = self.resolve_template_args(context) except: return "" pic_size = kwargs.get("pic_size", None) pic_size = args[0] if not pic_size and len(args) > 0 else pic_size template, author = ["content/info.html", content.author] ctx = {"created": content.created} if "signature" in display or display == "detail": ctx.update({"case": "signature"}) if display in ["signature", "detail"]: ctx.update({ "signature_author": True, "signature_pic": True, "author_name": user_display(author), "reputation": author.reputation.reputation_incremented, "author_url": author.get_absolute_url(), "profile_pic": profile_pic(author, size=pic_size) }) elif display == "signature-author": ctx.update({ "signature_author": True, "author_name": user_display(author), "reputation": author.reputation.reputation_incremented, "author_url": author.get_absolute_url() }) elif display == "signature-pic": ctx.update({ "signature_pic": True, "profile_pic": profile_pic(author, size=pic_size) }) else: raise TemplateSyntaxError("'content_info': wrong display.") elif display == "header": ctx.update({ "case": "header", # "author_name": user_display(author), # "author_url": author.get_absolute_url(), "author": author, "reputation": author.reputation.reputation_incremented, "about": author.get_profile().about }) elif display in ["date", "list"]: ctx.update({"case": "date"}) else: raise TemplateSyntaxError("'content_info': wrong display value.") return render_to_string(template, ctx, context_instance=context)
class FormPartNode(Node): def __init__(self, parser, token): bits = token.split_contents() if len(bits) > 5: raise TemplateSyntaxError( "%r accepts at most 4 arguments (part_id, section, asvar, varname), got: {}" % (bits[0], ','.join(bits[1:]))) self.part_id = Variable(bits[1]) self.section = bits[2] if len(bits) >= 3 else None self.varname = None if len(bits) > 3: if bits[3] != 'asvar': raise TemplateSyntaxError( 'Forth argument should be as var, got {}', format(bits[3])) if len(bits) < 4: raise TemplateSyntaxError('Variable name not provided') else: self.varname = Variable(bits[4]) self.nodelist = parser.parse(('end{}'.format(bits[0]), )) parser.delete_first_token() def resolve_part(self, context): part = self.part_id.resolve(context) if isinstance(part, BoundField): part = part.field return part def render(self, context): part = self.resolve_part(context) parts = context['form_parts'] if self.section in parts[part]: # already rendered if self.varname is not None: context[self.varname.resolve(context)] = parts[part][ self.section] return "" else: return parts[part][self.section] # child parts children = (node for node in self.nodelist if isinstance(node, FormPartNode)) _render_parts(context, children) # render own content value = self.nodelist.render(context).strip() if self.varname is not None: context[self.varname.resolve(context)] = value return '' else: if not value: return '' return value
class RepeatNode(Node): def __init__(self, nodelist, count_from, count_to): self.nodelist = nodelist self.count_from = Variable(count_from) self.count_to = Variable(count_to) def render(self, context): output = self.nodelist.render(context) return output * (int(self.count_from.resolve(context)) - int(self.count_to.resolve(context)))
class WidgetAttrNode(Node): """ {% attr form.email 'widget' 'data-validate' %}email{% endattr %} {% attr form.email 'widget' 'class' append %}green{% endattr %} {% attr form.email 'widget' 'required' %}True{% endattr %} """ def __init__(self, parser, token): bits = token.split_contents() if len(bits) < 4: raise TemplateSyntaxError( "{} accepts at least 3 arguments (bound_field, 'groupname' 'attr_name'), got: {}" .format((bits[0], ','.join(bits[1:])))) if len(bits) > 5: raise TemplateSyntaxError( "{} accepts at mast 4 arguments (bound_field, 'groupname' 'attr_name' action ), got: {}" .format((bits[0], ','.join(bits[1:])))) if len(bits) >= 5 and bits[4] not in ['append', 'override']: raise TemplateSyntaxError( "{} unknown action {} should be 'append' of 'override'". format((bits[0], ','.join(bits[4])))) self.field = Variable(bits[1]) self.group = Variable(bits[2]) self.attr = bits[3] self.action = bits[4] if len(bits) >= 5 else 'override' self.nodelist = parser.parse(('end{}'.format(bits[0]), )) parser.delete_first_token() def resolve_field(self, context): field = self.field.resolve(context) if isinstance(field, BoundField): field = field.field return field def render(self, context): field = self.resolve_field(context) group = self.group.resolve(context) form_widget_attrs = context['form_widget_attrs'] value = self.nodelist.render(context) if group not in form_widget_attrs[field]: form_widget_attrs[field][group] = {} attrs = form_widget_attrs[field][group] if self.attr not in attrs or self.action == 'override': attrs[self.attr] = (value, self.action) else: old_value, old_action = attrs[self.attr] if old_action != 'override': attrs[self.attr] = ('{} {}'.format(old_value, value), self.action)
class WidgetAttrNode(Node): """ {% attr form.email 'widget' 'data-validate' %}email{% endattr %} {% attr form.email 'widget' 'class' append %}green{% endattr %} {% attr form.email 'widget' 'required' %}True{% endattr %} """ def __init__(self, parser, token): bits = token.split_contents() if len(bits) < 4: raise TemplateSyntaxError( "{} accepts at least 3 arguments (bound_field, 'groupname' 'attr_name'), got: {}".format( (bits[0], ','.join(bits[1:])))) if len(bits) > 5: raise TemplateSyntaxError( "{} accepts at mast 4 arguments (bound_field, 'groupname' 'attr_name' action ), got: {}".format( (bits[0], ','.join(bits[1:])))) if len(bits) >= 5 and bits[4] not in ['append', 'override']: raise TemplateSyntaxError( "{} unknown action {} should be 'append' of 'override'".format( (bits[0], ','.join(bits[4])))) self.field = Variable(bits[1]) self.group = Variable(bits[2]) self.attr = bits[3] self.action = bits[4] if len(bits) >= 5 else 'override' self.nodelist = parser.parse(('end{}'.format(bits[0]),)) parser.delete_first_token() def resolve_field(self, context): field = self.field.resolve(context) if isinstance(field, BoundField): field = field.field return field def render(self, context): field = self.resolve_field(context) group = self.group.resolve(context) form_widget_attrs = context['form_widget_attrs'] value = self.nodelist.render(context) if group not in form_widget_attrs[field]: form_widget_attrs[field][group] = {} attrs = form_widget_attrs[field][group] if self.attr not in attrs or self.action == 'override': attrs[self.attr] = (value, self.action) else: old_value, old_action = attrs[self.attr] if old_action != 'override': attrs[self.attr] = ('{} {}'.format(old_value, value), self.action)
class FollowFormNode(GenericNode): template = "follow/follow_form.html" def __init__(self, user, obj, *args, **kwargs): self.user = Variable(user) self.obj = Variable(obj) super(FollowFormNode, self).__init__(*args, **kwargs) def render(self, context): try: user = self.user.resolve(context) obj = self.obj.resolve(context) args, kwargs = self.resolve_template_args(context) except: return "" fields = ["next", "base_class", "success_class", "extra_class", "tooltip_class", "quote_type"] for index, field in enumerate(fields): value = kwargs.get(field, None) value = args[index] if not value and len(args) > index else value setattr(self, field, value) is_following = Follow.objects.is_following(user, obj) buttons = copy.deepcopy(DEFAULT_CONF) for key in buttons.keys(): if user == obj: buttons[key].update({"disabled": "disabled"}) if self.base_class: buttons[key].update({"class": self.base_class}) if self.extra_class: buttons[key].update({"extra_class": self.extra_class}) tooltip_class = self.tooltip_class if self.tooltip_class else \ buttons[key]["tooltip_class"] buttons[key].update({ "class": buttons[key]["class"] + " " + tooltip_class}) if key == "following" and self.success_class: buttons[key]["class"] += " " + self.success_class key = "follow" if is_following else "following" buttons[key]["class"] = buttons[key]["class"] + " hidden" ctx = {"object": obj, "buttons": buttons} if self.next: ctx.update({"next": self.next}) html = render_to_string(self.template, ctx, context_instance=context) return self.render_output(context, html)
class _ABTestNode(Node): def __init__(self, ab_test_name, variant_a_content, variant_b_content): self.ab_test_name = Variable(ab_test_name) self.variant_a_content = variant_a_content self.variant_b_content = variant_b_content def render(self, context): ab_test_name = self.ab_test_name.resolve(context) ab_test = ABTest.objects.get_or_create(name=ab_test_name)[0] block_context = context.copy() block_context["ab_test"] = ab_test if self._is_variant_a_applicable(context["request"]): block_context["ab_test_variant"] = VARIANT_A content = self.variant_a_content.render(block_context) ab_test.times_a_presented += 1 else: block_context["ab_test_variant"] = VARIANT_B content = self.variant_b_content.render(block_context) ab_test.times_b_presented += 1 ab_test.save() return content @staticmethod def _is_variant_a_applicable(request): raise NotImplementedError("Determine whether A or B is applicable!")
class TagDisplayNode(GenericNode): def __init__(self, tag, *args, **kwargs): self.tag = Variable(tag) super(TagDisplayNode, self).__init__(*args, **kwargs) def render(self, context): try: tag = self.tag.resolve(context) args, kwargs = self.resolve_template_args(context) except: return "" template, ctx = ["tags/_tag_display.html", {"tag": tag}] fields = ["quote_type", "extra_class"] for index, field in enumerate(fields): value = kwargs.get(field, None) value = args[index] if not value and len(args) > index else value if not value: continue if field == "quote_type": setattr(self, field, value) elif field == "extra_class": ctx.update({field: value}) html = render_to_string(template, ctx, context_instance=context) html = hyphenate(html) return self.render_output(context, html)
class RangeNode(Node): def __init__(self, num, context_name): self.num, self.context_name = Variable(num), context_name def render(self, context): context[self.context_name] = range(int(self.num.resolve(context))) return ""
class IncludeHandlebarsNode(Node): def __init__(self, name): self.name = Variable(name) def render(self, context): name = self.name.resolve(context) template = get_template(name) return template.render(context)
class URINode(GenericNode): def __init__(self, obj, request, *args, **kwargs): self.obj = Variable(obj) self.request = Variable(request) super(URINode, self).__init__(*args, **kwargs) def render(self, context): try: obj = self.obj.resolve(context) request = self.request.resolve(context) except: return "" if obj.__class__ is not Tag: url = obj.get_absolute_url() else: # Dirty. Check coop-tag url = reverse("tagged_items", args=[obj.slug]) return self.render_output(context, request.build_absolute_uri(url))
class UserCommentCountNode(Node): def __init__(self, user, context_name): self.user = Variable(user) self.context_name = context_name def render(self, context): user = self.user.resolve(context) context[self.context_name] = user.jcomment_set.all().count() return ''
class FormRenderNode(Node): """Sugar for element in template rendering.""" def __init__(self, parser, token): # noqa D102 bits = token.split_contents() if len(bits) == 0: raise TemplateSyntaxError( "%r received invalid args, expected one element for render." " Got: %r".format(bits[0], bits[1:])) remaining_bits = bits[2:] self.kwargs = token_kwargs(remaining_bits, parser) if remaining_bits: raise TemplateSyntaxError("%r received an invalid token: %r" % (bits[0], remaining_bits[0])) for key in self.kwargs: if key not in ('template', 'widget'): raise TemplateSyntaxError("%r received an invalid key: %r" % (bits[0], key)) self.kwargs[key] = self.kwargs[key] self.nodelist = parser.parse(('end{}'.format(bits[0]), )) parser.delete_first_token() self.element = Variable(bits[1]) def render(self, context): # noqa D102 element = self.element.resolve(context) options = {} for key, value in self.kwargs.items(): options[key] = value.resolve(context) # render inner parts children = (node for node in self.nodelist if isinstance(node, FormPartNode)) _render_parts(context, children) attrs = (node for node in self.nodelist if isinstance(node, WidgetAttrNode)) for attr in attrs: attr.render(context) # render element if isinstance(element, BoundField): return Field(element.name).render(context, **options) elif hasattr(element, 'render'): with context.push(parent=element): return element.render(context, **options) else: raise TemplateSyntaxError( "form_render can't render %r".format(element))
class NnmcommentCountNode(Node): def __init__(self, content_object, context_name): self.content_object = Variable(content_object) self.context_name = context_name def render(self, context): content_object = self.content_object.resolve(context) context[self.context_name] = Nnmcomment.public.all_for_object( content_object).count() return ''
class FormRenderNode(Node): def __init__(self, parser, token): bits = token.split_contents() if len(bits) == 0: raise TemplateSyntaxError( "%r received invalid args, expected one element for render. Got: %r" % (bits[0], bits[1:])) remaining_bits = bits[2:] self.kwargs = token_kwargs(remaining_bits, parser) if remaining_bits: raise TemplateSyntaxError("%r received an invalid token: %r" % (bits[0], remaining_bits[0])) for key in self.kwargs: if key not in ('template', 'widget'): raise TemplateSyntaxError("%r received an invalid key: %r" % (bits[0], key)) self.kwargs[key] = self.kwargs[key] self.nodelist = parser.parse(('end{}'.format(bits[0]),)) parser.delete_first_token() self.element = Variable(bits[1]) def render(self, context): element = self.element.resolve(context) options = {} for key, value in self.kwargs.items(): options[key] = value.resolve(context) # render inner parts children = (node for node in self.nodelist if isinstance(node, FormPartNode)) _render_parts(context, children) attrs = (node for node in self.nodelist if isinstance(node, WidgetAttrNode)) for attr in attrs: attr.render(context) # render element if isinstance(element, BoundField): return Field(element.name).render(context, **options) elif hasattr(element, 'render'): with context.push(parent=element): return element.render(context, **options) else: raise TemplateSyntaxError("form_render can't render %r" % (element, ))
class IncludeNode(Node): def __init__(self, template_name): self.template_name = Variable(template_name) def render(self, context): try: template_name = self.template_name.resolve(context) t = get_template(template_name) return t.render(context) except TemplateSyntaxError, e: if settings.TEMPLATE_DEBUG: raise return '' except:
class EditButtonsNode(GenericNode): template = "items/_edit_buttons.html" def __init__(self, user, obj, *args, **kwargs): self.user = Variable(user) self.obj = Variable(obj) super(EditButtonsNode, self).__init__(*args, **kwargs) def render(self, context): try: user = self.user.resolve(context) obj = self.obj.resolve(context) args, kwargs = self.resolve_template_args(context) except: return "" #TODO: More generically, implement a 'can_manage' permission if not (user.is_superuser or getattr(obj, "author", None) == user): return "" url_args = [obj._meta.module_name, obj.id] ctx = { "edit_url": reverse("item_edit", args=url_args), "delete_url": reverse("item_delete", args=url_args) } fields = ["edit_next", "delete_next"] for index, field in enumerate(fields): value = kwargs.get(field, None) value = args[index] if not value and len(args) > index else value if value: key = string.replace(field, "next", "url") ctx.update({key: "%s?next=%s" % (ctx.get(key), value)}) html = render_to_string(self.template, ctx, context_instance=context) return self.render_output(context, html)
class IncludeThingTemplateNode(Node): def __init__(self, template_name, context_thing): self.template_name = template_name self.context_thing = Variable(context_thing) def render(self, context): thing = self.context_thing.resolve(context) try: template = loader.get_template(self.template_name % (thing.type,)) except TemplateDoesNotExist: template = loader.get_template(self.template_name % ("default",)) template_context = Context(dict_=context) template_context.update({ 'thing': thing }) return template.render(template_context)
class PriceNode(GenericNode): def __init__(self, value, *args, **kwargs): self.value = Variable(value) super(PriceNode, self).__init__(*args, **kwargs) def render(self, context): try: value = self.value.resolve(context) args, kwargs = self.resolve_template_args(context) except: return "" fields = ["currency", "language_code"] for index, field in enumerate(fields): val = kwargs.get(field, None) val = args[index] if not val and len(args) > index else val setattr(self, field, val) return self.render_output( context, format_price(value, self.currency, self.language_code))
class FormPartNode(Node): def __init__(self, parser, token): bits = token.split_contents() if len(bits) > 3: raise TemplateSyntaxError( "%r accepts at most 2 arguments (part_id, section), got: {}" % (bits[0], bits[1:])) self.part_id = Variable(bits[1]) self.section = bits[2] if len(bits) == 3 else None self.nodelist = parser.parse(('end{}'.format(bits[0]), )) parser.delete_first_token() def resolve_part(self, context): part = self.part_id.resolve(context) if isinstance(part, BoundField): part = part.field return part def render(self, context): part = self.resolve_part(context) parts = context['form_parts'] if self.section in parts[part]: # already rendered return parts[part][self.section] # child parts children = (node for node in self.nodelist if isinstance(node, FormPartNode)) _render_parts(context, children) # render own content value = self.nodelist.render(context).strip() if not value: return '' return value
class FormPartNode(Node): def __init__(self, parser, token): bits = token.split_contents() if len(bits) > 3: raise TemplateSyntaxError( "%r accepts at most 2 arguments (part_id, section), got: {}" % (bits[0], bits[1:])) self.part_id = Variable(bits[1]) self.section = bits[2] if len(bits) == 3 else None self.nodelist = parser.parse(('end{}'.format(bits[0]),)) parser.delete_first_token() def resolve_part(self, context): part = self.part_id.resolve(context) if isinstance(part, BoundField): part = part.field return part def render(self, context): part = self.resolve_part(context) parts = context['form_parts'] if self.section in parts[part]: # already rendered return parts[part][self.section] # child parts children = (node for node in self.nodelist if isinstance(node, FormPartNode)) _render_parts(context, children) # render own content value = self.nodelist.render(context) if not value.strip(): return '' return value
class UpdateQueryNode(template.Node): def __init__(self, query, parts): self.query = Variable(query) self.parts = self.query_update(parts) def query_update(self, query_parts): parts = [] for part in query_parts: parts.append(part.split('=')) return parts def render(self, context): query = self.query.resolve(context).copy() for var, value in self.parts: if (value[0] == value[-1] and value[0] in ('\'', '"') and len(value[0]) > 2): query[var] = value[1:-1] else: query[var] = Variable(value).resolve(context) query_str = query.urlencode() if query_str: return '?%s' % query_str else: return ''
class TagsDisplayNode(GenericNode): def __init__(self, tags, *args, **kwargs): self.tags = Variable(tags) super(TagsDisplayNode, self).__init__(*args, **kwargs) def render(self, context): try: tags = self.tags.resolve(context) args, kwargs = self.resolve_template_args(context) except: return "" f_kwargs = { "queryset": tags.all(), "object_name": "tag", "template": "tags/_tag_display.html", "context": context, "container": "tags" } fields = ["max_nb", "quote_type", "sep", "extra_class"] for index, field in enumerate(fields): value = kwargs.get(field, None) value = args[index] if not value and len(args) > index else value if not value: continue if field == "extra_class": f_kwargs.update({"template_context": {field: value}}) elif field == "quote_type": setattr(self, field, value) else: f_kwargs.update({field: value}) html = generate_objs_sentence(**f_kwargs) # html = hyphenate(html) return self.render_output(context, html)
class WidgetAttrsNode(Node): def __init__(self, parser, token): bits = token.split_contents() if len(bits) < 3: raise TemplateSyntaxError( "%r accepts at least 2 arguments (bound_field, 'groupname'), got: {}" % (bits[0], ','.join(bits[1:]))) if len(bits) > 5: raise TemplateSyntaxError( "%r accepts at mast 4 arguments (bound_field, 'groupname' default attrs_dict ), got: {}" % (bits[0], ','.join(bits[1:]))) if len(bits) > 3 and bits[3] != 'default': raise TemplateSyntaxError( "%r 3d argument should be 'default' (bound_field, 'groupname' default attrs_dict ), got: {}" % (bits[0], ','.join(bits[1:]))) self.field = Variable(bits[1]) self.group = Variable(bits[2]) self.widget_attrs = Variable(bits[4]) if len(bits) >= 5 else None self.nodelist = parser.parse(('end{}'.format(bits[0]), )) parser.delete_first_token() def resolve_field(self, context): field = self.field.resolve(context) if isinstance(field, BoundField): field = field.field return field def render(self, context): field = self.resolve_field(context) group = self.group.resolve(context) form_widget_attrs = context['form_widget_attrs'] override = {} if group in form_widget_attrs[field]: override = form_widget_attrs[field][group] build_in_attrs, tag_content = {}, self.nodelist.render(context) for attr, _, value in ATTRS_RE.findall(tag_content): build_in_attrs[attr] = mark_safe(value) if value != '' else True widget_attrs = {} if self.widget_attrs is not None: widget_attrs = self.widget_attrs.resolve(context) result = build_in_attrs.copy() if 'class' in result and 'class' in widget_attrs: result['class'] += ' ' + widget_attrs.pop('class') result.update(widget_attrs) for attr, (value, action) in override.items(): if action == 'override': result[attr] = value elif action == 'append': if attr in result: result[attr] += " " + value else: result[attr] = value return flatatt(result)
class TableNode(Node): """ Allows easy construction of complex tables. {% table 'object_list' %} {% tableheader attr='attribute' text='Display Name' %} {% tableheader attr='attribute2' sortable=True %} {% if some_condition %} {% tableheader attr='conditional_header' %} {% endif %} {% endtable %} """ def __init__(self, parser, token): bits = token.split_contents() bits.pop(0) if len(bits) == 0: raise TemplateSyntaxError("'table' statement takes one argument") self.data = Variable(bits.pop(0)) self.kwargs = token_kwargs(bits, parser) for key in self.kwargs: if key not in ('collapseclass', 'templatename', 'subtabletoggletext'): raise TemplateSyntaxError("'table' received invalid key: %s" % key) self.nodelist = parser.parse(('endtable', )) parser.delete_first_token() def render(self, context): request = context.get("request") data = self.data.resolve(context) collapseclass = self.kwargs.get('collapseclass') if collapseclass: collapseclass = collapseclass.resolve(context) template_name = self.kwargs.get('templatename') if template_name: template_name = template_name.resolve(context) else: template_name = 'dj_tables/bootstrap4.html' template = get_template(template_name) subtable_toggle_text = self.kwargs.get('subtabletoggletext') if subtable_toggle_text: subtable_toggle_text = subtable_toggle_text.resolve(context) else: subtable_toggle_text = 'View Details' with context.push( tableid=uuid.uuid4( ), # just some unique id for the table, useful for bootstrap collapsibles ie. tabledata=data, tableheaders=[], tablerowactions=[], subtabletoggletext=subtable_toggle_text, subtables=[]): self._render_nodelist(self.nodelist, context) context['collapseclass'] = collapseclass tablecolspan = len(context['tableheaders']) if context['tablerowactions']: tablecolspan += 1 if context['subtables']: tablecolspan += 1 context['tablecolspan'] = tablecolspan return template.render(context.flatten()) def _render_nodelist(self, nodelist, context): for node in nodelist: if self._can_render_node(node): node.render(context) elif hasattr(node, 'conditions_nodelists'): node.conditions_nodelists = [ (condition, NodeList([ node for node in nodelist if self._can_render_node(node) ])) for condition, nodelist in node.conditions_nodelists ] node.render(context) elif hasattr(node, 'nodelist'): node.nodelist = NodeList([ node for node in node.nodelist if self._can_render_node(node) ]) node.render(context) def _can_render_node(self, node): return isinstance(node, TableHeaderNode) or\ isinstance(node, TableRowActionNode) or\ isinstance(node, SubtableNode)
class WidgetAttrNode(Node): """The tag allows to add or override specific attribute in the rendered HTML. The first argumnent is the attribute group name, second is the attribute name. The third optional flag shows to override (by default) or `append` the value. Usage:: {% attr form.email 'widget' 'data-validate' %}email{% endattr %} {% attr form.email 'widget' 'class' append %}green{% endattr %} {% attr form.email 'widget' 'required' %}True{% endattr %} """ def __init__(self, parser, token): # noqa D102 bits = token.split_contents() if len(bits) < 4: raise TemplateSyntaxError( "{} accepts at least 3 arguments (bound_field, 'groupname'" " 'attr_name'), got: {}".format(bits[0], ','.join(bits[1:]))) if len(bits) > 5: raise TemplateSyntaxError( "{} accepts at mast 4 arguments (bound_field, 'groupname'" " 'attr_name' action ), got: {}".format( bits[0], ','.join(bits[1:]))) if len(bits) >= 5 and bits[4] not in ['append', 'override']: raise TemplateSyntaxError( "{} unknown action {} should be 'append'" " of 'override'".format(bits[0], ','.join(bits[4]))) self.field = Variable(bits[1]) self.group = Variable(bits[2]) self.attr = bits[3] self.action = bits[4] if len(bits) >= 5 else 'override' self.nodelist = parser.parse(('end{}'.format(bits[0]))) parser.delete_first_token() def resolve_field(self, context): """Resolve field reference form context.""" field = self.field.resolve(context) if isinstance(field, BoundField): field = field.field return field def render(self, context): # noqa D102 field = self.resolve_field(context) group = self.group.resolve(context) form_widget_attrs = context['form_widget_attrs'] value = self.nodelist.render(context) if group not in form_widget_attrs[field]: form_widget_attrs[field][group] = {} attrs = form_widget_attrs[field][group] if self.attr not in attrs or self.action == 'override': attrs[self.attr] = (value, self.action) else: old_value, old_action = attrs[self.attr] if old_action != 'override': attrs[self.attr] = ('{} {}'.format(old_value, value), self.action)
class WidgetAttrNode(Node): """The tag allows to add or override specific attribute in the rendered HTML. The first argumnent is the attribute group name, second is the attribute name. The third optional flag shows to override (by default) or `append` the value. Usage:: {% attr form.email 'widget' 'data-validate' %}email{% endattr %} {% attr form.email 'widget' 'class' append %}green{% endattr %} {% attr form.email 'widget' 'required' %}True{% endattr %} """ def __init__(self, parser, token): # noqa D102 bits = token.split_contents() if len(bits) < 4: raise TemplateSyntaxError( "{} accepts at least 3 arguments (bound_field, 'groupname'" " 'attr_name'), got: {}".format(bits[0], ','.join(bits[1:])) ) if len(bits) > 5: raise TemplateSyntaxError( "{} accepts at mast 4 arguments (bound_field, 'groupname'" " 'attr_name' action ), got: {}".format( bits[0], ','.join(bits[1:])) ) if len(bits) >= 5 and bits[4] not in ['append', 'override']: raise TemplateSyntaxError( "{} unknown action {} should be 'append'" " of 'override'".format(bits[0], ','.join(bits[4])) ) self.field = Variable(bits[1]) self.group = Variable(bits[2]) self.attr = bits[3] self.action = bits[4] if len(bits) >= 5 else 'override' self.nodelist = parser.parse(('end{}'.format(bits[0]))) parser.delete_first_token() def resolve_field(self, context): """Resolve field reference form context.""" field = self.field.resolve(context) if isinstance(field, BoundField): field = field.field return field def render(self, context): # noqa D102 field = self.resolve_field(context) group = self.group.resolve(context) form_widget_attrs = context['form_widget_attrs'] value = self.nodelist.render(context) if group not in form_widget_attrs[field]: form_widget_attrs[field][group] = {} attrs = form_widget_attrs[field][group] if self.attr not in attrs or self.action == 'override': attrs[self.attr] = (value, self.action) else: old_value, old_action = attrs[self.attr] if old_action != 'override': attrs[self.attr] = ( '{} {}'.format(old_value, value), self.action )
class WidgetAttrsNode(Node): """ Renders attrs for the html tag. <input{% attrs boundfield 'widget' default field.widget.attrs %} id="id_{{ bound_field.name }}" class="{% if bound_field.errors %}invalid{% endif %}" {% endattrs %}> """ def __init__(self, parser, token): # noqa D102 bits = token.split_contents() if len(bits) < 3: raise TemplateSyntaxError( "%r accepts at least 2 arguments (bound_field," " 'groupname'), got: {}".format(bits[0], ','.join(bits[1:])) ) if len(bits) > 5: raise TemplateSyntaxError( "%r accepts at mast 4 arguments (bound_field, 'groupname'" " default attrs_dict ), got: {}".format( bits[0], ','.join(bits[1:])) ) if len(bits) > 3 and bits[3] != 'default': raise TemplateSyntaxError( "%r 3d argument should be 'default' (bound_field, 'groupname'" " default attrs_dict ), got: {}".format( bits[0], ','.join(bits[1:])) ) self.field = Variable(bits[1]) self.group = Variable(bits[2]) self.widget_attrs = Variable(bits[4]) if len(bits) >= 5 else None self.nodelist = parser.parse(('end{}'.format(bits[0]))) parser.delete_first_token() def resolve_field(self, context): """Resolve field reference form context.""" field = self.field.resolve(context) if isinstance(field, BoundField): field = field.field return field def render(self, context): # noqa D102 field = self.resolve_field(context) group = self.group.resolve(context) form_widget_attrs = context['form_widget_attrs'] override = {} if group in form_widget_attrs[field]: override = form_widget_attrs[field][group] build_in_attrs, tag_content = {}, self.nodelist.render(context) for attr, _, value in ATTRS_RE.findall(tag_content): build_in_attrs[attr] = mark_safe(value) if value != '' else True widget_attrs = {} if self.widget_attrs is not None: widget_attrs = self.widget_attrs.resolve(context) result = build_in_attrs.copy() if 'class' in result and 'class' in widget_attrs: result['class'] += ' ' + widget_attrs.pop('class') result.update(widget_attrs) for attr, (value, action) in override.items(): if action == 'override': result[attr] = value elif action == 'append': if attr in result: result[attr] += " " + value else: result[attr] = value return flatatt(result)