radiant-concurrent_draft-extension 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HELP.rdoc +32 -0
- data/README.textile +52 -0
- data/Rakefile +136 -0
- data/VERSION +1 -0
- data/app/views/admin/_draft_controls.html.haml +85 -0
- data/app/views/admin/_edit_buttons.html.haml +10 -0
- data/app/views/admin/layouts/_edit_content.html.haml +5 -0
- data/app/views/admin/page_parts/_page_part.html.haml +20 -0
- data/app/views/admin/pages/_edit_layout_and_type.html.haml +11 -0
- data/app/views/admin/pages/_published_meta.html.haml +4 -0
- data/app/views/admin/snippets/_edit_content.html.haml +5 -0
- data/app/views/admin/users/_edit_roles.html.haml +26 -0
- data/concurrent_draft_extension.rb +30 -0
- data/config/initializers/radiant_config.rb +3 -0
- data/config/locales/en.yml +11 -0
- data/config/routes.rb +9 -0
- data/db/migrate/001_update_schemata.rb +29 -0
- data/db/migrate/002_create_draft_page_elements.rb +10 -0
- data/db/migrate/003_add_publisher_role.rb +9 -0
- data/lib/concurrent_draft/admin_controller_extensions.rb +52 -0
- data/lib/concurrent_draft/helper_extensions.rb +39 -0
- data/lib/concurrent_draft/model_extensions.rb +77 -0
- data/lib/concurrent_draft/page_extensions.rb +28 -0
- data/lib/concurrent_draft/site_controller_extensions.rb +16 -0
- data/lib/concurrent_draft/tags.rb +56 -0
- data/lib/tasks/concurrent_draft_extension_tasks.rake +46 -0
- data/public/images/admin/cancel.png +0 -0
- data/public/images/admin/clock.png +0 -0
- data/public/images/admin/page_delete.png +0 -0
- data/public/images/admin/page_edit.png +0 -0
- data/public/images/admin/page_refresh.png +0 -0
- data/public/images/admin/tick.png +0 -0
- data/public/javascripts/admin/concurrent_draft.js +72 -0
- data/public/stylesheets/admin/concurrent_draft.css +74 -0
- data/spec/controllers/admin_controller_extensions_spec.rb +184 -0
- data/spec/controllers/site_controller_extensions_spec.rb +31 -0
- data/spec/matchers/concurrent_draft_matcher.rb +11 -0
- data/spec/models/model_extensions_spec.rb +114 -0
- data/spec/models/page_extensions_spec.rb +56 -0
- data/spec/models/tags_spec.rb +43 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +38 -0
- data/test/helpers/caching_test_helper.rb +42 -0
- data/test/helpers/page_part_test_helper.rb +49 -0
- data/test/helpers/page_test_helper.rb +77 -0
- data/vendor/plugins/12_hour_time/CHANGELOG +2 -0
- data/vendor/plugins/12_hour_time/README +16 -0
- data/vendor/plugins/12_hour_time/Rakefile +22 -0
- data/vendor/plugins/12_hour_time/init.rb +1 -0
- data/vendor/plugins/12_hour_time/lib/12_hour_time.rb +78 -0
- data/vendor/plugins/12_hour_time/test/12_hour_time_test.rb +52 -0
- data/vendor/plugins/12_hour_time/test/test_helper.rb +3 -0
- metadata +143 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe "ConcurrentDraft::Tags" do
|
4
|
+
dataset :pages, :snippets
|
5
|
+
describe '<r:content>' do
|
6
|
+
before :each do
|
7
|
+
@page = pages(:home)
|
8
|
+
@page.part(:body).update_attribute('draft_content', 'Draft body.')
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should render the published content on the live site" do
|
12
|
+
@page.should render('<r:content />').as("Hello world!")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should render the draft content on the dev site" do
|
16
|
+
@page.should render('<r:content />').as("Draft body.").on('dev.host')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "<r:snippet>" do
|
21
|
+
before :each do
|
22
|
+
@snippet = snippets(:first)
|
23
|
+
@snippet.update_attribute(:draft_content, 'Draft content.')
|
24
|
+
@page = pages(:home)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should render published snippet content on the live site" do
|
28
|
+
@page.should render('<r:snippet name="first" />').as('test')
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should render draft snippet content on the dev site" do
|
32
|
+
@page.should render('<r:snippet name="first" />').as('Draft content.').on('dev.host')
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should promote the draft body if it is scheduled" do
|
36
|
+
@snippet.update_attribute(:draft_promotion_scheduled_at, 1.second.from_now)
|
37
|
+
sleep 2
|
38
|
+
@page.should render('<r:snippet name="first" />').as('Draft content.')
|
39
|
+
@snippet.reload
|
40
|
+
@snippet.should be_has_draft_promoted
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
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
|
+
Dataset::Resolver.default << (File.dirname(__FILE__) + "/datasets")
|
15
|
+
if File.directory?(File.dirname(__FILE__) + "/scenarios")
|
16
|
+
Scenario.load_paths.unshift File.dirname(__FILE__) + "/scenarios"
|
17
|
+
end
|
18
|
+
if File.directory?(File.dirname(__FILE__) + "/matchers")
|
19
|
+
Dir[File.dirname(__FILE__) + "/matchers/*.rb"].each {|file| require file }
|
20
|
+
end
|
21
|
+
|
22
|
+
Spec::Runner.configure do |config|
|
23
|
+
# config.use_transactional_fixtures = true
|
24
|
+
# config.use_instantiated_fixtures = false
|
25
|
+
# config.fixture_path = RAILS_ROOT + '/spec/fixtures'
|
26
|
+
|
27
|
+
# You can declare fixtures for each behaviour like this:
|
28
|
+
# describe "...." do
|
29
|
+
# fixtures :table_a, :table_b
|
30
|
+
#
|
31
|
+
# Alternatively, if you prefer to declare them only once, you can
|
32
|
+
# do so here, like so ...
|
33
|
+
#
|
34
|
+
# config.global_fixtures = :table_a, :table_b
|
35
|
+
#
|
36
|
+
# If you declare global fixtures, be aware that they will be declared
|
37
|
+
# for all of your examples, even those that don't use them.
|
38
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class FakeResponseCache
|
2
|
+
attr_accessor :expired_path, :expired_paths
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@expired_paths = []
|
6
|
+
@cached_responses = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def clear
|
10
|
+
@cached_responses.clear
|
11
|
+
@cleared = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def cache_response(path, response)
|
15
|
+
@cached_responses[path] = response
|
16
|
+
response
|
17
|
+
end
|
18
|
+
|
19
|
+
def update_response(path, response)
|
20
|
+
if r = @cached_response[path]
|
21
|
+
response.headers.merge!(r.headers)
|
22
|
+
response.body = r.body
|
23
|
+
end
|
24
|
+
response
|
25
|
+
end
|
26
|
+
|
27
|
+
def expire_response(path)
|
28
|
+
@expired_paths << path
|
29
|
+
@expired_path = path
|
30
|
+
end
|
31
|
+
|
32
|
+
def response_cached?(path)
|
33
|
+
@cached_responses.keys.include?(path)
|
34
|
+
end
|
35
|
+
|
36
|
+
def cleared?
|
37
|
+
!!@cleared
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module CachingTestHelper
|
42
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module PagePartTestHelper
|
2
|
+
VALID_PAGE_PART_PARAMS = {
|
3
|
+
:name => 'custom',
|
4
|
+
:content => 'Some simple content.',
|
5
|
+
:page_id => '1'
|
6
|
+
}
|
7
|
+
|
8
|
+
def part_params(options = {})
|
9
|
+
params = VALID_PAGE_PART_PARAMS.dup
|
10
|
+
params.delete(:page_id)
|
11
|
+
params.merge!(:name => @part_name) if @part_name
|
12
|
+
params.merge!(options)
|
13
|
+
params
|
14
|
+
end
|
15
|
+
|
16
|
+
def destroy_test_part(title = @part_name)
|
17
|
+
while part = get_test_part(title) do
|
18
|
+
part.destroy
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_test_part(name = @part_name)
|
23
|
+
PagePart.find_by_name(name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_test_part(name = @part_name)
|
27
|
+
params = part_params
|
28
|
+
params.merge!(:name => name)
|
29
|
+
part = PagePart.new(params)
|
30
|
+
if part.save
|
31
|
+
part
|
32
|
+
else
|
33
|
+
raise "part <#{part.inspect}> could not be saved"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# must be included after PageTestHelper to work
|
38
|
+
def create_test_page(options = {})
|
39
|
+
no_part = options.delete(:no_part)
|
40
|
+
page = super(options)
|
41
|
+
unless no_part
|
42
|
+
part = PagePart.new part_params(:name => 'body', :content => 'test')
|
43
|
+
page.parts << part
|
44
|
+
page.save
|
45
|
+
part.save
|
46
|
+
end
|
47
|
+
page
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
class NoCachePage < Page
|
2
|
+
description 'Turns caching off for testing.'
|
3
|
+
|
4
|
+
def cache?
|
5
|
+
false
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class CustomFileNotFoundPage < FileNotFoundPage
|
10
|
+
end
|
11
|
+
|
12
|
+
class TestPage < Page
|
13
|
+
description 'this is just a test page'
|
14
|
+
|
15
|
+
tag 'test1' do
|
16
|
+
'Hello world!'
|
17
|
+
end
|
18
|
+
|
19
|
+
tag 'test2' do
|
20
|
+
'Another test.'
|
21
|
+
end
|
22
|
+
|
23
|
+
def headers
|
24
|
+
{
|
25
|
+
'cool' => 'beans',
|
26
|
+
'request' => @request.inspect[20..30],
|
27
|
+
'response' => @response.inspect[20..31]
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
class VirtualPage < Page
|
34
|
+
def virtual?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module PageTestHelper
|
40
|
+
|
41
|
+
VALID_PAGE_PARAMS = {
|
42
|
+
:title => 'New Page',
|
43
|
+
:slug => 'page',
|
44
|
+
:breadcrumb => 'New Page',
|
45
|
+
:status_id => '1',
|
46
|
+
:parent_id => nil
|
47
|
+
}
|
48
|
+
|
49
|
+
def page_params(options = {})
|
50
|
+
params = VALID_PAGE_PARAMS.dup
|
51
|
+
params.merge!(:title => @page_title) if @page_title
|
52
|
+
params.merge!(:status_id => '5')
|
53
|
+
params.merge!(options)
|
54
|
+
end
|
55
|
+
|
56
|
+
def destroy_test_page(title = @page_title)
|
57
|
+
while page = get_test_page(title) do
|
58
|
+
page.destroy
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_test_page(title = @page_title)
|
63
|
+
Page.find_by_title(title)
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_test_page(options = {})
|
67
|
+
options[:title] ||= @page_title
|
68
|
+
klass = options.delete(:class_name) || Page
|
69
|
+
klass = Kernel.eval(klass) if klass.kind_of? String
|
70
|
+
page = klass.new page_params(options)
|
71
|
+
if page.save
|
72
|
+
page
|
73
|
+
else
|
74
|
+
raise "page <#{page.inspect}> could not be saved"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
12-HOUR TIME PLUGIN
|
2
|
+
|
3
|
+
Not all of us are adept at those exotic, 24-hour clocks. So for the rest of us,
|
4
|
+
here's a simple plugin that tacks on an AM/PM selector to the DateHelper
|
5
|
+
methods. It also handles the backend, so things are properly stored in 24-hour
|
6
|
+
format.
|
7
|
+
|
8
|
+
USAGE:
|
9
|
+
|
10
|
+
<%= time_select 'event', 'time', {:twelve_hour => true} %>
|
11
|
+
|
12
|
+
Authors: Nick Muerdter (original code)
|
13
|
+
Maurice Aubrey <maurice.aubrey+12hour@gmail.com>
|
14
|
+
|
15
|
+
Homepage:
|
16
|
+
http://code.google.com/p/rails-twelve-hour-time-plugin/
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the 12_hour_time plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the 12_hour_time plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = '12-Hour Time'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "12_hour_time"
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class ActiveRecord::Base
|
2
|
+
def instantiate_time_object_with_ampm(name, values)
|
3
|
+
if values.last < 0
|
4
|
+
ampm = values.pop
|
5
|
+
if ampm == ActionView::Helpers::DateTimeSelector::AM and values[3] == 12
|
6
|
+
values[3] = 0
|
7
|
+
elsif ampm == ActionView::Helpers::DateTimeSelector::PM and values[3] != 12
|
8
|
+
values[3] += 12
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
instantiate_time_object_without_ampm(name, values)
|
13
|
+
end
|
14
|
+
|
15
|
+
alias_method_chain :instantiate_time_object, :ampm
|
16
|
+
end
|
17
|
+
|
18
|
+
module ActionView::Helpers
|
19
|
+
class DateTimeSelector
|
20
|
+
POSITION = {
|
21
|
+
:year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5,
|
22
|
+
:second => 6, :ampm => 7
|
23
|
+
}
|
24
|
+
# XXX would like to do this, but it's frozen
|
25
|
+
# POSITION[:ampm] = 7
|
26
|
+
|
27
|
+
# We give them negative values so can differentiate between normal
|
28
|
+
# date/time values. The way the multi param stuff works, from what I
|
29
|
+
# can see, results in a variable number of fields (if you tell it to
|
30
|
+
# include seconds, for example). So we expect the AM/PM field, if
|
31
|
+
# present, to be last and have a negative value.
|
32
|
+
AM = -1
|
33
|
+
PM = -2
|
34
|
+
|
35
|
+
def select_hour_with_ampm
|
36
|
+
unless @options[:twelve_hour]
|
37
|
+
return select_hour_without_ampm
|
38
|
+
end
|
39
|
+
|
40
|
+
if @options[:use_hidden] || @options[:discard_hour]
|
41
|
+
build_hidden(:hour, hour12)
|
42
|
+
else
|
43
|
+
build_options_and_select(:hour, hour12, :start => 1, :end => 12)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
alias_method_chain :select_hour, :ampm
|
48
|
+
|
49
|
+
def select_ampm
|
50
|
+
selected = hour < 12 ? AM : PM
|
51
|
+
|
52
|
+
# XXX i18n?
|
53
|
+
label = { AM => 'AM', PM => 'PM' }
|
54
|
+
ampm_options = []
|
55
|
+
[AM, PM].each do |meridiem|
|
56
|
+
option = { :value => meridiem }
|
57
|
+
option[:selected] = "selected" if selected == meridiem
|
58
|
+
ampm_options << content_tag(:option, label[meridiem], option) + "\n"
|
59
|
+
end
|
60
|
+
build_select(:ampm, ampm_options.join)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def build_selects_from_types_with_ampm(order)
|
66
|
+
order += [:ampm] if @options[:twelve_hour] and !order.include?(:ampm)
|
67
|
+
build_selects_from_types_without_ampm(order)
|
68
|
+
end
|
69
|
+
|
70
|
+
alias_method_chain :build_selects_from_types, :ampm
|
71
|
+
|
72
|
+
def hour12
|
73
|
+
h12 = hour % 12
|
74
|
+
h12 = 12 if h12 == 0
|
75
|
+
return h12
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Todo: use assert_seelect? Only avail in edge?
|
2
|
+
# http://nubyonrails.com/articles/test-your-helpers
|
3
|
+
|
4
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
5
|
+
|
6
|
+
class String
|
7
|
+
def nstrip
|
8
|
+
self.gsub(/\n+/, '')
|
9
|
+
end
|
10
|
+
|
11
|
+
def nstrip!
|
12
|
+
self.gsub!(/\n+/, '')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class TwelveHourTimeTest < Test::Unit::TestCase
|
17
|
+
include ActionView::Helpers::TagHelper
|
18
|
+
include ActionView::Helpers::FormTagHelper
|
19
|
+
include ActionView::Helpers::FormOptionsHelper
|
20
|
+
include ActionView::Helpers::DateHelper
|
21
|
+
|
22
|
+
def test_24_to_12_hour
|
23
|
+
assert_equal(12, _12_hour(0), "12 AM")
|
24
|
+
assert_equal(1, _12_hour(1), "1 AM")
|
25
|
+
assert_equal(12, _12_hour(12), "12 PM")
|
26
|
+
assert_equal(1, _12_hour(13), "1 PM")
|
27
|
+
assert_equal(12, _12_hour(24), "12 AM")
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_select_hour_12
|
31
|
+
time = Time.parse '2005-07-27 15:03:34'
|
32
|
+
|
33
|
+
options = options_for_select((1..12).to_a.map { |h| "%02d" % h }, "03")
|
34
|
+
expected = select_tag 'date[hour]', options, :id => 'date_hour'
|
35
|
+
actual = select_hour time, :twelve_hour => true
|
36
|
+
assert_equal(expected.nstrip, actual.nstrip, "12 hour select")
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_select_hour_24
|
40
|
+
time = Time.parse '2005-07-27 15:03:34'
|
41
|
+
|
42
|
+
options = options_for_select((0..23).to_a.map { |h| "%02d" % h }, "15")
|
43
|
+
expected = select_tag 'date[hour]', options, :id => 'date_hour'
|
44
|
+
expected.nstrip!
|
45
|
+
|
46
|
+
actual = select_hour time, :twelve_hour => false
|
47
|
+
assert_equal(expected, actual.nstrip, "24 hour select, explicit")
|
48
|
+
|
49
|
+
actual = select_hour time
|
50
|
+
assert_equal(expected, actual.nstrip, "24 hour select, default")
|
51
|
+
end
|
52
|
+
end
|