diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b40d0f3..567f79d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +3.1.2 +=== + +* Added `redirect` command for generating meta refreshes + 3.1.1 === diff --git a/middleman-core/features/redirects.feature b/middleman-core/features/redirects.feature new file mode 100644 index 00000000..bcbe3baf --- /dev/null +++ b/middleman-core/features/redirects.feature @@ -0,0 +1,60 @@ +Feature: Meta redirects + + Scenario: Redirect to unknown file + Given a fixture app "large-build-app" + And a file named "config.rb" with: + """ + redirect "hello.html", :to => "world.html" + """ + And the Server is running at "large-build-app" + When I go to "/hello.html" + Then I should see ' "http://example.com" + """ + And the Server is running at "large-build-app" + When I go to "/hello.html" + Then I should see ' r + end + """ + And the Server is running at "large-build-app" + When I go to "/hello.html" + Then I should see ' "link_test.html" + redirect "hello2.html", :to => "services/index.html" + """ + And the Server is running at "large-build-app" + When I go to "/hello/index.html" + Then I should see ' "world.html" do |from, to| + "#{from} to #{to}" + end + """ + And the Server is running at "large-build-app" + When I go to "/hello.html" + Then I should see 'hello.html to world.html' \ No newline at end of file diff --git a/middleman-core/lib/middleman-core/sitemap.rb b/middleman-core/lib/middleman-core/sitemap.rb index c41d6125..bd786731 100644 --- a/middleman-core/lib/middleman-core/sitemap.rb +++ b/middleman-core/lib/middleman-core/sitemap.rb @@ -2,6 +2,7 @@ require "middleman-core/sitemap/store" require "middleman-core/sitemap/resource" require "middleman-core/sitemap/extensions/on_disk" +require "middleman-core/sitemap/extensions/redirects" require "middleman-core/sitemap/extensions/request_endpoints" require "middleman-core/sitemap/extensions/proxies" require "middleman-core/sitemap/extensions/ignores" @@ -20,6 +21,7 @@ module Middleman app.register Middleman::Sitemap::Extensions::RequestEndpoints app.register Middleman::Sitemap::Extensions::Proxies app.register Middleman::Sitemap::Extensions::Ignores + app.register Middleman::Sitemap::Extensions::Redirects # Set to automatically convert some characters into a directory app.config.define_setting :automatic_directory_matcher, nil, 'Set to automatically convert some characters into a directory' diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb b/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb new file mode 100644 index 00000000..6ae8d6dd --- /dev/null +++ b/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb @@ -0,0 +1,124 @@ +module Middleman + + module Sitemap + + module Extensions + + module Redirects + + # Setup extension + class << self + + # Once registered + def registered(app) + # Include methods + app.send :include, InstanceMethods + end + + alias :included :registered + end + + module InstanceMethods + def redirect_manager + @_redirect_manager ||= RedirectManager.new(self) + end + + def redirect(*args, &block) + redirect_manager.create_redirect(*args, &block) + end + end + + # Manages the list of proxy configurations and manipulates the sitemap + # to include new resources based on those configurations + class RedirectManager + def initialize(app) + @app = app + @redirects = {} + end + + # Setup a redirect from a path to a target + # @param [String] path + # @param [Hash] The :to value gives a target path + # @return [void] + def create_redirect(path, opts={}, &block) + if block_given? + opts[:template] = block + end + + @redirects[path] = opts + + @app.sitemap.rebuild_resource_list!(:added_redirect) + end + + # Update the main sitemap resource list + # @return [void] + def manipulate_resource_list(resources) + resources + @redirects.map do |path, opts| + r = RedirectResource.new( + @app.sitemap, + path, + opts[:to] + ) + r.output = opts[:template] if opts[:template] + r + end + end + end + + class RedirectResource < ::Middleman::Sitemap::Resource + attr_accessor :output + + def initialize(store, path, target) + @request_path = target + + super(store, path) + end + + def template? + true + end + + def render(*args, &block) + url = ::Middleman::Util.url_for(store.app, @request_path, :relative => false, :find_resource => true) + + if output + output.call(path, url) + else + <<-END + +
+ + + + + + + END + end + end + + # def request_path + # @request_path + # end + + def binary? + false + end + + def raw_data + {} + end + + def ignored? + false + end + + def metadata + @local_metadata.dup + end + + end + end + end + end +end diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb b/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb index 43d33503..1227f60b 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb @@ -11,8 +11,6 @@ module Middleman # Once registered def registered(app) - # ::Middleman::Sitemap::Resource.send :include, ResourceInstanceMethods - # Include methods app.send :include, InstanceMethods end @@ -20,65 +18,6 @@ module Middleman alias :included :registered end - # module ResourceInstanceMethods - # # Whether this page is a proxy - # # @return [Boolean] - # def proxy? - # !!@proxied_to - # end - - # # Set this page to proxy to a target path - # # @param [String] target - # # @return [void] - # def proxy_to(target) - # target = ::Middleman::Util.normalize_path(target) - # raise "You can't proxy #{path} to itself!" if target == path - # @proxied_to = target - # end - - # # The path of the page this page is proxied to, or nil if it's not proxied. - # # @return [String] - # def proxied_to - # @proxied_to - # end - - # # The resource for the page this page is proxied to. Throws an exception - # # if there is no resource. - # # @return [Sitemap::Resource] - # def proxied_to_resource - # proxy_resource = store.find_resource_by_path(proxied_to) - - # unless proxy_resource - # raise "Path #{path} proxies to unknown file #{proxied_to}:#{store.resources.map(&:path)}" - # end - - # if proxy_resource.proxy? - # raise "You can't proxy #{path} to #{proxied_to} which is itself a proxy." - # end - - # proxy_resource - # end - - # def get_source_file - # if proxy? - # proxied_to_resource.source_file - # else - # super - # end - # end - - # def content_type - # mime_type = super - # return mime_type if mime_type - - # if proxy? - # proxied_to_resource.content_type - # else - # nil - # end - # end - # end - module InstanceMethods def endpoint_manager @_endpoint_manager ||= EndpointManager.new(self) diff --git a/middleman-core/lib/middleman-core/sitemap/store.rb b/middleman-core/lib/middleman-core/sitemap/store.rb index 4ae2bc15..91107a75 100644 --- a/middleman-core/lib/middleman-core/sitemap/store.rb +++ b/middleman-core/lib/middleman-core/sitemap/store.rb @@ -41,6 +41,9 @@ module Middleman # Proxies register_resource_list_manipulator(:proxies, @app.proxy_manager) + + # Redirects + register_resource_list_manipulator(:redirects, @app.redirect_manager) end # Register a klass which can manipulate the main site map list. Best to register diff --git a/middleman-core/lib/middleman-core/util.rb b/middleman-core/lib/middleman-core/util.rb index 01ed0c4c..96436e26 100644 --- a/middleman-core/lib/middleman-core/util.rb +++ b/middleman-core/lib/middleman-core/util.rb @@ -146,5 +146,81 @@ module Middleman end end.flatten.compact end + + # Given a source path (referenced either absolutely or relatively) + # or a Resource, this will produce the nice URL configured for that + # path, respecting :relative_links, directory indexes, etc. + def self.url_for(app, path_or_resource, options={}) + # Handle Resources and other things which define their own url method + url = path_or_resource.respond_to?(:url) ? path_or_resource.url : path_or_resource + + begin + uri = URI(url) + rescue URI::InvalidURIError + # Nothing we can do with it, it's not really a URI + return url + end + + relative = options.delete(:relative) + raise "Can't use the relative option with an external URL" if relative && uri.host + + # Allow people to turn on relative paths for all links with + # set :relative_links, true + # but still override on a case by case basis with the :relative parameter. + effective_relative = relative || false + effective_relative = true if relative.nil? && app.config[:relative_links] + + # Try to find a sitemap resource corresponding to the desired path + this_resource = app.current_resource # store in a local var to save work + + if path_or_resource.is_a?(::Middleman::Sitemap::Resource) + resource = path_or_resource + resource_url = url + elsif this_resource && uri.path + # Handle relative urls + url_path = Pathname(uri.path) + current_source_dir = Pathname('/' + this_resource.path).dirname + url_path = current_source_dir.join(url_path) if url_path.relative? + resource = app.sitemap.find_resource_by_path(url_path.to_s) + resource_url = resource.url if resource + elsif options[:find_resource] && uri.path + resource = app.sitemap.find_resource_by_path(uri.path) + resource_url = resource.url if resource + end + + if resource + # Switch to the relative path between this_resource and the given resource + # if we've been asked to. + if effective_relative + # Output urls relative to the destination path, not the source path + current_dir = Pathname('/' + this_resource.destination_path).dirname + relative_path = Pathname(resource_url).relative_path_from(current_dir).to_s + + # Put back the trailing slash to avoid unnecessary Apache redirects + if resource_url.end_with?('/') && !relative_path.end_with?('/') + relative_path << '/' + end + + uri.path = relative_path + else + uri.path = resource_url + end + else + # If they explicitly asked for relative links but we can't find a resource... + raise "No resource exists at #{url}" if relative + end + + # Support a :query option that can be a string or hash + if query = options.delete(:query) + uri.query = query.respond_to?(:to_param) ? query.to_param : query.to_s + end + + # Support a :fragment or :anchor option just like Padrino + fragment = options.delete(:anchor) || options.delete(:fragment) + uri.fragment = fragment.to_s if fragment + + # Finally make the URL back into a string + uri.to_s + end end end diff --git a/middleman-core/lib/middleman-more/core_extensions/default_helpers.rb b/middleman-core/lib/middleman-more/core_extensions/default_helpers.rb index a15cfe55..1a584da2 100644 --- a/middleman-core/lib/middleman-more/core_extensions/default_helpers.rb +++ b/middleman-core/lib/middleman-more/core_extensions/default_helpers.rb @@ -189,72 +189,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension # or a Resource, this will produce the nice URL configured for that # path, respecting :relative_links, directory indexes, etc. def url_for(path_or_resource, options={}) - # Handle Resources and other things which define their own url method - url = path_or_resource.respond_to?(:url) ? path_or_resource.url : path_or_resource - - begin - uri = URI(url) - rescue URI::InvalidURIError - # Nothing we can do with it, it's not really a URI - return url - end - - relative = options.delete(:relative) - raise "Can't use the relative option with an external URL" if relative && uri.host - - # Allow people to turn on relative paths for all links with - # set :relative_links, true - # but still override on a case by case basis with the :relative parameter. - effective_relative = relative || false - effective_relative = true if relative.nil? && config[:relative_links] - - # Try to find a sitemap resource corresponding to the desired path - this_resource = current_resource # store in a local var to save work - if path_or_resource.is_a?(::Middleman::Sitemap::Resource) - resource = path_or_resource - resource_url = url - elsif this_resource && uri.path - # Handle relative urls - url_path = Pathname(uri.path) - current_source_dir = Pathname('/' + this_resource.path).dirname - url_path = current_source_dir.join(url_path) if url_path.relative? - resource = sitemap.find_resource_by_path(url_path.to_s) - resource_url = resource.url if resource - end - - if resource - # Switch to the relative path between this_resource and the given resource - # if we've been asked to. - if effective_relative - # Output urls relative to the destination path, not the source path - current_dir = Pathname('/' + this_resource.destination_path).dirname - relative_path = Pathname(resource_url).relative_path_from(current_dir).to_s - - # Put back the trailing slash to avoid unnecessary Apache redirects - if resource_url.end_with?('/') && !relative_path.end_with?('/') - relative_path << '/' - end - - uri.path = relative_path - else - uri.path = resource_url - end - else - # If they explicitly asked for relative links but we can't find a resource... - raise "No resource exists at #{url}" if relative - end - - # Support a :query option that can be a string or hash - if query = options.delete(:query) - uri.query = query.respond_to?(:to_param) ? query.to_param : query.to_s - end - - # Support a :fragment or :anchor option just like Padrino - fragment = options.delete(:anchor) || options.delete(:fragment) - uri.fragment = fragment.to_s if fragment - - # Finally make the URL back into a string - uri.to_s + ::Middleman::Util.url_for(self, path_or_resource, options) end # Overload the regular link_to to be sitemap-aware - if you