module Scrivito # This class is the server-side equivalent of the JavaScript +cms_field_element+ class. class CmsFieldTag < Struct.new(:view, :tag_name, :obj_or_widget, :editing_options) FIELD_TYPES_WITH_ORIGINAL_CONTENT = %w[ binary date enum float html integer link linklist multienum reference referencelist string stringlist ] include TagRenderer def initialize(*args) super assert_valid_attribute end def content(&block) if field_type == 'widgetlist' raise ArgumentError, 'No block allowed for widgetlist fields' if block_given? modifications = modification_info || [] rendered_widgets = default_content.each_with_index.map do |widget, index| WidgetTag.new(view, widget, placement_modification: modifications[index], render_context: widget_render_context, inner_tag: inner_tag).render end view.safe_join(rendered_widgets) else attribute_renderer = AttributeValueRenderer.new(view) block_given? ? view.capture { yield } : attribute_renderer.render(default_content, field_type) end end def options return {} unless authenticated_editor? options = { 'field-name' => field_name, 'field-obj-class' => obj_or_widget.obj_class, 'field-type' => field_type, 'private-editor' => editing_options[:editor], 'private-field-allowed-values' => valid_values, 'private-field-workspace-id' => current_workspace_id, } modification = comparison.modification_for_attribute(obj_or_widget, field_name) if modification == Modification::EDITED && field_type != 'widgetlist' options['field-modification'] = modification end if obj_or_widget.kind_of?(BasicWidget) options['private-field-obj-id'] = obj_or_widget.obj.id options['private-field-widget-id'] = obj_or_widget.id else options['private-field-obj-id'] = obj_or_widget.id end if FIELD_TYPES_WITH_ORIGINAL_CONTENT.include?(field_type) original_value = view.scrivito_value(current_value) original_content = original_content(field_type, original_value, current_value) encoded_content = Base64.strict_encode64(MultiJson.encode(original_content)) options['private-field-original-content'] = encoded_content end if field_type == 'widgetlist' options["private-field-widget-inner-tag"] = inner_tag options['private-field-widget-allowed-classes'] = build_valid_widget_classes.to_json options['private-field-widget-render-mode'] = widget_render_context if editing_options[:disable_margins] options['private-field-widget-disable-margins'] = true end end options end def modification_info calculate_content_and_modification @content_and_modification.second end private def build_valid_widget_classes obj_or_widget.valid_widget_ruby_classes_for(field_name).map(&:to_s) end def widget_render_context editing_options[:widget_render_context] || WidgetTag::DEFAULT_RENDER_CONTEXT end def field_name editing_options[:field_name] end def inner_tag editing_options[:inner_tag] || WidgetTag::DEFAULT_TAG end def field_type @field_type ||= obj_or_widget.type_of_attribute(field_name) end def description_for_widget_class(class_name) class_name.constantize.description_for_editor end def default_content calculate_content_and_modification @content_and_modification.first end def calculate_content_and_modification @content_and_modification ||= comparison.diff_for(obj_or_widget, field_name) || [current_value, nil] end def comparison editing_context.comparison end def current_value obj_or_widget[field_name] end def current_workspace_id Workspace.current.id end def authenticated_editor? editing_context.authenticated_editor? end def editing_context EditingContextMiddleware.from_request(view.request) end def original_content(field_type, field_value, raw_value) if %w[ binary date reference referencelist ].include?(field_type) ClientAttributeSerializer.serialize_custom_attr_value(field_type, raw_value) else ClientAttributeSerializer.serialize_custom_attr_value(field_type, field_value) end end def valid_values MultiJson.encode(obj_or_widget.attribute_definitions[field_name].try(:values) || []) end def assert_valid_attribute unless obj_or_widget.has_attribute?(field_name) raise ScrivitoError, "Unknown attribute #{field_name} for #{obj_or_widget.obj_class}" end end end end