module Scrivito # @api public module CmsTagHelper VOID_TAGS = %w[area base br col command embed hr img input keygen link meta param source track wbr] # Returns an HTML block tag containing content from an Obj. # Add HTML attributes by passing an attributes hash to options. # The helper method is somewhat similar to (and internally uses) http://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag. # # This helper method also renders additional data attributes, which are needed for inplace editing. # These attributes are only rendered when appropriate, i.e. not for a regular visitor. # # @param tag_name [String, Symbol] Name of the html tag (e.g. +:h1+ or +:div+). # @param obj_or_widget [BasicObj, BasicWidget] A {BasicObj} or {BasicWidget} from which the +field_name+ is read. # @param field_name [String, Symbol] Which field of the Obj should be rendered. # @param options [Hash] Additional options, which are passed to +content_tag+. Use them to add HTML attributes to the tag. # @param block [Proc] Optional block to render inner HTML. If none given the value of attribute will be rendered instead. # @return [String] The rendered html tag # # @example Renders an

tag containing the text of the +headline+ attribute of +@obj+ and assigns the tag a css class called +very_important+. # <%= cms_tag :h2, @obj, :headline, class: "very_important" %> # # @example Renders an

tag containing an escaped +headline+. # <%= cms_tag :h2, @obj, :headline do %> # <%= strip_tags @obj.headline %> # <% end %> # # @api public def cms_tag(tag_name, obj_or_widget, field_name, options = {}, &block) cms_field_tag = CmsFieldTag.new( self, cms_editing_context, obj_or_widget, field_name.to_s ) begin field_type = cms_field_tag.field_type rescue ScrivitoError => e return content_tag(tag_name, '', options) end options.merge!(cms_field_tag.options) if VOID_TAGS.include?(tag_name.to_s) tag(tag_name, options) else inner_html = if block_given? && field_type != 'widget' capture { yield } else to_render = cms_field_tag.default_content if field_type == 'widget' modifications = cms_field_tag.modification_info || [] rendered_widgets = to_render.each_with_index.map do |widget, index| render_widget(widget, modifications[index]) end safe_join(rendered_widgets) else display_value(to_render) end end content_tag(tag_name, inner_html, options) end end class List attr_reader :output def initialize(template, child_obj, show_sortable_options, modification) @template = template @child_obj = child_obj @show_sortable_options = show_sortable_options @modification = modification end # @param tag_name [String, Symbol] Name of the html tag (e.g. +:div+ or +:span+). # @param options [Hash] Additional options, which are passed to +content_tag+. Use them to add HTML attributes to the tag. # @return [String] The rendered html tag # # @example Render a
tag containing the text "random content" and assigns the tag a css class called +very_important+. # <%= list.tag :div, class: "very_important" do %> # random content # <% end %> def tag(tag_name, options = {}, &block) if @output.present? raise '"list.tag" can only be called once per iteration!' else if @show_sortable_options options = options.merge({ 'data-scrivito-private-obj-id' => @child_obj.id, 'data-scrivito-private-obj-description-for-editor' => @child_obj.description_for_editor, }) if @modification == Modification::NEW || @modification == Modification::DELETED options['data-scrivito-private-obj-modification'] = @modification end end @output = @template.content_tag(tag_name, options, &block) + "\n" nil end end end # @param tag_name [String, Symbol] Name of the html tag (e.g. +:h1+ or +:div+). # @param obj [Obj] A {Obj} from which method_name is read. # @param method_name [String, Symbol] Which method_name of the Obj should be rendered. Currently only +toclist+ is supported # @param options [Hash] Additional options, which are passed to +content_tag+. Use them to add HTML attributes to the tag. # @yieldparam [List] list An instance, where +tag+ should be called once. # @yieldparam [Obj] child Each child of +toclist+ # @return [String] The rendered html tag # # @example # <%= cms_tag_list :div, @obj, :toclist, class: "very_important" do |list, child| %> # <%= list.tag :div, class: "also_important" do %> # <%= link_to cms_path(child) do %> # <%= cms_tag :span, child, :title %> # <% end %> # <% end %> # <% end %> # # # results for an obj with two children in # # def cms_tag_list(tag_name, obj, method_name, options = {}) if method_name.to_s == 'toclist' items = obj.toclist else raise "#{method_name} is not (yet) supported. Currently only toclist is supported." end children_sortable = false if inplace_editing_allowed? options['data-scrivito-private-obj-description-for-editor'] = obj.description_for_editor end if obj.has_attribute?(:child_order) items = Obj.sort_by_list(items, obj.child_order) children_sortable = inplace_editing_allowed? if children_sortable options = options.merge({ 'data-scrivito-private-child-list-order-obj-id' => obj.id, }) modification = cms_editing_context.comparison.modification_for_attribute(obj, :child_order) if modification == Modification::EDITED options['data-scrivito-private-child-list-modification'] = modification end end end inner_html = "\n".html_safe items.each do |item| item_modification = cms_editing_context.comparison.modification(item) list = List.new(self, item, children_sortable, item_modification) yield list, item inner_html << list.output end valid_page_classes = Obj.valid_page_classes_beneath(obj.path) if inplace_editing_allowed? options = options.merge({ 'data-scrivito-private-child-list-path' => obj.path, 'data-scrivito-private-child-list-allowed-classes' => valid_page_classes.to_json, }) end content_tag(tag_name, inner_html, options) end def render_widget(widget, container_modification = nil, obj = @obj, field_name = widget.container_field_name, container = widget.container) options = {} if inplace_editing_allowed? options['data-scrivito-private-widget-id'] = widget.id options['data-scrivito-widget-obj-class'] = widget.obj_class_name if has_widget_details_view?(widget) options['data-scrivito-private-widget-has-details-view'] = true end comparison = cms_editing_context.comparison options['data-scrivito-private-widget-modification'] = if comparison.revision == widget.revision if container_modification unless widget.in_revision(Workspace.current.revision) Modification::DELETED end end else comparison.modification(widget) end options['data-scrivito-private-widget-container-modification'] = container_modification end content_tag(:div, options) do render(template: "#{widget.class.to_s.underscore}/show", locals: {widget: widget}) end end def has_widget_details_view?(widget) details_template_path = "#{widget.obj_class_name.underscore}/details" lookup_context.find(details_template_path).present? rescue ActionView::MissingTemplate false end end end