radiant-vapor-extension 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ .svn
7
+ *.gemspec
data/HELP_admin.md ADDED
@@ -0,0 +1,37 @@
1
+ With Vapor you can create URLs that will point users to other locations.
2
+
3
+ ## How It Works
4
+
5
+ Vapor manages the redirection in objects called flow meters. In each flow meter you'll need to have
6
+ the `catch_url` (which is the URL that will be the false page) and the `redirect_url` (which is an
7
+ actual page on your site). By default the `status` is set to '307 Temporarily Moved'.
8
+
9
+ You may also set the redirect_url to an external site beginning with 'http://'.
10
+
11
+ ## What to Catch
12
+
13
+ You have some options with Vapor.
14
+
15
+ By default, each flow meter that you create will only match against
16
+ the exact url. This means that a `catch_url` of `/articles` will not redirect for a url of
17
+ `/articles/2008/09/19/third-post/`.
18
+
19
+ To change this, you may set `Radiant::Config['vapor.use_regexp'] = 'true'`. This will catch any url
20
+ that _begins_ with the given `catch_url`. The `catch_url` in this case is a regular expression, and
21
+ the `redirect_url` may contain substitution variables like $0 (the matched string), $1 (the first
22
+ match group), and so on.
23
+
24
+ You may also nest your flow meters with this setting. Setting `/about` to redirect to `/us` and `/about/team` to
25
+ redirect to `/team` will work. When the `vapor.use_regexp` option is set, the flow meters will be
26
+ compared to the requests in reverse alphabetical order so that longer `catch_url`s will be processed
27
+ first.
28
+
29
+ ## Where and When
30
+
31
+ Vapor is a simple solution to allow your users to manage and edit URL redirection themselves. Long-term
32
+ redirects might be better served by addressing them outside of the application by handling the redirect
33
+ with your web or application server.
34
+
35
+ In order to prevent database access with each request to the site, Vapor loads all URL directives into
36
+ memory when the extension is initialized, when a new flow meter is created, and when a flow meter is
37
+ destroyed.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Jim Gay
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,11 @@
1
+ = Vapor
2
+
3
+ The Vapor extension provides an interface to redirect URLs.
4
+
5
+ Installing Vapor is as easy as any other Radiant extension. Drop it into your vendor/extensions directory and:
6
+
7
+ rake radiant:extensions:vapor:migrate
8
+
9
+ See the HELP_admin.md for more details.
10
+
11
+ built by Saturn Flyer http://www.saturnflyer.com
data/Rakefile ADDED
@@ -0,0 +1,135 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gem|
6
+ gem.name = "radiant-vapor-extension"
7
+ gem.summary = %Q{Provides an interface to redirect URLs in RadiantCMS}
8
+ gem.description = %Q{Provides an interface to redirect URLs in RadiantCMS}
9
+ gem.email = "jim@saturnflyer.com"
10
+ gem.homepage = "http://github.com/saturnflyer/radiant-vapor-extension"
11
+ gem.authors = ["Jim Gay"]
12
+ gem.add_development_dependency "radiant"
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ rescue LoadError
16
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
17
+ end
18
+
19
+ # task :test => :check_dependencies
20
+
21
+ # Determine where the RSpec plugin is by loading the boot
22
+ unless defined? RADIANT_ROOT
23
+ ENV["RAILS_ENV"] = "test"
24
+ case
25
+ when ENV["RADIANT_ENV_FILE"]
26
+ require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
27
+ when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
28
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
29
+ else
30
+ boot_path = "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
31
+ require boot_path if File.exist?(boot_path)
32
+ end
33
+ end
34
+
35
+ require 'rake'
36
+ require 'rake/rdoctask'
37
+ require 'rake/testtask'
38
+
39
+ if defined? RADIANT_ROOT
40
+ rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
41
+ $LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
42
+ end
43
+
44
+ require 'spec/rake/spectask'
45
+
46
+ # Cleanup the RADIANT_ROOT constant so specs will load the environment
47
+ Object.send(:remove_const, :RADIANT_ROOT) if defined? RADIANT_ROOT
48
+
49
+ extension_root = File.expand_path(File.dirname(__FILE__))
50
+
51
+ task :default => :spec
52
+ task :stats => "spec:statsetup"
53
+
54
+ desc "Run all specs in spec directory"
55
+ Spec::Rake::SpecTask.new(:spec) do |t|
56
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
57
+ t.spec_files = FileList['spec/**/*_spec.rb']
58
+ end
59
+
60
+ namespace :spec do
61
+ desc "Run all specs in spec directory with RCov"
62
+ Spec::Rake::SpecTask.new(:rcov) do |t|
63
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
64
+ t.spec_files = FileList['spec/**/*_spec.rb']
65
+ t.rcov = true
66
+ t.rcov_opts = ['--exclude', 'spec', '--rails']
67
+ end
68
+
69
+ desc "Print Specdoc for all specs"
70
+ Spec::Rake::SpecTask.new(:doc) do |t|
71
+ t.spec_opts = ["--format", "specdoc", "--dry-run"]
72
+ t.spec_files = FileList['spec/**/*_spec.rb']
73
+ end
74
+
75
+ [:models, :controllers, :views, :helpers].each do |sub|
76
+ desc "Run the specs under spec/#{sub}"
77
+ Spec::Rake::SpecTask.new(sub) do |t|
78
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
79
+ t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
80
+ end
81
+ end
82
+
83
+ # Setup specs for stats
84
+ task :statsetup do
85
+ require 'code_statistics'
86
+ ::STATS_DIRECTORIES << %w(Model\ specs spec/models)
87
+ ::STATS_DIRECTORIES << %w(View\ specs spec/views)
88
+ ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
89
+ ::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
90
+ ::CodeStatistics::TEST_TYPES << "Model specs"
91
+ ::CodeStatistics::TEST_TYPES << "View specs"
92
+ ::CodeStatistics::TEST_TYPES << "Controller specs"
93
+ ::CodeStatistics::TEST_TYPES << "Helper specs"
94
+ ::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
95
+ end
96
+
97
+ namespace :db do
98
+ namespace :fixtures do
99
+ desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
100
+ task :load => :environment do
101
+ require 'active_record/fixtures'
102
+ ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
103
+ (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
104
+ Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ desc 'Generate documentation for the vapor extension.'
112
+ Rake::RDocTask.new(:rdoc) do |rdoc|
113
+ if File.exist?('VERSION')
114
+ version = File.read('VERSION')
115
+ else
116
+ version = ""
117
+ end
118
+
119
+ rdoc.rdoc_dir = 'rdoc'
120
+ rdoc.title = "Vapor Extension #{version}"
121
+ rdoc.options << '--line-numbers' << '--inline-source'
122
+ rdoc.rdoc_files.include('README')
123
+ rdoc.rdoc_files.include('lib/**/*.rb')
124
+ end
125
+
126
+ # For extensions that are in transition
127
+ desc 'Test the vapor extension.'
128
+ Rake::TestTask.new(:test) do |t|
129
+ t.libs << 'lib'
130
+ t.pattern = 'test/**/*_test.rb'
131
+ t.verbose = true
132
+ end
133
+
134
+ # Load any custom rakefiles for extension
135
+ Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.1.0
@@ -0,0 +1,28 @@
1
+ class Admin::FlowMetersController < ApplicationController
2
+ only_allow_access_to :index, :create, :destroy,
3
+ :when => :admin,
4
+ :denied_url => { :controller => 'pages', :action => 'index' },
5
+ :denied_message => 'You must be an administrator to manage Redirects.'
6
+
7
+ def index
8
+ @flow_meter = FlowMeter.new
9
+ @flow_meters = FlowMeter.find(:all)
10
+ end
11
+
12
+ def create
13
+ @flow_meter = FlowMeter.create!(params[:flow_meter])
14
+ redirect_to admin_flow_meters_url
15
+ rescue ActiveRecord::RecordInvalid => e
16
+ flash[:error] = "#{e.message}"
17
+ redirect_to admin_flow_meters_url
18
+ rescue FlowMeter::DataMismatch => e
19
+ flash[:error] = "#{e.message}"
20
+ redirect_to admin_flow_meters_url
21
+ end
22
+
23
+ def destroy
24
+ @flow_meter = FlowMeter.find(params[:id])
25
+ @flow_meter.destroy
26
+ redirect_to admin_flow_meters_url
27
+ end
28
+ end
@@ -0,0 +1,2 @@
1
+ module FlowMetersHelper
2
+ end
@@ -0,0 +1,59 @@
1
+ require 'cgi'
2
+
3
+ class VaporFlow
4
+ include Vaporizer
5
+
6
+ # Radiant must be restarted if the configuration changes for this setting
7
+ @@use_regexp = nil
8
+ class << self
9
+ def call(env)
10
+ if env["PATH_INFO"].blank?
11
+ return send_to_radiant
12
+ end
13
+ url = env["PATH_INFO"].sub(/^\//,'') #clean off the first slash, like it is stored in the db
14
+ db_escaped_key = ActiveRecord::Base.connection.adapter_name =~ /mysql/i ? '`key`' : 'key'
15
+ sql = "SELECT * FROM config where #{db_escaped_key} = 'vapor.use_regexp'"
16
+ if @@use_regexp.nil?
17
+ config_key = Radiant::Config.connection.select_one(sql)
18
+ @@use_regexp = (config_key && config_key['value'] == 'true') ? true : false
19
+ end
20
+ if @@use_regexp
21
+ catch_with_regexp(url)
22
+ else
23
+ catch_without_regexp(url)
24
+ end
25
+ end
26
+
27
+ def catch_with_regexp(url)
28
+ result = self.send_to_radiant
29
+ FlowMeter.all.sort.reverse.each do |meter|
30
+ key = meter[0]
31
+ value = meter[1]
32
+ if (match = url.match(Regexp.new('^'+key)))
33
+ status = value[1].to_i
34
+ redirect_url = self.match_substitute(value[0], match)
35
+ result = [status, {"Location" => CGI.unescape(local_or_external_path(redirect_url))}, [status.to_s]]
36
+ return result
37
+ break
38
+ end
39
+ end
40
+ return result
41
+ end
42
+
43
+ def catch_without_regexp(url)
44
+ url = url.sub(/\/$/, '') unless url == '/' # drop the trailing slash for lookup
45
+ a_match = FlowMeter.all[url]
46
+ unless a_match.nil?
47
+ status = a_match[1].to_i
48
+ redirect_url = a_match[0]
49
+ [status, {"Location" => CGI.unescape(local_or_external_path(redirect_url))}, [status.to_s]]
50
+ else
51
+ self.send_to_radiant
52
+ end
53
+ end
54
+
55
+ def send_to_radiant
56
+ [404, {'Content-Type' => 'text/html'}, ['Off to Radiant we go!']]
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,137 @@
1
+ class FlowMeter < ActiveRecord::Base
2
+ class DataMismatch < StandardError; end
3
+
4
+ include Vaporizer
5
+
6
+ before_save :set_default_status
7
+ after_validation :clean_catch_url
8
+ after_validation :clean_redirect_url
9
+ after_save :initialize_all
10
+ after_destroy :initialize_all
11
+
12
+ validates_presence_of :catch_url, :on => :create, :message => "can't be blank"
13
+ validates_presence_of :redirect_url, :on => :create, :message => "can't be blank"
14
+
15
+ validates_uniqueness_of :catch_url
16
+
17
+ validate :catch_url_not_restricted
18
+ validate :redirect_url_not_restricted
19
+
20
+ @@all = {}
21
+
22
+ def self.all
23
+ @@all
24
+ end
25
+
26
+ def all
27
+ @@all
28
+ end
29
+
30
+ def all=(all_hash)
31
+ @@all = all_hash
32
+ end
33
+
34
+ def initialize_all
35
+ FlowMeter.initialize_all
36
+ end
37
+
38
+ def self.initialize_all
39
+ @@all = {}
40
+ FlowMeter.find(:all).each do |fm|
41
+ @@all[fm[:catch_url]] = [fm[:redirect_url], fm[:status]]
42
+ end
43
+ end
44
+
45
+ def display_url(att)
46
+ path = self[att]
47
+ if path == '/'
48
+ '/'
49
+ elsif path.match('^https?://')
50
+ path
51
+ else
52
+ radiant_path(self.cleaned_up_path(path))
53
+ end
54
+ end
55
+
56
+ [:catch_url, :redirect_url].each do |att|
57
+ define_method "#{att.to_s}_for_display" do
58
+ display_url(att)
59
+ end
60
+ end
61
+
62
+ def catch_url_not_restricted
63
+ if catch_url =~ %r{\A\/?(admin)}
64
+ errors.add(:catch_url, 'cannot catch the admin url')
65
+ end
66
+ end
67
+
68
+ def redirect_url_not_restricted
69
+ if catch_url == redirect_url
70
+ raise DataMismatch, "Catch URL and Redirect URL may not be the same."
71
+ end
72
+ end
73
+
74
+ def self.find_for_page(page)
75
+ if Radiant::Config['vapor.use_regexp'] == 'true'
76
+ match = catch_url_match_with_regexp(page.url)
77
+ else
78
+ match = catch_url_match(page.url)
79
+ end
80
+ match = self.find_by_catch_url(match) if match
81
+ end
82
+
83
+ def self.redirect_url_for_page(page)
84
+ if Radiant::Config['vapor.use_regexp'] == true
85
+ FlowMeter.match_for_page_with_regexp(page).to_s
86
+ else
87
+ FlowMeter.match_for_page(page).to_s
88
+ end
89
+ end
90
+
91
+ protected
92
+
93
+ def self.match_for_page(page)
94
+ url = page.url.sub(/\/$/, '') unless url == '/' # drop the trailing slash for lookup
95
+ url = url.sub(/^\//, '') unless url == '/'
96
+ a_match = FlowMeter.all[url]
97
+ unless a_match.nil?
98
+ redirect_url = a_match[0]
99
+ redirect_url = '/' + redirect_url unless redirect_url.match('^https?://') || redirect_url == '/'
100
+ return redirect_url
101
+ end
102
+ nil
103
+ end
104
+
105
+ def self.match_for_page_with_regexp(page)
106
+ redirect_url = nil
107
+ FlowMeter.all.sort.reverse.each do |meter|
108
+ key = meter[0]
109
+ value = meter[1]
110
+ if (match = url.match(Regexp.new('^'+key)))
111
+ redirect_url = match_substitute(value[0], match)
112
+ return redirect_url
113
+ break
114
+ end
115
+ end
116
+ redirect_url
117
+ end
118
+
119
+ def set_default_status
120
+ self.status = '307 Temporarily Moved' if self.status.blank?
121
+ end
122
+
123
+ def clean_catch_url
124
+ clean_url(:catch_url) unless catch_url.blank?
125
+ end
126
+
127
+ def clean_redirect_url
128
+ clean_url(:redirect_url) unless redirect_url.blank? or redirect_url.match('^http://')
129
+ end
130
+
131
+ def clean_url(att)
132
+ if !self[att].blank? and !self[att].nil?
133
+ path = self[att]
134
+ self.update_attribute(att, self.cleaned_up_path(path))
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,25 @@
1
+ .outset
2
+ - form_for @flow_meter, :url => admin_flow_meters_path do |f|
3
+ %table.index
4
+ %thead
5
+ %tr
6
+ %th Catch URL
7
+ %th Redirect URL
8
+ %th Status
9
+ %th Action
10
+ %tbody
11
+ - @flow_meters.each do |flow_meter|
12
+ %tr
13
+ %td= flow_meter.catch_url_for_display
14
+ %td= flow_meter.redirect_url_for_display
15
+ %td= flow_meter.status
16
+ %td= link_to "Delete", admin_flow_meter_path(flow_meter), :method => :delete
17
+ %tr
18
+ %td= f.text_field :catch_url
19
+ %td= f.text_field :redirect_url
20
+ %td= f.select :status, ['307 Temporary Redirect','302 Found','301 Moved Permanently'].reverse
21
+ %td= submit_tag "Create"
22
+ %p With redirects you can create URLs that will point to content elsewhere on your site. Your 'Catch URL' is the part of the URL that will be processed to redirect to the final destination of your 'Redirect URL'.
23
+ - if config['vapor.use_regexp'] == 'true'
24
+ %p If the incoming request begins with any of your defined 'Catch URLs', the visitor will be redirected to the corresponding 'Redirect URL'. This means that you may protect an entire tree of your site simply by specifying the beginning of the URL.
25
+ %p You may use a full url for the 'Redirect URL' to direct your visitors to another site.
@@ -0,0 +1,4 @@
1
+ - admin_help do
2
+ %h4 Slugs and Redirects
3
+ %p= "You've got the Vapor extension installed which allows you to create false pages or false slugs. The #{link_to 'Redirects', admin_flow_meters_path} tab provides you with an area where you can create a slug, or url that will redirect your site visitors to another location."
4
+ %p So if you want your 'About Us' page to be found at yoursite.com/about and also at yoursite.com/about-us then you can create your page with 'about-us' as the slug, and create a redirect from '/about' to '/about-us'.
@@ -0,0 +1,30 @@
1
+ %tr.node{:id => "page-#{page.id}", :class =>"level-#{level}#{children_class}#{virtual_class}"}
2
+ - render_region :node, :locals => {:page => page, :level => level, :simple => simple} do |node|
3
+ - node.title_column do
4
+ %td.page{:style => "padding-left: #{padding_left(level)}px"}
5
+ %span.w1
6
+ - if simple
7
+ = icon
8
+ = node_title
9
+ - else
10
+ = expander + link_to("#{icon} #{node_title}", edit_admin_page_url(page), :title => page.url)
11
+ = page_type
12
+ = spinner
13
+ - flow_meter = page.flow_meter
14
+ - if flow_meter
15
+ %small.info
16
+ Redirects to
17
+ = link_to flow_meter.redirect_url_for_display, flow_meter.redirect_url_for_display
18
+ - node.status_column do
19
+ - unless simple
20
+ %td.status{:class => "#{page.status.name.downcase}-status"}= page.status.name
21
+ - node.add_child_column do
22
+ - unless simple
23
+ %td.add-child= link_to image('add-child', :alt => 'add child'), new_admin_page_child_url(page)
24
+ - node.remove_column do
25
+ - unless simple
26
+ %td.remove= link_to image('remove', :alt => 'remove page'), remove_admin_page_url(page)
27
+
28
+ - if expanded
29
+ - page.children.each do |child|
30
+ = render_node child, :level => level + 1, :simple => simple
@@ -0,0 +1,9 @@
1
+ - if !@page.new_record? && (flow_meter = @page.flow_meter)
2
+ %p
3
+ Visitors to this page will redirected to
4
+ = link_to flow_meter.redirect_url_for_display, flow_meter.local_or_external_redirect_url
5
+ - if admin?
6
+ - unless config['vapor.use_regexp']
7
+ = link_to "Remove this", admin_flow_meter_path(flow_meter), :method => :delete
8
+ - else
9
+ = link_to "Manage your traffic flow...", admin_flow_meters_path
@@ -0,0 +1,15 @@
1
+ class CreateFlowMeters < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :flow_meters do |t|
4
+ t.string :catch_url
5
+ t.string :redirect_url
6
+ t.string :status, :default => '307'
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+
12
+ def self.down
13
+ drop_table :flow_meters
14
+ end
15
+ end
data/lib/page_vapor.rb ADDED
@@ -0,0 +1,12 @@
1
+ module PageVapor
2
+ def self.included(base)
3
+ base.class_eval do
4
+ include InstanceMethods
5
+ end
6
+ end
7
+ module InstanceMethods
8
+ def flow_meter
9
+ FlowMeter.find_for_page(self)
10
+ end
11
+ end
12
+ end
File without changes
@@ -0,0 +1,28 @@
1
+ namespace :radiant do
2
+ namespace :extensions do
3
+ namespace :vapor do
4
+
5
+ desc "Runs the migration of the Vapor extension"
6
+ task :migrate => :environment do
7
+ require 'radiant/extension_migrator'
8
+ if ENV["VERSION"]
9
+ VaporExtension.migrator.migrate(ENV["VERSION"].to_i)
10
+ else
11
+ VaporExtension.migrator.migrate
12
+ end
13
+ end
14
+
15
+ desc "Copies public assets of the Vapor to the instance public/ directory."
16
+ task :update => :environment do
17
+ is_svn_or_dir = proc {|path| path =~ /\.svn/ || File.directory?(path) }
18
+ Dir[VaporExtension.root + "/public/**/*"].reject(&is_svn_or_dir).each do |file|
19
+ path = file.sub(VaporExtension.root, '')
20
+ directory = File.dirname(path)
21
+ puts "Copying #{path}..."
22
+ mkdir_p RAILS_ROOT + directory
23
+ cp file, RAILS_ROOT + path
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
data/lib/vaporizer.rb ADDED
@@ -0,0 +1,71 @@
1
+ module Vaporizer
2
+ def self.included(klass)
3
+ klass.instance_eval do
4
+ extend ClassMethods
5
+ include InstanceMethods
6
+ end
7
+ end
8
+
9
+ module ClassMethods
10
+ def catch_url_match_with_regexp(url)
11
+ result = nil
12
+ FlowMeter.all.sort.reverse.each do |meter|
13
+ prepared_url = cleaned_up_path(meter[0])
14
+ prepared_url = radiant_path(prepared_url)
15
+ if (match = url.match(Regexp.new('^'+prepared_url)))
16
+ return meter[0]
17
+ break
18
+ end
19
+ end
20
+ return result
21
+ end
22
+
23
+ def catch_url_match(url)
24
+ url = cleaned_up_path(url)
25
+ a_match = FlowMeter.all[url]
26
+ a_match ? url : nil
27
+ end
28
+
29
+ def cleaned_up_path(path)
30
+ new_path = path.gsub(%r{//+},'/').gsub(%r{\s+},'')
31
+ new_path.gsub!(%r{\/$},'') unless new_path == '/'
32
+ new_path.gsub!(%r{^/},'') unless new_path == '/'
33
+ new_path
34
+ end
35
+
36
+ def radiant_path(path)
37
+ return path if path == '/'
38
+ '/' + path
39
+ end
40
+
41
+ def local_or_external_path(path)
42
+ path.match(/https?:\/\//) ? path : self.radiant_path(path)
43
+ end
44
+
45
+ def match_substitute(string, match)
46
+ string.gsub(/\$([`&0-9'$])/) do |sub|
47
+ case $1
48
+ when "`": match.pre_match
49
+ when "&": match[0]
50
+ when "0".."9": puts $1.to_i; match[$1.to_i]
51
+ when "'": match.post_match
52
+ when "$": '$'
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ module InstanceMethods
59
+ def local_or_external_redirect_url
60
+ self.class.local_or_external_path(redirect_url)
61
+ end
62
+
63
+ def cleaned_up_path(path)
64
+ self.class.cleaned_up_path(path)
65
+ end
66
+
67
+ def radiant_path(path)
68
+ self.class.radiant_path(path)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,80 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Admin::FlowMetersController do
4
+ dataset :users
5
+
6
+ before(:each) do
7
+ login_as :admin
8
+ end
9
+
10
+ describe "/admin/flow_meters with GET" do
11
+ before(:each) do
12
+ @flow_meter = mock_model(FlowMeter)
13
+ @new_flow_meter = mock_model(FlowMeter)
14
+ @flow_meters = [@flow_meter]
15
+ FlowMeter.stub!(:find).with(:all).and_return(@flow_meters)
16
+ FlowMeter.stub!(:new).and_return(@new_flow_meter)
17
+ get :index
18
+ end
19
+
20
+ it "should render the admin/index template" do
21
+ response.should render_template('index')
22
+ end
23
+
24
+ it "should get all flow_meters" do
25
+ assigns[:flow_meters].should == [@flow_meter]
26
+ end
27
+
28
+ it "should get a new flow_meter" do
29
+ assigns[:flow_meter].should == @new_flow_meter
30
+ end
31
+ end
32
+ describe "/admin/flow_meters with valid POST" do
33
+ before(:each) do
34
+ @flow_meter = FlowMeter.create!(:catch_url => 'this', :redirect_url => 'that', :status => '302 Found')
35
+ @flow_meter_count = FlowMeter.count
36
+ post :create, :flow_meter => {:catch_url => 'other', :redirect_url => 'that'}
37
+ end
38
+
39
+ it "should increase the flow_meter_count by 1" do
40
+ FlowMeter.count.should > @flow_meter_count
41
+ end
42
+
43
+ it "should redirect to the index" do
44
+ response.should redirect_to('/admin/flow_meters')
45
+ end
46
+ end
47
+ describe "/admin/flow_meters with invalid POST" do
48
+ before(:each) do
49
+ post :create, :flow_meter => {:catch_url => 'admin', :redirect_url => 'that'}
50
+ end
51
+
52
+ it "should assign the error message to the flash" do
53
+ flash[:error].should_not be_blank
54
+ end
55
+
56
+ it "should redirect to the index" do
57
+ response.should redirect_to('/admin/flow_meters')
58
+ end
59
+ end
60
+ describe "/admin/flow_meters/:id with DELETE" do
61
+ before(:each) do
62
+ @flow_meter = FlowMeter.create!(:catch_url => 'this', :redirect_url => 'that', :status => '302 Found')
63
+ end
64
+
65
+ it "should find the flow_meter" do
66
+ FlowMeter.stub!(:find).with(:all).and_return([@flow_meter])
67
+ FlowMeter.should_receive(:find).with(@flow_meter.id.to_s).and_return(@flow_meter)
68
+ delete :destroy, :id => @flow_meter.id
69
+ end
70
+ it "should reduce the flow_meter count by 1" do
71
+ @flow_meter_count = FlowMeter.count
72
+ delete :destroy, :id => @flow_meter.id
73
+ FlowMeter.count.should < @flow_meter_count
74
+ end
75
+ it "should redirect to the index" do
76
+ delete :destroy, :id => @flow_meter.id
77
+ response.should redirect_to('/admin/flow_meters')
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,28 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe VaporFlow do
4
+ describe "radiant_path" do
5
+ it "should return the given path prepended with a /" do
6
+ VaporFlow.radiant_path('slashy').should == '/slashy'
7
+ end
8
+ end
9
+ describe "local_or_external_path" do
10
+ it "should return the given path if it begins with 'http'" do
11
+ VaporFlow.local_or_external_path('http://saturnflyer.com').should == 'http://saturnflyer.com'
12
+ end
13
+ it "should return the given path if it begins with 'https'" do
14
+ VaporFlow.local_or_external_path('https://saturnflyer.com').should == 'https://saturnflyer.com'
15
+ end
16
+ it "should return the radiant_path if the given path does not begin with http" do
17
+ VaporFlow.local_or_external_path('about/the/site').should == VaporFlow.radiant_path('about/the/site')
18
+ end
19
+ end
20
+ describe "send_to_radiant" do
21
+ it "should return an array" do
22
+ VaporFlow.send_to_radiant.kind_of?(Array).should be_true
23
+ end
24
+ it "should have 404 as the first item in the array" do
25
+ VaporFlow.send_to_radiant.first.should == 404
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,146 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe FlowMeter do
4
+ dataset :pages
5
+ before(:each) do
6
+ # FlowMeter.all = {}
7
+ @flow_meter = FlowMeter.new(:catch_url => "/stuff", :redirect_url => '/things', :status => '')
8
+ end
9
+
10
+ it "should err without a catch_url" do
11
+ @flow_meter.catch_url = nil
12
+ @flow_meter.valid?
13
+ @flow_meter.errors.on(:catch_url).should match(/can't be blank/)
14
+ end
15
+
16
+ it "should err without a redirect_url" do
17
+ @flow_meter.redirect_url = nil
18
+ @flow_meter.valid?
19
+ @flow_meter.errors.on(:redirect_url).should match(/can't be blank/)
20
+ end
21
+
22
+ it "should set '307 Temporarily Moved' as the status if created with no status" do
23
+ @flow_meter.save!
24
+ @flow_meter.status.should == '307 Temporarily Moved'
25
+ end
26
+
27
+ it "should err with a catch_url beginning with 'admin'" do
28
+ @flow_meter.catch_url = "admin"
29
+ @flow_meter.valid?
30
+ @flow_meter.errors.on(:catch_url).should match(/cannot catch the admin url/)
31
+ end
32
+
33
+ it "should err with a catch_url beginning with '/admin'" do
34
+ @flow_meter.catch_url = "/admin"
35
+ @flow_meter.valid?
36
+ @flow_meter.errors.on(:catch_url).should match(/cannot catch the admin url/)
37
+ end
38
+
39
+ it "should err with a non-unique catch_url" do
40
+ @flow_meter.save
41
+ @flow_meter2 = FlowMeter.new(:catch_url => @flow_meter.catch_url, :redirect_url => '/other')
42
+ @flow_meter2.valid?
43
+ @flow_meter2.errors.on(:catch_url).should match(/has already been taken/)
44
+ end
45
+
46
+ it "should remove the first character from catch_url beginning with a slash" do
47
+ @flow_meter.save
48
+ @flow_meter.catch_url.should == 'stuff'
49
+ end
50
+
51
+ it "should remove the first character from redirect_url beginning with a slash" do
52
+ @flow_meter.save
53
+ @flow_meter.redirect_url.should == 'things'
54
+ end
55
+
56
+ it "should remove consecutive slashes from the catch_url before saving" do
57
+ @flow_meter.catch_url = "///slasher"
58
+ @flow_meter.save
59
+ @flow_meter.catch_url.should == 'slasher'
60
+ end
61
+
62
+ it "should remove consecutive slashes from the redirect_url before saving" do
63
+ @flow_meter.redirect_url = "///chop"
64
+ @flow_meter.save
65
+ @flow_meter.redirect_url.should == 'chop'
66
+ end
67
+
68
+ it "should remove a trailing slash from the catch_url before saving" do
69
+ @flow_meter.catch_url = '/no_trailing_slash/'
70
+ @flow_meter.save
71
+ @flow_meter.catch_url.should == 'no_trailing_slash'
72
+ end
73
+
74
+ it "should remove all whitespace from catch_url before saving" do
75
+ @flow_meter.catch_url = "hello there"
76
+ @flow_meter.save
77
+ @flow_meter.catch_url.should == 'hellothere'
78
+ end
79
+
80
+ it "should remove all whitespace from redirect_url before saving" do
81
+ @flow_meter.redirect_url = "how are you"
82
+ @flow_meter.save
83
+ @flow_meter.redirect_url.should == 'howareyou'
84
+ end
85
+
86
+ it "should allow '/' as the redirect_url" do
87
+ @flow_meter.redirect_url = "/"
88
+ @flow_meter.save
89
+ @flow_meter.redirect_url.should == '/'
90
+ end
91
+
92
+ it "should allow a redirect_url formatted like 'http://www.saturnflyer.com/'" do
93
+ @flow_meter.redirect_url = 'http://www.saturnflyer.com/'
94
+ @flow_meter.save!
95
+ @flow_meter.redirect_url.should == 'http://www.saturnflyer.com/'
96
+ end
97
+
98
+ it "should provide a catch_url_for_display which includes a leading slash" do
99
+ @flow_meter.catch_url_for_display.should == '/stuff'
100
+ end
101
+
102
+ it "should provide a redirect_url_for_display which includes a leading slash" do
103
+ @flow_meter.redirect_url_for_display.should == '/things'
104
+ end
105
+
106
+ it "should provide the actual redirect_url_for_display if it begins with 'http://'" do
107
+ @flow_meter.redirect_url = "http://www.saturnflyer.com"
108
+ @flow_meter.redirect_url_for_display.should == "http://www.saturnflyer.com"
109
+ end
110
+
111
+ it "should err with 'Catch URL and Redirect URL may not be the same.' when given a catch_url that matches the redirect_url" do
112
+ @flow_meter.redirect_url = "/stuff"
113
+ lambda {@flow_meter.save!}.should raise_error(FlowMeter::DataMismatch, "Catch URL and Redirect URL may not be the same.")
114
+ end
115
+
116
+ it "should load save all flow_meters into FlowMeter.all, a Hash with the catch_url as the key, and an array of redirect_url and status as the value" do
117
+ FlowMeter.destroy_all
118
+ @flow_meter.save
119
+ FlowMeter.all.should == {'stuff' => ['things', '307 Temporarily Moved']}
120
+ end
121
+
122
+ it "should reload FlowMeter.all after destroying a flow_meter" do
123
+ @flow_meter.save
124
+ @flow_meter2 = FlowMeter.new(:catch_url => 'old', :redirect_url => 'new')
125
+ @flow_meter2.save
126
+ @flow_meter.destroy
127
+ FlowMeter.all.should == {'old' => ['new', '307']}
128
+ end
129
+
130
+ describe "self.find_for_page" do
131
+ it "should return nil if no flow_meter matches for the page" do
132
+ FlowMeter.find_for_page(pages(:home)).should be_nil
133
+ end
134
+ it "should return the first flow_meter found that matches the page url" do
135
+ @redirector = FlowMeter.create!({:catch_url => '/first', :redirect_url => '/another', :status => '307'})
136
+ FlowMeter.find_for_page(pages(:first)).should == @redirector
137
+ end
138
+ describe "while vapor.use_regexp" do
139
+ it "should return the first flow_meter found that matches the page url" do
140
+ Radiant::Config['vapor.use_regexp'] = 'true'
141
+ @redirector = FlowMeter.create!({:catch_url => '/fi\w', :redirect_url => '/another', :status => '307'})
142
+ FlowMeter.find_for_page(pages(:first)).should == @redirector
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,18 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Page do
4
+ dataset :users_and_pages
5
+
6
+ before(:each) do
7
+ @redirector = FlowMeter.create!({:catch_url => '/first', :redirect_url => '/another', :status => '307'})
8
+ end
9
+
10
+ describe 'flow_meter' do
11
+ it "should return nil if none is found" do
12
+ pages(:home).flow_meter.should be_nil
13
+ end
14
+ it "should return the first flow_meter found which matches the page url" do
15
+ pages(:first).flow_meter.should == @redirector
16
+ end
17
+ end
18
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,6 @@
1
+ --colour
2
+ --format
3
+ progress
4
+ --loadby
5
+ mtime
6
+ --reverse
@@ -0,0 +1,37 @@
1
+ unless defined? RADIANT_ROOT
2
+ ENV["RAILS_ENV"] = "test"
3
+ case
4
+ when ENV["RADIANT_ENV_FILE"]
5
+ require ENV["RADIANT_ENV_FILE"]
6
+ when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
7
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../../")}/config/environment"
8
+ else
9
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../")}/config/environment"
10
+ end
11
+ end
12
+ require "#{RADIANT_ROOT}/spec/spec_helper"
13
+
14
+ if File.directory?(File.dirname(__FILE__) + "/scenarios")
15
+ Scenario.load_paths.unshift File.dirname(__FILE__) + "/scenarios"
16
+ end
17
+ if File.directory?(File.dirname(__FILE__) + "/matchers")
18
+ Dir[File.dirname(__FILE__) + "/matchers/*.rb"].each {|file| require file }
19
+ end
20
+
21
+ Spec::Runner.configure do |config|
22
+ # config.use_transactional_fixtures = true
23
+ # config.use_instantiated_fixtures = false
24
+ # config.fixture_path = RAILS_ROOT + '/spec/fixtures'
25
+
26
+ # You can declare fixtures for each behaviour like this:
27
+ # describe "...." do
28
+ # fixtures :table_a, :table_b
29
+ #
30
+ # Alternatively, if you prefer to declare them only once, you can
31
+ # do so here, like so ...
32
+ #
33
+ # config.global_fixtures = :table_a, :table_b
34
+ #
35
+ # If you declare global fixtures, be aware that they will be declared
36
+ # for all of your examples, even those that don't use them.
37
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class RadiantVaporTest < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'radiant-vapor'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,37 @@
1
+ require_dependency 'application_controller'
2
+
3
+ class VaporExtension < Radiant::Extension
4
+ version "#{File.read(File.expand_path(File.dirname(__FILE__)) + '/VERSION')}"
5
+ description "Manage redirects without creating useless pages"
6
+ url "http://saturnflyer.com/"
7
+
8
+ define_routes do |map|
9
+ map.namespace :admin do |admin|
10
+ admin.resources 'flow_meters', :only => [:index, :create, :destroy]
11
+ end
12
+ end
13
+
14
+ def activate
15
+ unless respond_to?(:tab)
16
+ admin.tabs.add "Redirects", "/admin/flow_meters", :after => "Layouts", :visibility => [:admin]
17
+ else
18
+ tab 'Content' do
19
+ add_item 'Redirects', '/admin/flow_meters'
20
+ end
21
+ end
22
+ FlowMeter.initialize_all if ActiveRecord::Base.connection.tables.include?('flow_meters')
23
+
24
+ Page.class_eval { include PageVapor }
25
+
26
+ admin.pages.edit.add :form, 'vapor_details', :before => 'edit_title'
27
+
28
+ if admin.respond_to? :help
29
+ admin.help.index.add :page_details, 'slug_redirect', :after => 'slug'
30
+ end
31
+ end
32
+
33
+ def deactivate
34
+
35
+ end
36
+
37
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: radiant-vapor-extension
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jim Gay
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-04 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: radiant
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: Provides an interface to redirect URLs in RadiantCMS
26
+ email: jim@saturnflyer.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.rdoc
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - HELP_admin.md
38
+ - LICENSE
39
+ - README.rdoc
40
+ - Rakefile
41
+ - VERSION
42
+ - app/controllers/admin/flow_meters_controller.rb
43
+ - app/helpers/flow_meters_helper.rb
44
+ - app/metal/vapor_flow.rb
45
+ - app/models/flow_meter.rb
46
+ - app/views/admin/flow_meters/index.html.haml
47
+ - app/views/admin/help/_slug_redirect.html.haml
48
+ - app/views/admin/pages/_node.html.haml
49
+ - app/views/admin/pages/_vapor_details.html.haml
50
+ - db/migrate/001_create_flow_meters.rb
51
+ - lib/page_vapor.rb
52
+ - lib/radiant-vapor-extension.rb
53
+ - lib/tasks/vapor_extension_tasks.rake
54
+ - lib/vaporizer.rb
55
+ - spec/controllers/admin/flow_meters_controller_spec.rb
56
+ - spec/metal/vapor_flow_spec.rb
57
+ - spec/models/flow_meter_spec.rb
58
+ - spec/models/page_spec.rb
59
+ - spec/spec.opts
60
+ - spec/spec_helper.rb
61
+ - test/radiant-vapor_test.rb
62
+ - test/test_helper.rb
63
+ - vapor_extension.rb
64
+ has_rdoc: true
65
+ homepage: http://github.com/saturnflyer/radiant-vapor-extension
66
+ licenses: []
67
+
68
+ post_install_message:
69
+ rdoc_options:
70
+ - --charset=UTF-8
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: "0"
84
+ version:
85
+ requirements: []
86
+
87
+ rubyforge_project:
88
+ rubygems_version: 1.3.5
89
+ signing_key:
90
+ specification_version: 3
91
+ summary: Provides an interface to redirect URLs in RadiantCMS
92
+ test_files:
93
+ - spec/controllers/admin/flow_meters_controller_spec.rb
94
+ - spec/metal/vapor_flow_spec.rb
95
+ - spec/models/flow_meter_spec.rb
96
+ - spec/models/page_spec.rb
97
+ - spec/spec_helper.rb
98
+ - test/radiant-vapor_test.rb
99
+ - test/test_helper.rb