module Scrivito class CmsRouting < Struct.new(:request, :main_app) LINK_TO_EMPTY_LINKLIST = "#__empty_linklist" LINK_TO_EMPTY_BLOB = "#__empty_blob" def self.match_protocol(url, request) request.ssl? && !url.starts_with?('https') ? url.gsub(/^http/, 'https') : url end def path_or_url(target, path_or_url, options = {}) if cms_with_editing_context?(target) path_or_url_with_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.gsub(%r{?}) do if obj = Obj.find_by_id($1) path_or_url(obj, "path") else "#__target_object_not_reachable" end end end end private def path_or_url_with_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?(Obj) 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 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 = {}) url = basic_url_or_path_for_link(link, path_or_url, Rack::Utils.parse_nested_query(link.query).merge(options)) url = url + "##{link.fragment}" if link.fragment.present? url end def path_or_url_for_objs(obj, path_or_url, options = {}) permalink = obj.permalink if permalink main_app.public_send("scrivito_permalink_#{path_or_url}", options.merge(:permalink => permalink)) elsif obj.homepage? main_app.public_send("scrivito_root_#{path_or_url}", options) else if obj.binary? && editing_context.display_mode != "editing" if obj.binary_url self.class.match_protocol(obj.binary_url, request) else LINK_TO_EMPTY_BLOB end else main_app.public_send( "cms_id_#{path_or_url}", options.merge( id: obj.id, slug: obj.slug.presence ) ) end end end def editor_authenticated? editing_context.authenticated_editor? end def editing_context EditingContextMiddleware.from_request(request) end def cms_with_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 = Rack::Utils.parse_query(parsed_url.query) merged_query = query.merge(options.stringify_keys) parsed_url.query = merged_query.to_query parsed_url.to_s end end end