module Scrivito class CmsRouting < Struct.new(:request, :context, :scrivito_engine, :image_options) LINK_TO_EMPTY_LINKLIST = "#__empty_linklist" LINK_TO_EMPTY_BLOB = "#__empty_blob" def path_or_url(target, path_or_url, options = {}) if needs_editing_context?(target) path_or_url_needs_editing_context(target, path_or_url, options) else path_or_url_without_editing_context(target, path_or_url, options) end end def convert_links(html) if html html.to_str.gsub(%r{\bobjid:([a-f0-9]{16})\b([^"']*)}) do if obj = Obj.find_by_id($1) options = {} if $2.present? begin uri = URI.parse($2) options.merge!(extract_query(uri)) options[:anchor] = uri.fragment rescue end end if editing_context.display_mode == 'editing' options[:id] = obj.id scrivito_engine.base_id_path(options) else path_or_url(obj, :path, options) end else "#__target_object_not_reachable" end end end end private def path_or_url_needs_editing_context(target, path_or_url, options) path_or_url_without_editing_context(target, path_or_url, options.merge(editing_context.to_params)) end def path_or_url_without_editing_context(target, path_or_url, options) if target.is_a?(Link) path_or_url_for_links(target, path_or_url, options) elsif target.is_a?(Scrivito::BasicObj) path_or_url_for_objs(target, path_or_url, options) elsif target.respond_to?(:first) if target.first.is_a?(Link) path_or_url_for_links(target.first, path_or_url, options) else return LINK_TO_EMPTY_LINKLIST end elsif target.is_a?(Binary) binary_url(target, path_or_url) else raise "scrivito_path or scrivito_url was called with an instance of #{target.class}. "+ "It must only be called with an Obj or a Link or a non-empty LinkList." end end def path_or_url_for_links(link, path_or_url, options) parsed_options = Rack::Utils.parse_nested_query(link.query) combined_options = parsed_options.symbolize_keys.merge(options) url = basic_url_or_path_for_link(link, path_or_url, combined_options) url = url + "##{link.fragment}" if link.fragment.present? url end def path_or_url_for_objs(obj, path_or_url, options) if permalink_route_applicable?(obj) && route = find_route(:permalink) return route.generate(path_or_url, options.merge(permalink: obj.permalink)) end if homepage?(obj) && route = find_route(:homepage) return route.generate(path_or_url, options) end if obj.binary? return binary_obj_url(obj, path_or_url) || LINK_TO_EMPTY_BLOB end if route = find_route(:slug_id) slug = obj.slug.present? ? obj.slug.sub(/^\//, '') : nil return route.generate(path_or_url, options.merge(id: obj.id, slug: slug)) end raise ScrivitoError, "The required scrivito route 'slug_id' is not defined. "\ "Please add a 'slug_id' definition to your routes.rb. See the documentation"\ " of 'scrivito_route' for further details." end def permalink_route_applicable?(obj) permalink = obj.permalink permalink && permalink.split('/').first != 'scrivito' && !obj.deleted? end def find_route(name) Route.find(context, name) end def homepage?(obj) RequestHomepage.call(request.env) == obj end def editor_authenticated? editing_context.authenticated_editor? end def editing_context EditingContextMiddleware.from_request(request) end def needs_editing_context?(target) editor_authenticated? && (!target.is_a?(Link) || target.internal?) end def basic_url_or_path_for_link(link, path_or_url, options = {}) if link.internal? path_or_url_without_editing_context(link.obj, path_or_url, options) else url = link.url url = merge_options(url, options) if options.any? url end end def merge_options(url, options) parsed_url = URI.parse(url) query = extract_query(parsed_url) merged_query = query.merge(options.stringify_keys) parsed_url.query = merged_query.to_query parsed_url.to_s end def extract_query(uri) Rack::Utils.parse_query(uri.query) end def binary_url(binary, path_or_url) BinaryRouting.new(request, scrivito_engine).binary_url(binary, path_or_url) end def binary_obj_url(obj, path_or_url) if binary = obj.binary BinaryRouting.new(request, scrivito_engine) .transformed_binary_url(binary, path_or_url, image_options || {}) end end end end