trusty-snippets-extension 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +7 -0
- data/Rakefile +109 -0
- data/app/controllers/admin/snippets_controller.rb +8 -0
- data/app/helpers/admin/snippets_helper.rb +3 -0
- data/app/models/snippet.rb +23 -0
- data/app/models/snippet_finder.rb +24 -0
- data/app/models/snippet_tags.rb +80 -0
- data/app/views/admin/snippets/_form.html.haml +29 -0
- data/app/views/admin/snippets/_popups.html.haml +4 -0
- data/app/views/admin/snippets/edit.html.haml +11 -0
- data/app/views/admin/snippets/index.html.haml +33 -0
- data/app/views/admin/snippets/new.html.haml +9 -0
- data/app/views/admin/snippets/remove.html.haml +17 -0
- data/config/initializers/radiant_config.rb +3 -0
- data/config/locales/en.yml +16 -0
- data/config/routes.rb +8 -0
- data/cucumber.yml +1 -0
- data/features/admin/configuration.feature +9 -0
- data/features/admin/pagination.feature +27 -0
- data/features/admin/snippets_management.feature +82 -0
- data/features/step_definitions/admin/snippet_steps.rb +25 -0
- data/features/support/env.rb +11 -0
- data/features/support/paths.rb +22 -0
- data/lib/snippets/engine.rb +5 -0
- data/lib/tasks/snippets_extension_tasks.rake +47 -0
- data/lib/trusty-snippets-extension.rb +8 -0
- data/snippets_extension.rb +69 -0
- data/spec/ci/before_script +22 -0
- data/spec/ci/script +2 -0
- data/spec/controllers/admin/snippets_controller_spec.rb +110 -0
- data/spec/datasets/snippets_dataset.rb +44 -0
- data/spec/fixtures/snippet_template.radius +1 -0
- data/spec/lib/radiant/admin_ui_spec.rb +33 -0
- data/spec/models/page_spec.rb +81 -0
- data/spec/models/snippet_finder_spec.rb +20 -0
- data/spec/models/snippet_spec.rb +59 -0
- data/spec/models/user_action_observer_spec.rb +25 -0
- data/spec/snippets_spec_helper.rb +36 -0
- data/spec/spec.opts +6 -0
- data/trusty-snippets-extension.gemspec +26 -0
- metadata +120 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
Feature: Managing snippets
|
2
|
+
In order to share content between layouts and pages, as a designer I want to
|
3
|
+
manage a collection of snippets
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given I am logged in as "designer"
|
7
|
+
|
8
|
+
Scenario: List snippets
|
9
|
+
When I follow "Design" within "#navigation"
|
10
|
+
And I follow "Snippets"
|
11
|
+
Then I should see "first"
|
12
|
+
And I should see "another"
|
13
|
+
And I should see "markdown"
|
14
|
+
# And a host of others
|
15
|
+
|
16
|
+
Scenario: Create a snippet
|
17
|
+
When I follow "Design" within "#navigation"
|
18
|
+
And I follow "Snippets"
|
19
|
+
And I follow "New Snippet"
|
20
|
+
And I fill in "Name" with "Mine"
|
21
|
+
And I fill in "Body" with "My snippet"
|
22
|
+
And I press "Create Snippet"
|
23
|
+
Then I should be on the snippets list
|
24
|
+
And I should see "Mine"
|
25
|
+
|
26
|
+
Scenario: Display form errors
|
27
|
+
When I follow "Design" within "#navigation"
|
28
|
+
And I follow "Snippets"
|
29
|
+
And I follow "New Snippet"
|
30
|
+
And I fill in "Body" with "My snippet"
|
31
|
+
And I press "Create Snippet"
|
32
|
+
Then I should see an error message
|
33
|
+
And I should see the form
|
34
|
+
|
35
|
+
Scenario: Continue editing
|
36
|
+
When I follow "Design" within "#navigation"
|
37
|
+
And I follow "Snippets"
|
38
|
+
And I follow "New Snippet"
|
39
|
+
And I fill in "Name" with "Mine"
|
40
|
+
And I fill in "Body" with "My snippet"
|
41
|
+
And I press "Save and Continue Editing"
|
42
|
+
Then I should see "Edit Snippet"
|
43
|
+
And I should see the form
|
44
|
+
|
45
|
+
Scenario: View a snippet
|
46
|
+
When I view a filtered snippet
|
47
|
+
Then I should see "Edit Snippet"
|
48
|
+
And I should see "Markdown"
|
49
|
+
|
50
|
+
Scenario: Delete a snippet with confirmation
|
51
|
+
When I follow "Design" within "#navigation"
|
52
|
+
And I follow "Snippets"
|
53
|
+
And I follow "Remove"
|
54
|
+
Then I should see "permanently remove"
|
55
|
+
And I should see "another"
|
56
|
+
When I press "Delete Snippet"
|
57
|
+
Then I should not see "another"
|
58
|
+
|
59
|
+
Scenario Outline: Admins and designers can see and edit snippets
|
60
|
+
Given I am logged in as "<username>"
|
61
|
+
And I should see "Design"
|
62
|
+
When I follow "Design" within "#navigation"
|
63
|
+
And I follow "Snippets"
|
64
|
+
And I should not see "You must have designer privileges"
|
65
|
+
And I follow "first"
|
66
|
+
Then I should see "Edit Snippet"
|
67
|
+
|
68
|
+
Examples:
|
69
|
+
| username |
|
70
|
+
| admin |
|
71
|
+
| designer |
|
72
|
+
|
73
|
+
Scenario Outline: Ordinary users cannot edit snippets
|
74
|
+
Given I am logged in as "<username>"
|
75
|
+
And I should not see "Design"
|
76
|
+
When I go to the "snippets" admin page
|
77
|
+
Then I should see "You must have designer privileges"
|
78
|
+
|
79
|
+
Examples:
|
80
|
+
| username |
|
81
|
+
| existing |
|
82
|
+
| another |
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Given /^There are many snippets$/ do
|
2
|
+
100.times do |i|
|
3
|
+
Snippet.create(:name => "snippet_#{i}", :content => "This is snippet #{i}")
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
Given /^There are few snippets$/ do
|
8
|
+
#
|
9
|
+
end
|
10
|
+
|
11
|
+
Then /^I should see all the snippets$/ do
|
12
|
+
Snippet.all.each do |snippet|
|
13
|
+
response.body.should have_tag('tr.snippet') do
|
14
|
+
with_tag("a", :text => snippet.name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
When /^I view a snippet$/ do
|
20
|
+
visit "/admin/snippets/#{snippets(:first).id}"
|
21
|
+
end
|
22
|
+
|
23
|
+
When /^I view a filtered snippet$/ do
|
24
|
+
visit "/admin/snippets/#{snippets(:markdown).id}"
|
25
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Sets up the Rails environment for Cucumber
|
2
|
+
ENV["RAILS_ENV"] = "test"
|
3
|
+
# Extension root
|
4
|
+
extension_env = File.expand_path(File.dirname(__FILE__) + '/../../../../../config/environment')
|
5
|
+
require extension_env+'.rb'
|
6
|
+
|
7
|
+
Dir.glob(File.join(RADIANT_ROOT, "features", "**", "*.rb")).each {|step| require step unless step =~ /datasets_loader\.rb$/}
|
8
|
+
|
9
|
+
Cucumber::Rails::World.class_eval do
|
10
|
+
dataset :snippets
|
11
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module NavigationHelpers
|
2
|
+
|
3
|
+
# Extend the standard PathMatchers with your own paths
|
4
|
+
# to be used in your features.
|
5
|
+
#
|
6
|
+
# The keys and values here may be used in your standard web steps
|
7
|
+
# Using:
|
8
|
+
#
|
9
|
+
# When I go to the "snippets" admin page
|
10
|
+
#
|
11
|
+
# would direct the request to the path you provide in the value:
|
12
|
+
#
|
13
|
+
# admin_snippets_path
|
14
|
+
#
|
15
|
+
PathMatchers = {} unless defined?(PathMatchers)
|
16
|
+
PathMatchers.merge!({
|
17
|
+
/snippets/i => 'admin_snippets_path'
|
18
|
+
})
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
World(NavigationHelpers)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
namespace :trusty do
|
2
|
+
namespace :extensions do
|
3
|
+
namespace :snippets do
|
4
|
+
|
5
|
+
desc "Runs the migration of the Snippets extension"
|
6
|
+
task :migrate => :environment do
|
7
|
+
require 'trusty_cms/extension_migrator'
|
8
|
+
if ENV["VERSION"]
|
9
|
+
SnippetsExtension.migrator.migrate(ENV["VERSION"].to_i)
|
10
|
+
Rake::Task['db:schema:dump'].invoke
|
11
|
+
else
|
12
|
+
SnippetsExtension.migrator.migrate
|
13
|
+
Rake::Task['db:schema:dump'].invoke
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Copies public assets of the Snippets to the instance public/ directory."
|
18
|
+
task :update => :environment do
|
19
|
+
is_svn_or_dir = proc {|path| path =~ /\.svn/ || File.directory?(path) }
|
20
|
+
puts "Copying assets from SnippetsExtension"
|
21
|
+
Dir[SnippetsExtension.root + "/public/**/*"].reject(&is_svn_or_dir).each do |file|
|
22
|
+
path = file.sub(SnippetsExtension.root, '')
|
23
|
+
directory = File.dirname(path)
|
24
|
+
mkdir_p RAILS_ROOT + directory, :verbose => false
|
25
|
+
cp file, RAILS_ROOT + path, :verbose => false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Syncs all available translations for this ext to the English ext master"
|
30
|
+
task :sync => :environment do
|
31
|
+
# The main translation root, basically where English is kept
|
32
|
+
language_root = SnippetsExtension.root + "/config/locales"
|
33
|
+
words = TranslationSupport.get_translation_keys(language_root)
|
34
|
+
|
35
|
+
Dir["#{language_root}/*.yml"].each do |filename|
|
36
|
+
next if filename.match('_available_tags')
|
37
|
+
basename = File.basename(filename, '.yml')
|
38
|
+
puts "Syncing #{basename}"
|
39
|
+
(comments, other) = TranslationSupport.read_file(filename, basename)
|
40
|
+
words.each { |k,v| other[k] ||= words[k] } # Initializing hash variable as empty if it does not exist
|
41
|
+
other.delete_if { |k,v| !words[k] } # Remove if not defined in en.yml
|
42
|
+
TranslationSupport.write_file(filename, basename, comments, other)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module TrustySnippetsExtension
|
2
|
+
VERSION = "1.0.0"
|
3
|
+
SUMMARY = "Snippets for Trusty CMS"
|
4
|
+
DESCRIPTION = "Makes Trusty better by adding snippets!"
|
5
|
+
URL = "http://github.com/pgharts/trusty-snippets-extension"
|
6
|
+
AUTHORS = ["Jim Gay", "Eric Sipple"]
|
7
|
+
EMAIL = ["sipple@trustarts.org"]
|
8
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Uncomment this if you reference any of your controllers in activate
|
2
|
+
# require_dependency "application_controller"
|
3
|
+
require "trusty-snippets-extension"
|
4
|
+
|
5
|
+
class SnippetsExtension < TrustyCms::Extension
|
6
|
+
version TrustySnippetsExtension::VERSION
|
7
|
+
description TrustySnippetsExtension::DESCRIPTION
|
8
|
+
url TrustySnippetsExtension::URL
|
9
|
+
|
10
|
+
def activate
|
11
|
+
|
12
|
+
if defined?(Radiant::Exporter)
|
13
|
+
TrustyCms::Exporter.exportable_models << Snippet
|
14
|
+
TrustyCms::Exporter.template_models << Snippet
|
15
|
+
end
|
16
|
+
|
17
|
+
Page.class_eval do
|
18
|
+
include SnippetTags
|
19
|
+
end
|
20
|
+
|
21
|
+
TrustyCms::AdminUI.class_eval do
|
22
|
+
attr_accessor :snippet
|
23
|
+
|
24
|
+
alias_method :snippets, :snippet
|
25
|
+
|
26
|
+
def load_default_snippet_regions
|
27
|
+
OpenStruct.new.tap do |snippet|
|
28
|
+
snippet.edit = TrustyCms::AdminUI::RegionSet.new do |edit|
|
29
|
+
edit.main.concat %w{edit_header edit_form}
|
30
|
+
edit.form.concat %w{edit_title edit_content edit_filter}
|
31
|
+
edit.form_bottom.concat %w{edit_buttons edit_timestamp}
|
32
|
+
end
|
33
|
+
snippet.index = TrustyCms::AdminUI::RegionSet.new do |index|
|
34
|
+
index.top.concat %w{}
|
35
|
+
index.thead.concat %w{title_header actions_header}
|
36
|
+
index.tbody.concat %w{title_cell actions_cell}
|
37
|
+
index.bottom.concat %w{new_button}
|
38
|
+
end
|
39
|
+
snippet.new = snippet.edit
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def load_default_snippet_file_regions
|
44
|
+
OpenStruct.new.tap do |snippet|
|
45
|
+
snippet.show = TrustyCms::AdminUI::RegionSet.new do |edit|
|
46
|
+
edit.main.concat %w{ header }
|
47
|
+
edit.display_content.concat %w{ title content }
|
48
|
+
edit.bottom.concat %w{ timestamp }
|
49
|
+
end
|
50
|
+
snippet.index = TrustyCms::AdminUI::RegionSet.new do |index|
|
51
|
+
index.top.concat %w{}
|
52
|
+
index.thead.concat %w{title_header}
|
53
|
+
index.tbody.concat %w{title_cell}
|
54
|
+
index.bottom.concat %w{new_button}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
admin.snippet ||= TrustyCms::AdminUI.load_default_snippet_regions
|
61
|
+
|
62
|
+
UserActionObserver.instance.send :add_observer!, ::Snippet
|
63
|
+
|
64
|
+
tab 'Design' do
|
65
|
+
add_item "Snippets", "/admin/snippets"
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
git clone git://github.com/radiant/radiant.git test_app
|
2
|
+
cd test_app
|
3
|
+
|
4
|
+
if [[ $RADIANT_VERSION != "master" ]]
|
5
|
+
then
|
6
|
+
git checkout -t origin/$RADIANT_VERSION
|
7
|
+
fi
|
8
|
+
gem install bundler --pre
|
9
|
+
echo 'gemspec' >> Gemfile
|
10
|
+
echo 'gem "radiant-snippets-extension", :path => ".."' >> Gemfile
|
11
|
+
bundle install
|
12
|
+
|
13
|
+
case $DB in
|
14
|
+
"mysql" )
|
15
|
+
mysql -e 'create database radiant_test;'
|
16
|
+
cp spec/ci/database.mysql.yml config/database.yml;;
|
17
|
+
"postgres" )
|
18
|
+
psql -c 'create database radiant_test;' -U postgres
|
19
|
+
cp spec/ci/database.postgresql.yml config/database.yml;;
|
20
|
+
esac
|
21
|
+
|
22
|
+
bundle exec rake db:migrate
|
data/spec/ci/script
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../snippets_spec_helper.rb')
|
2
|
+
|
3
|
+
describe Admin::SnippetsController do
|
4
|
+
dataset :users, :snippets
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
ActionController::Routing::Routes.reload
|
8
|
+
login_as :designer
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should be an ResourceController" do
|
12
|
+
controller.should be_kind_of(Admin::ResourceController)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should handle Snippets" do
|
16
|
+
controller.class.model_class.should == Snippet
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
describe "show" do
|
21
|
+
it "should redirect to the edit action" do
|
22
|
+
get :show, :id => 1
|
23
|
+
response.should redirect_to(edit_admin_snippet_path(params[:id]))
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should show xml when format is xml" do
|
27
|
+
snippet = Snippet.first
|
28
|
+
get :show, :id => snippet.id, :format => "xml"
|
29
|
+
response.body.should == snippet.to_xml
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "with invalid snippet id" do
|
34
|
+
[:edit, :remove].each do |action|
|
35
|
+
before do
|
36
|
+
@parameters = {:id => 999}
|
37
|
+
end
|
38
|
+
it "should redirect the #{action} action to the index action" do
|
39
|
+
get action, @parameters
|
40
|
+
response.should redirect_to(admin_snippets_path)
|
41
|
+
end
|
42
|
+
it "should say that the 'Snippet could not be found.' after the #{action} action" do
|
43
|
+
get action, @parameters
|
44
|
+
flash[:notice].should == 'Snippet could not be found.'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
it 'should redirect the update action to the index action' do
|
48
|
+
put :update, @parameters
|
49
|
+
response.should redirect_to(admin_snippets_path)
|
50
|
+
end
|
51
|
+
it "should say that the 'Snippet could not be found.' after the update action" do
|
52
|
+
put :update, @parameters
|
53
|
+
flash[:notice].should == 'Snippet could not be found.'
|
54
|
+
end
|
55
|
+
it 'should redirect the destroy action to the index action' do
|
56
|
+
delete :destroy, @parameters
|
57
|
+
response.should redirect_to(admin_snippets_path)
|
58
|
+
end
|
59
|
+
it "should say that the 'Snippet could not be found.' after the destroy action" do
|
60
|
+
delete :destroy, @parameters
|
61
|
+
flash[:notice].should == 'Snippet could not be found.'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
{:get => [:index, :show, :new, :edit, :remove],
|
66
|
+
:post => [:create],
|
67
|
+
:put => [:update],
|
68
|
+
:delete => [:destroy]}.each do |method, actions|
|
69
|
+
actions.each do |action|
|
70
|
+
it "should require login to access the #{action} action" do
|
71
|
+
logout
|
72
|
+
lambda { send(method, action, :id => snippet_id(:first)) }.should require_login
|
73
|
+
end
|
74
|
+
|
75
|
+
if action == :show
|
76
|
+
it "should request authentication for API access on show" do
|
77
|
+
logout
|
78
|
+
send(method, action, :id => snippet_id(:first), :format => "xml")
|
79
|
+
response.response_code.should == 401
|
80
|
+
end
|
81
|
+
else
|
82
|
+
it "should allow access to designers for the #{action} action" do
|
83
|
+
lambda {
|
84
|
+
send(method, action, :id => snippet_id(:first))
|
85
|
+
}.should restrict_access(:allow => [users(:designer)],
|
86
|
+
:url => '/admin/pages')
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should allow access to admins for the #{action} action" do
|
90
|
+
lambda {
|
91
|
+
send(method, action, :id => snippet_id(:first))
|
92
|
+
}.should restrict_access(:allow => [users(:designer)],
|
93
|
+
:url => '/admin/pages')
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should deny non-designers and non-admins for the #{action} action" do
|
97
|
+
lambda {
|
98
|
+
send(method, action, :id => Snippet.first.id)
|
99
|
+
}.should restrict_access(:deny => [users(:non_admin), users(:existing)],
|
100
|
+
:url => '/admin/pages')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should clear the page cache when saved" do
|
107
|
+
Radiant::Cache.should_receive(:clear)
|
108
|
+
put :update, :id => snippet_id(:first), :snippet => {:content => "Foobar."}
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class SnippetMarkdownFilter < TextFilter
|
2
|
+
def filter(text)
|
3
|
+
text + ' for Snippets!'
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class SnippetsDataset < Dataset::Base
|
8
|
+
|
9
|
+
def load
|
10
|
+
create_snippet "first", :content => "test"
|
11
|
+
create_snippet "another", :content => "another test"
|
12
|
+
create_snippet "markdown", :filter_id => "Snippet Markdown", :content => "**markdown**"
|
13
|
+
create_snippet "radius", :content => "<r:title />"
|
14
|
+
create_snippet "global_page_cascade", :content => "<r:children:each><r:page:title /> </r:children:each>"
|
15
|
+
create_snippet "recursive", :content => "<r:children:each><r:snippet name='recursive' /></r:children:each><r:title />"
|
16
|
+
create_snippet "yielding", :content => "Before...<r:yield/>...and after"
|
17
|
+
create_snippet "div_wrap", :content => "<div><r:yield/></div>"
|
18
|
+
create_snippet "nested_yields", :content => '<snippet name="div_wrap">above <r:yield/> below</snippet>'
|
19
|
+
create_snippet "yielding_often", :content => '<r:yield/> is <r:yield/>er than <r:yield/>'
|
20
|
+
end
|
21
|
+
|
22
|
+
helpers do
|
23
|
+
def create_snippet(name, attributes={})
|
24
|
+
create_record :snippet, name.symbolize, snippet_params(attributes.reverse_merge(:name => name))
|
25
|
+
end
|
26
|
+
|
27
|
+
def snippet_params(attributes={})
|
28
|
+
name = attributes[:name] || unique_snippet_name
|
29
|
+
{
|
30
|
+
:name => name,
|
31
|
+
:content => "<r:content />"
|
32
|
+
}.merge(attributes)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def unique_snippet_name
|
38
|
+
@@unique_snippet_name_call_count ||= 0
|
39
|
+
@@unique_snippet_name_call_count += 1
|
40
|
+
"snippet-#{@@unique_snippet_name_call_count}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|