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
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# Determine where the RSpec plugin is by loading the boot
|
2
|
+
unless defined? RADIANT_ROOT
|
3
|
+
ENV["RAILS_ENV"] = "test"
|
4
|
+
case
|
5
|
+
when ENV["RADIANT_ENV_FILE"]
|
6
|
+
require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
|
7
|
+
when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
|
8
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
|
9
|
+
else
|
10
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'rake'
|
15
|
+
require 'rdoc/task'
|
16
|
+
require 'rake/testtask'
|
17
|
+
|
18
|
+
rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
|
19
|
+
$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
|
20
|
+
require 'spec/rake/spectask'
|
21
|
+
require 'cucumber'
|
22
|
+
require 'cucumber/rake/task'
|
23
|
+
|
24
|
+
# Cleanup the RADIANT_ROOT constant so specs will load the environment
|
25
|
+
Object.send(:remove_const, :RADIANT_ROOT)
|
26
|
+
|
27
|
+
extension_root = File.expand_path(File.dirname(__FILE__))
|
28
|
+
|
29
|
+
task :default => [:spec, :features]
|
30
|
+
task :stats => "spec:statsetup"
|
31
|
+
|
32
|
+
desc "Run all specs in spec directory"
|
33
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
34
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
35
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
36
|
+
end
|
37
|
+
|
38
|
+
task :features => 'spec:integration'
|
39
|
+
|
40
|
+
namespace :spec do
|
41
|
+
desc "Run all specs in spec directory with RCov"
|
42
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
43
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
44
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
45
|
+
t.rcov = true
|
46
|
+
t.rcov_opts = ['--exclude', 'spec', '--rails']
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "Print Specdoc for all specs"
|
50
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
51
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
52
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
53
|
+
end
|
54
|
+
|
55
|
+
[:models, :controllers, :views, :helpers].each do |sub|
|
56
|
+
desc "Run the specs under spec/#{sub}"
|
57
|
+
Spec::Rake::SpecTask.new(sub) do |t|
|
58
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
59
|
+
t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
desc "Run the Cucumber features"
|
64
|
+
Cucumber::Rake::Task.new(:integration) do |t|
|
65
|
+
t.fork = true
|
66
|
+
t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'pretty')]
|
67
|
+
# t.feature_pattern = "#{extension_root}/features/**/*.feature"
|
68
|
+
t.profile = "default"
|
69
|
+
end
|
70
|
+
|
71
|
+
# Setup specs for stats
|
72
|
+
task :statsetup do
|
73
|
+
require 'code_statistics'
|
74
|
+
::STATS_DIRECTORIES << %w(Model\ specs spec/models)
|
75
|
+
::STATS_DIRECTORIES << %w(View\ specs spec/views)
|
76
|
+
::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
|
77
|
+
::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
|
78
|
+
::CodeStatistics::TEST_TYPES << "Model specs"
|
79
|
+
::CodeStatistics::TEST_TYPES << "View specs"
|
80
|
+
::CodeStatistics::TEST_TYPES << "Controller specs"
|
81
|
+
::CodeStatistics::TEST_TYPES << "Helper specs"
|
82
|
+
::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
|
83
|
+
end
|
84
|
+
|
85
|
+
namespace :db do
|
86
|
+
namespace :fixtures do
|
87
|
+
desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
|
88
|
+
task :load => :environment do
|
89
|
+
require 'active_record/fixtures'
|
90
|
+
ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
|
91
|
+
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
|
92
|
+
Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
desc 'Generate documentation for the snippets extension.'
|
100
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
101
|
+
rdoc.rdoc_dir = 'rdoc'
|
102
|
+
rdoc.title = 'SnippetsExtension'
|
103
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
104
|
+
rdoc.rdoc_files.include('README')
|
105
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
106
|
+
end
|
107
|
+
|
108
|
+
# Load any custom rakefiles for extension
|
109
|
+
Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class Admin::SnippetsController < Admin::ResourceController
|
2
|
+
paginate_models
|
3
|
+
only_allow_access_to :index, :show, :new, :create, :edit, :update, :remove, :destroy,
|
4
|
+
:when => [:designer, :admin],
|
5
|
+
:denied_url => { :controller => 'admin/pages', :action => 'index' },
|
6
|
+
:denied_message => 'You must have designer privileges to perform this action.'
|
7
|
+
|
8
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Snippet < ActiveRecord::Base
|
2
|
+
|
3
|
+
# Default Order
|
4
|
+
default_scope :order => 'name'
|
5
|
+
|
6
|
+
# Associations
|
7
|
+
belongs_to :created_by, :class_name => 'User'
|
8
|
+
belongs_to :updated_by, :class_name => 'User'
|
9
|
+
|
10
|
+
# Validations
|
11
|
+
validates_presence_of :name
|
12
|
+
validates_length_of :name, :maximum => 100
|
13
|
+
validates_length_of :filter_id, :maximum => 25, :allow_nil => true
|
14
|
+
validates_format_of :name, :with => %r{^\S*$}
|
15
|
+
validates_uniqueness_of :name
|
16
|
+
|
17
|
+
object_id_attr :filter, TextFilter
|
18
|
+
|
19
|
+
def after_initialize
|
20
|
+
self.filter_id ||= TrustyCms::Config['defaults.snippet.filter'] if new_record?
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class SnippetFinder
|
2
|
+
class << self
|
3
|
+
def find(id)
|
4
|
+
find_map('find',id)
|
5
|
+
end
|
6
|
+
|
7
|
+
def find_by_name(name)
|
8
|
+
find_map('find_by_name', name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def finder_types
|
12
|
+
[SnippetFile, Snippet]
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def find_map(meth, *args)
|
18
|
+
finder_types.find{|type|
|
19
|
+
found = type.send(meth, *args)
|
20
|
+
return found if found
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module SnippetTags
|
2
|
+
include TrustyCms::Taggable
|
3
|
+
|
4
|
+
class TagError < StandardError; end
|
5
|
+
|
6
|
+
desc %{
|
7
|
+
Renders the snippet specified in the @name@ attribute within the context of a page.
|
8
|
+
|
9
|
+
*Usage:*
|
10
|
+
|
11
|
+
<pre><code><r:snippet name="snippet_name" /></code></pre>
|
12
|
+
|
13
|
+
When used as a double tag, the part in between both tags may be used within the
|
14
|
+
snippet itself, being substituted in place of @<r:yield/>@.
|
15
|
+
|
16
|
+
*Usage:*
|
17
|
+
|
18
|
+
<pre><code><r:snippet name="snippet_name">Lorem ipsum dolor...</r:snippet></code></pre>
|
19
|
+
}
|
20
|
+
tag 'snippet' do |tag|
|
21
|
+
required_attr(tag, 'name')
|
22
|
+
name = tag['name']
|
23
|
+
|
24
|
+
snippet = snippet_cache(name.strip)
|
25
|
+
|
26
|
+
if snippet
|
27
|
+
tag.locals.yield = tag.expand if tag.double?
|
28
|
+
tag.globals.page.render_snippet(snippet)
|
29
|
+
else
|
30
|
+
raise TagError.new("snippet '#{name}' not found")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def snippet_cache(name)
|
35
|
+
@snippet_cache ||= {}
|
36
|
+
|
37
|
+
snippet = @snippet_cache[name]
|
38
|
+
unless snippet
|
39
|
+
snippet = SnippetFinder.find_by_name(name)
|
40
|
+
@snippet_cache[name] = snippet
|
41
|
+
end
|
42
|
+
snippet
|
43
|
+
end
|
44
|
+
private :snippet_cache
|
45
|
+
|
46
|
+
desc %{
|
47
|
+
Used within a snippet as a placeholder for substitution of child content, when
|
48
|
+
the snippet is called as a double tag.
|
49
|
+
|
50
|
+
*Usage (within a snippet):*
|
51
|
+
|
52
|
+
<pre><code>
|
53
|
+
<div id="outer">
|
54
|
+
<p>before</p>
|
55
|
+
<r:yield/>
|
56
|
+
<p>after</p>
|
57
|
+
</div>
|
58
|
+
</code></pre>
|
59
|
+
|
60
|
+
If the above snippet was named "yielding", you could call it from any Page,
|
61
|
+
Layout or Snippet as follows:
|
62
|
+
|
63
|
+
<pre><code><r:snippet name="yielding">Content within</r:snippet></code></pre>
|
64
|
+
|
65
|
+
Which would output the following:
|
66
|
+
|
67
|
+
<pre><code>
|
68
|
+
<div id="outer">
|
69
|
+
<p>before</p>
|
70
|
+
Content within
|
71
|
+
<p>after</p>
|
72
|
+
</div>
|
73
|
+
</code></pre>
|
74
|
+
|
75
|
+
When called in the context of a Page or a Layout, @<r:yield/>@ outputs nothing.
|
76
|
+
}
|
77
|
+
tag 'yield' do |tag|
|
78
|
+
tag.locals.yield
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
= form_for [:admin, @snippet], :html => {'data-onsubmit_status' => onsubmit_status(@snippet)} do |f|
|
2
|
+
= f.hidden_field :lock_version
|
3
|
+
= render_region :form_top, :locals => {:f => f}
|
4
|
+
.form_area
|
5
|
+
- render_region :form, :locals => {:f => f} do |form|
|
6
|
+
- form.edit_title do
|
7
|
+
%p.title
|
8
|
+
= f.label :name, t('name')
|
9
|
+
= f.text_field :name, :class => 'textbox activate', :maxlength => 100
|
10
|
+
- form.edit_content do
|
11
|
+
%p.content
|
12
|
+
= f.label :content, t('body')
|
13
|
+
~ f.text_area :content, :class => "textarea large", :style => "width: 100%"
|
14
|
+
- form.edit_filter do
|
15
|
+
.set
|
16
|
+
%p
|
17
|
+
%span.reference_links
|
18
|
+
== #{t('reference')}:
|
19
|
+
%span{:id => "tag_reference_link"}
|
20
|
+
= link_to t('available_tags'), admin_reference_url(:tags), id: 'tag_reference'
|
21
|
+
- render_region :form_bottom, :locals => {:f => f} do |form_bottom|
|
22
|
+
- form_bottom.edit_buttons do
|
23
|
+
.buttons{:style=>"clear: left"}
|
24
|
+
= save_model_button(@snippet)
|
25
|
+
= save_model_and_continue_editing_button(@snippet)
|
26
|
+
= t('or')
|
27
|
+
= link_to t('cancel'), admin_snippets_url
|
28
|
+
- form_bottom.edit_timestamp do
|
29
|
+
= updated_stamp @snippet
|
@@ -0,0 +1,11 @@
|
|
1
|
+
- @page_title = @snippet.name + ' ' + t('snippet') + ' - ' + default_page_title
|
2
|
+
|
3
|
+
- render_region :main do |main|
|
4
|
+
- main.edit_header do
|
5
|
+
%h1= t('edit_snippet')
|
6
|
+
- main.edit_form do
|
7
|
+
= render :partial => 'form'
|
8
|
+
- main.edit_popups do
|
9
|
+
= render :partial => "popups"
|
10
|
+
|
11
|
+
- content_for 'page_scripts'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
- @page_title = t('snippets') + ' - ' + default_page_title
|
2
|
+
|
3
|
+
.outset
|
4
|
+
= render_region :top
|
5
|
+
%table.index#snippets
|
6
|
+
%thead
|
7
|
+
%tr
|
8
|
+
- render_region :thead do |thead|
|
9
|
+
- thead.title_header do
|
10
|
+
%th.name= t('snippet')
|
11
|
+
- thead.actions_header do
|
12
|
+
%th.actions{:style=>"width:9em"}= t('modify')
|
13
|
+
%tbody
|
14
|
+
- if @snippets.any?
|
15
|
+
- @snippets.each do |snippet|
|
16
|
+
%tr[snippet]
|
17
|
+
- render_region :tbody, :locals => {:snippet => snippet} do |tbody|
|
18
|
+
- tbody.title_cell do
|
19
|
+
%td.name
|
20
|
+
= link_to image('snippet', :alt => '') + ' ' + snippet.name, edit_admin_snippet_url(snippet)
|
21
|
+
- tbody.actions_cell do
|
22
|
+
%td.actions
|
23
|
+
= link_to image('minus') + ' ' + t('remove'), remove_admin_snippet_url(snippet), :class => "action"
|
24
|
+
- else
|
25
|
+
%tr
|
26
|
+
%td.empty{:colspan => admin.snippet.index.tbody.length}= t('no_snippets')
|
27
|
+
|
28
|
+
- render_region :bottom do |bottom|
|
29
|
+
- bottom.new_button do
|
30
|
+
#actions
|
31
|
+
= pagination_for(@snippets)
|
32
|
+
%ul
|
33
|
+
%li= link_to image('plus') + " " + t('new_snippet'), new_admin_snippet_url, :class => 'action_button'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
%h1= t('remove_snippet')
|
2
|
+
|
3
|
+
%p
|
4
|
+
= t('text.snippets.remove_warning')
|
5
|
+
|
6
|
+
%table.index#snippets
|
7
|
+
%tbody
|
8
|
+
%tr.node.level_1
|
9
|
+
%td.snippet
|
10
|
+
= image('snippet', :alt => "")
|
11
|
+
%span.title= @snippet.name
|
12
|
+
|
13
|
+
= form_for [:admin, @snippet], :html => {:method => :delete, 'data-onsubmit_status'=>"Removing snippet…"} do
|
14
|
+
.buttons
|
15
|
+
%input.button{:type=>"submit", :value => t('delete_snippet')}/
|
16
|
+
= t('or')
|
17
|
+
= link_to t('cancel'), admin_snippets_url
|
@@ -0,0 +1,16 @@
|
|
1
|
+
---
|
2
|
+
en:
|
3
|
+
snippet: 'Snippet'
|
4
|
+
snippets: 'Snippets'
|
5
|
+
config:
|
6
|
+
defaults:
|
7
|
+
snippet:
|
8
|
+
filter: "snippet filter"
|
9
|
+
delete_snippet: 'Delete Snippet'
|
10
|
+
edit_snippet: 'Edit Snippet'
|
11
|
+
new_snippet: 'New Snippet'
|
12
|
+
no_snippets: 'No Snippets'
|
13
|
+
remove_snippet: 'Remove Snippet'
|
14
|
+
text:
|
15
|
+
snippets:
|
16
|
+
remove_warning: 'Are you sure you want to <strong class="warning">permanently remove</strong> the following snippet?'
|
data/config/routes.rb
ADDED
data/cucumber.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
default: --format progress features --tags ~@proposed,~@in_progress
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Feature: Rich configuration
|
2
|
+
In order to control site delivery
|
3
|
+
an administrator
|
4
|
+
wants to change the configuration of the site
|
5
|
+
|
6
|
+
Scenario: Reviewing configuration
|
7
|
+
Given I am logged in as "admin"
|
8
|
+
And I go to the "configuration" admin page
|
9
|
+
Then I should see "Snippet Filter"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Feature: Paginating admin views
|
2
|
+
In order to reduce load time and page bulk
|
3
|
+
a content editor
|
4
|
+
wants longer resource lists to be paginated
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given There are many snippets
|
8
|
+
And I am logged in as "designer"
|
9
|
+
And I go to the "snippets" admin page
|
10
|
+
|
11
|
+
Scenario: More snippets than we want to show on one page
|
12
|
+
Then I should see "first"
|
13
|
+
And I should not see "snippet_50"
|
14
|
+
And I should see page 1 of the results
|
15
|
+
And I should see pagination controls
|
16
|
+
And I should see a depagination link
|
17
|
+
|
18
|
+
When I follow "2" within "div.pagination"
|
19
|
+
Then I should see page 2 of the results
|
20
|
+
And I should see "snippet_50"
|
21
|
+
|
22
|
+
When I follow "show all"
|
23
|
+
# Then I should mention the request parameters
|
24
|
+
Then I should see all the snippets
|
25
|
+
And I should not see pagination controls
|
26
|
+
And I should not see a depagination link
|
27
|
+
|