radiant-sheets-extension 1.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README.md +33 -0
- data/Rakefile +129 -0
- data/app/controllers/admin/scripts_controller.rb +13 -0
- data/app/controllers/admin/sheet_resource_controller.rb +54 -0
- data/app/controllers/admin/styles_controller.rb +13 -0
- data/app/helpers/admin/scripts_helper.rb +5 -0
- data/app/helpers/admin/styles_helper.rb +5 -0
- data/app/models/javascript_page.rb +16 -0
- data/app/models/sheet.rb +93 -0
- data/app/models/stylesheet_page.rb +16 -0
- data/app/views/admin/scripts/edit.html.haml +30 -0
- data/app/views/admin/scripts/index.html.haml +33 -0
- data/app/views/admin/scripts/new.html.haml +30 -0
- data/app/views/admin/sheets/_edit_scripts.html.haml +40 -0
- data/app/views/admin/sheets/_upload_script.html.haml +16 -0
- data/app/views/admin/styles/edit.html.haml +30 -0
- data/app/views/admin/styles/index.html.haml +33 -0
- data/app/views/admin/styles/new.html.haml +30 -0
- data/config/locales/en.yml +17 -0
- data/config/routes.rb +6 -0
- data/cucumber.yml +1 -0
- data/features/admin/managing_javascripts.feature +147 -0
- data/features/admin/managing_stylesheets.feature +108 -0
- data/features/support/env.rb +11 -0
- data/features/support/paths.rb +11 -0
- data/lib/coffee_filter.rb +5 -0
- data/lib/javascript_tags.rb +58 -0
- data/lib/radiant-sheets-extension/version.rb +3 -0
- data/lib/radiant-sheets-extension.rb +2 -0
- data/lib/sass_filter.rb +18 -0
- data/lib/scss_filter.rb +19 -0
- data/lib/stylesheet_tags.rb +61 -0
- data/lib/tasks/sheets_extension_tasks.rake +79 -0
- data/public/images/admin/javascript.png +0 -0
- data/public/images/admin/stylesheet.png +0 -0
- data/radiant-sheets-extension.gemspec +32 -0
- data/sass.html +82 -0
- data/sheets_extension.rb +96 -0
- data/spec/controllers/admin/scripts_controller_spec.rb +123 -0
- data/spec/controllers/admin/styles_controller_spec.rb +124 -0
- data/spec/datasets/javascripts_dataset.rb +15 -0
- data/spec/datasets/stylesheets_dataset.rb +17 -0
- data/spec/helpers/admin/scripts_helper_spec.rb +5 -0
- data/spec/helpers/admin/styles_helper_spec.rb +5 -0
- data/spec/lib/javascript_tags_spec.rb +36 -0
- data/spec/lib/stylesheet_tags_spec.rb +41 -0
- data/spec/models/javascript_page_spec.rb +80 -0
- data/spec/models/page_spec.rb +5 -0
- data/spec/models/stylesheet_page_spec.rb +80 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +36 -0
- metadata +169 -0
data/sass.html
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
<h1 id='features'>Features</h1>
|
2
|
+
|
3
|
+
<p>Although it’s nice that every CSS file is a valid SCSS file, that’s not why you’re learning about SCSS. You’re learning because it offers <em>more</em> than CSS. SCSS provides all sorts of useful features on top of CSS. This tutorial will walk you through some of the most fundamental ones, along with examples that you can add to your <code>style.scss</code> and play with.</p>
|
4
|
+
|
5
|
+
<h2 id='nesting'>Nesting</h2>
|
6
|
+
|
7
|
+
<p>Often when writing CSS, you’ll have several selectors that all begin with the same thing. For example, you might have “#navbar ul”, “#navbar li”, and “#navbar li a”. It’s a pain to repeat the beginning over and over again, especially when it gets long.</p>
|
8
|
+
|
9
|
+
<p>Sass allows you to avoid this by nesting the child selectors within the parent selector.</p>
|
10
|
+
<div class='compare'>
|
11
|
+
<pre class='sass'>/* style.scss */

#navbar {
 width: 80%;
 height: 23px;

 ul { list-style-type: none; }
 li {
 float: left;
 a { font-weight: bold; }
 }
}
</pre>
|
12
|
+
<pre class='css'>/* style.css */

#navbar {
 width: 80%;
 height: 23px; }
 #navbar ul {
 list-style-type: none; }
 #navbar li {
 float: left; }
 #navbar li a {
 font-weight: bold; }
</pre>
|
13
|
+
</div>
|
14
|
+
<p>Notice how the output is formatted to reflect the original nesting of the selectors. This is the default CSS style, but there are <a href='/docs/yardoc/file.SASS_REFERENCE.html#output_style'>other styles</a> for all sorts of CSS formatting preferences. There’s even one for compressing the CSS as much as possible!</p>
|
15
|
+
|
16
|
+
<p>You can also nest properties, so you don’t have to repeat stuff like “<code>border-left-</code>” all the time.</p>
|
17
|
+
<div class='compare'>
|
18
|
+
<pre class='sass'>/* style.scss */

.fakeshadow {
 border: {
 style: solid;
 left: {
 width: 4px;
 color: #888;
 }
 right: {
 width: 2px;
 color: #ccc;
 }
 }
</pre>
|
19
|
+
<pre class='css'>/* style.css */

.fakeshadow {
 border-style: solid;
 border-left-width: 4px;
 border-left-color: #888;
 border-right-width: 2px;
 border-right-color: #ccc; }
</pre>
|
20
|
+
</div>
|
21
|
+
<h3 id='parent_references'>Parent References</h3>
|
22
|
+
|
23
|
+
<p>What about pseudoclasses, like <code>:hover</code>? There isn’t a space between them and their parent selector, but it’s still possible to nest them. You just need to use the Sass special character <code>&</code>. In a selector, <code>&</code> will be replaced verbatim with the parent selector.</p>
|
24
|
+
<div class='compare'>
|
25
|
+
<pre class='sass'>/* style.scss */

a {
 color: #ce4dd6;
 &:hover { color: #ffb3ff; }
 &:visited { color: #c458cb; }
}
</pre>
|
26
|
+
<pre class='css'>/* style.css */

a {
 color: #ce4dd6; }
 a:hover {
 color: #ffb3ff; }
 a:visited {
 color: #c458cb; }
</pre>
|
27
|
+
</div>
|
28
|
+
<h2 id='variables'>Variables</h2>
|
29
|
+
|
30
|
+
<p>Sass allows you to declare variables that can be used throughout your stylesheet. Variables begin with <code>$</code> and are declared just like properties. They can have any value that’s allowed for a CSS property, such as colors, numbers (with units), or text.</p>
|
31
|
+
<div class='compare'>
|
32
|
+
<pre class='sass'>/* style.scss */

$main-color: #ce4dd6;
$style: solid;

#navbar {
 border-bottom: {
 color: $main-color;
 style: $style;
 }
}

a {
 color: $main-color;
 &:hover { border-bottom: $style 1px; }
}
</pre>
|
33
|
+
<pre class='css'>/* style.css */

#navbar {
 border-bottom-color: #ce4dd6;
 border-bottom-style: solid; }

a {
 color: #ce4dd6; }
 a:hover {
 border-bottom: solid 1px; }
</pre>
|
34
|
+
</div>
|
35
|
+
<p>Variables allow you to re-use colors, sizes, and other values without repeating yourself. This means that changes that should be small, such as tweaking the coloring or the sizing, can be done in one place, not all over the stylesheet.</p>
|
36
|
+
|
37
|
+
<h3 id='operations_and_functions'>Operations and Functions</h3>
|
38
|
+
|
39
|
+
<p>In addition to just using variable values as they’re defined, you can also modify and combine them using math and useful predefined functions. This allows you to compute element sizing and even coloration dynamically.</p>
|
40
|
+
|
41
|
+
<p>The standard math operations (<code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, and <code>%</code>) are supported for numbers, even those with units. For colors, there are all sorts of useful functions for changing the <a href='/docs/yardoc/Sass/Script/Functions.html#lighten-instance_method'>lightness</a>, <a href='/docs/yardoc/Sass/Script/Functions.html#adjust_hue-instance_method'>hue</a>, <a href='/docs/yardoc/Sass/Script/Functions.html#saturate-instance_method'>saturation</a>, and <a href='/docs/yardoc/Sass/Script/Functions.html'>more</a>.</p>
|
42
|
+
<div class='compare'>
|
43
|
+
<pre class='sass'>/* style.scss */

#navbar {
 $navbar-width: 800px;
 $items: 5;
 $navbar-color: #ce4dd6;

 width: $navbar-width;
 border-bottom: 2px solid $navbar-color;

 li {
 float: left;
 width: $navbar-width/$items - 10px;

 background-color:
 lighten($navbar-color, 20%);
 &:hover {
 background-color:
 lighten($navbar-color, 10%);
 }
 }
}
</pre>
|
44
|
+
<pre class='css'>/* style.css */

#navbar {
 width: 800px;
 border-bottom: 2px solid #ce4dd6; }
 #navbar li {
 float: left;
 width: 150px;
 background-color: #e5a0e9; }
 #navbar li:hover {
 background-color: #d976e0; }
</pre>
|
45
|
+
</div>
|
46
|
+
<h3 id='interpolation'>Interpolation</h3>
|
47
|
+
|
48
|
+
<p>Variables can be used for more than just property values. You can use <code>#{}</code> to insert them into property names or selectors.</p>
|
49
|
+
<div class='compare'>
|
50
|
+
<pre class='sass'>/* style.scss */

$side: top;
$radius: 10px;

.rounded- {
 border-#{$side}-radius: $radius;
 -moz-border-radius-#{$side}: $radius;
 -webkit-border-#{$side}-radius: $radius;
}
</pre>
|
51
|
+
<pre class='css'>/* style.css */

.rounded-top {
 border-top-radius: 10px;
 -moz-border-radius-top: 10px;
 -webkit-border-top-radius: 10px; }
</pre>
|
52
|
+
</div>
|
53
|
+
<h2 id='mixins'>Mixins</h2>
|
54
|
+
|
55
|
+
<p>Mixins are one of the most powerful Sass features. They allow re-use of styles – properties or even selectors – without having to copy and paste them or move them into a non-semantic class.</p>
|
56
|
+
|
57
|
+
<p>Mixins are defined using the “<code>@mixin</code>” directive, which takes a block of styles that can then be included in another selector using the “<code>@include</code>” directive.</p>
|
58
|
+
<div class='compare'>
|
59
|
+
<pre class='sass'>/* style.scss */

@mixin rounded-top {
 $side: top;
 $radius: 10px;

 border-#{$side}-radius: $radius;
 -moz-border-radius-#{$side}: $radius;
 -webkit-border-#{$side}-radius: $radius;
}

#navbar li { @include rounded-top; }
#footer { @include rounded-top; }
</pre>
|
60
|
+
<pre class='css'>/* style.css */

#navbar li {
 border-top-radius: 10px;
 -moz-border-radius-top: 10px;
 -webkit-border-top-radius: 10px; }

#footer {
 border-top-radius: 10px;
 -moz-border-radius-top: 10px;
 -webkit-border-top-radius: 10px; }
</pre>
|
61
|
+
</div>
|
62
|
+
<h3 id='arguments'>Arguments</h3>
|
63
|
+
|
64
|
+
<p>The real power of mixins comes when you pass them arguments. Arguments are declared as a parenthesized, comma-separated list of variables. Each of those variables is assigned a value each time the mixin is used.</p>
|
65
|
+
|
66
|
+
<p>Mixin arguments can also be given default values just like you’d declare them normally. Then the user of the mixin can choose not to pass that argument and it will be assigned the default value.</p>
|
67
|
+
<div class='compare'>
|
68
|
+
<pre class='sass'>/* style.scss */

@mixin rounded($side, $radius: 10px) {
 border-#{$side}-radius: $radius;
 -moz-border-radius-#{$side}: $radius;
 -webkit-border-#{$side}-radius: $radius;
}

#navbar li { @include rounded(top); }
#footer { @include rounded(top, 5px); }
#sidebar { @include rounded(left, 8px); }
</pre>
|
69
|
+
<pre class='css'>/* style.css */

#navbar li {
 border-top-radius: 10px;
 -moz-border-radius-top: 10px;
 -webkit-border-top-radius: 10px; }

#footer {
 border-top-radius: 5px;
 -moz-border-radius-top: 5px;
 -webkit-border-top-radius: 5px; }

#sidebar {
 border-left-radius: 8px;
 -moz-border-radius-left: 8px;
 -webkit-border-left-radius: 8px; }
</pre>
|
70
|
+
</div>
|
71
|
+
<h2 id='id1'><code>@import</code></h2>
|
72
|
+
|
73
|
+
<p>Stylesheets can get pretty big. CSS has an <code>@import</code> directive that allows you to break your styles up into multiple stylesheets, but each stylesheet takes a separate (slow) HTTP request. That’s why Sass’s <code>@import</code> directive pulls in the stylesheets directly. Not only that, but any variables or mixins defined in <code>@import</code>ed files are available to the files that import them.</p>
|
74
|
+
|
75
|
+
<p>Sass has a naming convention for files that are meant to be imported (called “partials”): they begin with an underscore. Let’s create a partial called <strong><code>_rounded.scss</code></strong> to hold our <code>rounded</code> mixin.</p>
|
76
|
+
|
77
|
+
<p>In order to support both <code>.scss</code> and <code>.sass</code> files, Sass allows files to be imported without specifying a file extension. That means we can just import <code>"rounded"</code>, rather than <code>"rounded.scss"</code>.</p>
|
78
|
+
<div class='compare'>
|
79
|
+
<pre class='sass'>/* _rounded.scss */

@mixin rounded($side, $radius: 10px) {
 border-#{$side}-radius: $radius;
 -moz-border-radius-#{$side}: $radius;
 -webkit-border-#{$side}-radius: $radius;
}
</pre>
|
80
|
+
<pre class='css'>/* style.css */

#navbar li {
 border-top-radius: 10px;
 -moz-border-radius-top: 10px;
 -webkit-border-top-radius: 10px; }

#footer {
 border-top-radius: 5px;
 -moz-border-radius-top: 5px;
 -webkit-border-top-radius: 5px; }

#sidebar {
 border-left-radius: 8px;
 -moz-border-radius-left: 8px;
 -webkit-border-left-radius: 8px; }
</pre>
|
81
|
+
<pre class='sass'>/* style.scss */

@import "rounded";

#navbar li { @include rounded(top); }
#footer { @include rounded(top, 5px); }
#sidebar { @include rounded(left, 8px); }
</pre>
|
82
|
+
</div>
|
data/sheets_extension.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# Uncomment this if you reference any of your controllers in activate
|
2
|
+
require_dependency 'application_controller'
|
3
|
+
begin
|
4
|
+
require 'compass'
|
5
|
+
rescue
|
6
|
+
# Running on edge, compass already exists
|
7
|
+
end
|
8
|
+
require 'radiant-sheets-extension/version'
|
9
|
+
|
10
|
+
class SheetsExtension < Radiant::Extension
|
11
|
+
version RadiantSheetsExtension::VERSION
|
12
|
+
description "Manage CSS and Javascript content in Radiant CMS as Sheets, a subset of Pages"
|
13
|
+
url "http://github.com/radiant/radiant"
|
14
|
+
|
15
|
+
cattr_accessor :stylesheet_filters
|
16
|
+
cattr_accessor :javascript_filters
|
17
|
+
|
18
|
+
@@stylesheet_filters ||= []
|
19
|
+
@@stylesheet_filters += [SassFilter, ScssFilter]
|
20
|
+
@@javascript_filters ||= []
|
21
|
+
@@javascript_filters << CoffeeFilter
|
22
|
+
|
23
|
+
extension_config do |config|
|
24
|
+
config.gem 'coffee-script', :version => '~> 2.2.0'
|
25
|
+
config.gem 'sass', :version => '~> 3.1.2'
|
26
|
+
end
|
27
|
+
|
28
|
+
def activate
|
29
|
+
SassFilter
|
30
|
+
ScssFilter
|
31
|
+
CoffeeFilter
|
32
|
+
|
33
|
+
tab 'Design' do
|
34
|
+
add_item "Stylesheets", "/admin/styles"
|
35
|
+
add_item "Javascripts", "/admin/scripts"
|
36
|
+
end
|
37
|
+
|
38
|
+
ApplicationHelper.module_eval do
|
39
|
+
def filter_options_for_select_with_sheet_restrictions(selected=nil)
|
40
|
+
sheet_filters = SheetsExtension.stylesheet_filters + SheetsExtension.javascript_filters
|
41
|
+
filters = TextFilter.descendants - sheet_filters
|
42
|
+
options_for_select([[t('select.none'), '']] + filters.map { |s| s.filter_name }.sort, selected)
|
43
|
+
end
|
44
|
+
alias_method_chain :filter_options_for_select, :sheet_restrictions
|
45
|
+
end
|
46
|
+
|
47
|
+
# Will only be called in 0.9.1 and below, avoid redeclaring
|
48
|
+
unless Page.respond_to?('in_menu')
|
49
|
+
Page.class_eval do
|
50
|
+
class_inheritable_accessor :in_menu
|
51
|
+
self.in_menu = true
|
52
|
+
|
53
|
+
class << self
|
54
|
+
alias_method :in_menu?, :in_menu
|
55
|
+
alias_method :in_menu, :in_menu=
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
Page.class_eval do
|
61
|
+
def sheet?
|
62
|
+
self.class.included_modules.include?(Sheet::Instance)
|
63
|
+
end
|
64
|
+
|
65
|
+
include JavascriptTags
|
66
|
+
include StylesheetTags
|
67
|
+
end
|
68
|
+
|
69
|
+
Admin::NodeHelper.module_eval do
|
70
|
+
def render_node_with_sheets(page, locals = {})
|
71
|
+
unless page.sheet?
|
72
|
+
render_node_without_sheets(page, locals)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
alias_method_chain :render_node, :sheets
|
76
|
+
end
|
77
|
+
|
78
|
+
SiteController.class_eval do
|
79
|
+
cattr_writer :sheet_cache_timeout
|
80
|
+
|
81
|
+
def self.sheet_cache_timeout
|
82
|
+
@@sheet_cache_timeout ||= 30.days
|
83
|
+
end
|
84
|
+
|
85
|
+
def set_cache_control_with_sheets
|
86
|
+
if @page.sheet?
|
87
|
+
expires_in self.class.sheet_cache_timeout, :public => true, :private => false
|
88
|
+
else
|
89
|
+
set_cache_control_without_sheets
|
90
|
+
end
|
91
|
+
end
|
92
|
+
alias_method_chain :set_cache_control, :sheets
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../../spec_helper"
|
2
|
+
|
3
|
+
describe Admin::ScriptsController do
|
4
|
+
dataset :users, :home_page, :javascripts
|
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 JavascriptPages" do
|
16
|
+
controller.class.model_class.should == JavascriptPage
|
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_script_path(params[:id]))
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should show xml when format is xml" do
|
27
|
+
javascript = JavascriptPage.first
|
28
|
+
get :show, :id => javascript.id, :format => "xml"
|
29
|
+
response.body.should == javascript.to_xml
|
30
|
+
end
|
31
|
+
end
|
32
|
+
describe "upload" do
|
33
|
+
it "should respond with a bad request header if given no upload" do
|
34
|
+
post :upload
|
35
|
+
response.code.should == "400"
|
36
|
+
end
|
37
|
+
it "should redirect to the index action" do
|
38
|
+
uploaded_file = mock(ActionController::UploadedFile).as_null_object
|
39
|
+
uploaded_file.stub!(:original_filename).and_return('test.doc')
|
40
|
+
uploaded_file.stub!(:read).and_return("Some content")
|
41
|
+
uploaded_file.stub!(:size).and_return(100)
|
42
|
+
uploaded_file.stub!(:kind_of?).with(ActionController::UploadedFile).and_return(true)
|
43
|
+
post :upload, :upload => {:upload => uploaded_file}
|
44
|
+
response.should redirect_to(admin_scripts_path)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "with invalid javascript id" do
|
49
|
+
before do
|
50
|
+
@parameters = {:id => 999}
|
51
|
+
end
|
52
|
+
it "should redirect the edit action to the index action" do
|
53
|
+
get :edit, @parameters
|
54
|
+
response.should redirect_to(admin_scripts_path)
|
55
|
+
end
|
56
|
+
it "should say that the 'JavascriptPage could not be found.' after the edit action" do
|
57
|
+
get :edit, @parameters
|
58
|
+
flash[:notice].should match(/could not be found/)
|
59
|
+
end
|
60
|
+
it 'should redirect the update action to the index action' do
|
61
|
+
put :update, @parameters
|
62
|
+
response.should redirect_to(admin_scripts_path)
|
63
|
+
end
|
64
|
+
it "should say that the 'JavascriptPage could not be found.' after the update action" do
|
65
|
+
put :update, @parameters
|
66
|
+
flash[:notice].should match(/could not be found/)
|
67
|
+
end
|
68
|
+
it 'should redirect the destroy action to the index action' do
|
69
|
+
delete :destroy, @parameters
|
70
|
+
response.should redirect_to(admin_scripts_path)
|
71
|
+
end
|
72
|
+
it "should say that the 'JavascriptPage could not be found.' after the destroy action" do
|
73
|
+
delete :destroy, @parameters
|
74
|
+
flash[:notice].should match(/could not be found/)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
{:get => [:index, :show, :new, :edit],
|
79
|
+
:post => [:create, :upload],
|
80
|
+
:put => [:update],
|
81
|
+
:delete => [:destroy]}.each do |method, actions|
|
82
|
+
actions.each do |action|
|
83
|
+
it "should require login to access the #{action} action" do
|
84
|
+
logout
|
85
|
+
lambda { send(method, action, :id => page_id(:site_js)) }.should require_login
|
86
|
+
end
|
87
|
+
|
88
|
+
if action == :show
|
89
|
+
it "should request authentication for API access on show" do
|
90
|
+
logout
|
91
|
+
send(method, action, :id => page_id(:site_js), :format => "xml")
|
92
|
+
response.response_code.should == 401
|
93
|
+
end
|
94
|
+
else
|
95
|
+
it "should allow access to designers for the #{action} action" do
|
96
|
+
lambda {
|
97
|
+
send(method, action, :id => page_id(:site_js))
|
98
|
+
}.should restrict_access(:allow => [users(:designer)],
|
99
|
+
:url => '/admin/pages')
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should allow access to admins for the #{action} action" do
|
103
|
+
lambda {
|
104
|
+
send(method, action, :id => page_id(:site_js))
|
105
|
+
}.should restrict_access(:allow => [users(:designer)],
|
106
|
+
:url => '/admin/pages')
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should deny non-designers and non-admins for the #{action} action" do
|
110
|
+
lambda {
|
111
|
+
send(method, action, :id => JavascriptPage.first.id)
|
112
|
+
}.should restrict_access(:deny => [users(:non_admin), users(:existing)],
|
113
|
+
:url => '/admin/pages')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should clear the page cache when saved" do
|
120
|
+
Radiant::Cache.should_receive(:clear)
|
121
|
+
put :update, :id => page_id(:site_js), :javascript => {:content => "Foobar."}
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../../spec_helper"
|
2
|
+
|
3
|
+
describe Admin::StylesController do
|
4
|
+
dataset :users, :home_page, :stylesheets
|
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 StylesheetPages" do
|
16
|
+
controller.class.model_class.should == StylesheetPage
|
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_style_path(params[:id]))
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should show xml when format is xml" do
|
27
|
+
stylesheet = StylesheetPage.first
|
28
|
+
get :show, :id => stylesheet.id, :format => "xml"
|
29
|
+
response.body.should == stylesheet.to_xml
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "upload" do
|
34
|
+
it "should respond with a bad request header if given no upload" do
|
35
|
+
post :upload
|
36
|
+
response.code.should == "400"
|
37
|
+
end
|
38
|
+
it "should redirect to the index action" do
|
39
|
+
uploaded_file = mock(ActionController::UploadedFile).as_null_object
|
40
|
+
uploaded_file.stub!(:original_filename).and_return('test.doc')
|
41
|
+
uploaded_file.stub!(:read).and_return("Some content")
|
42
|
+
uploaded_file.stub!(:size).and_return(100)
|
43
|
+
uploaded_file.stub!(:kind_of?).with(ActionController::UploadedFile).and_return(true)
|
44
|
+
post :upload, :upload => {:upload => uploaded_file}
|
45
|
+
response.should redirect_to(admin_styles_path)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "with invalid stylesheet id" do
|
50
|
+
before do
|
51
|
+
@parameters = {:id => 999}
|
52
|
+
end
|
53
|
+
it "should redirect the edit action to the index action" do
|
54
|
+
get :edit, @parameters
|
55
|
+
response.should redirect_to(admin_styles_path)
|
56
|
+
end
|
57
|
+
it "should say that the 'StylesheetPage could not be found.' after the edit action" do
|
58
|
+
get :edit, @parameters
|
59
|
+
flash[:notice].should match(/could not be found/)
|
60
|
+
end
|
61
|
+
it 'should redirect the update action to the index action' do
|
62
|
+
put :update, @parameters
|
63
|
+
response.should redirect_to(admin_styles_path)
|
64
|
+
end
|
65
|
+
it "should say that the 'StylesheetPage could not be found.' after the update action" do
|
66
|
+
put :update, @parameters
|
67
|
+
flash[:notice].should match(/could not be found/)
|
68
|
+
end
|
69
|
+
it 'should redirect the destroy action to the index action' do
|
70
|
+
delete :destroy, @parameters
|
71
|
+
response.should redirect_to(admin_styles_path)
|
72
|
+
end
|
73
|
+
it "should say that the 'StylesheetPage could not be found.' after the destroy action" do
|
74
|
+
delete :destroy, @parameters
|
75
|
+
flash[:notice].should match(/could not be found/)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
{:get => [:index, :show, :new, :edit],
|
80
|
+
:post => [:create, :upload],
|
81
|
+
:put => [:update],
|
82
|
+
:delete => [:destroy]}.each do |method, actions|
|
83
|
+
actions.each do |action|
|
84
|
+
it "should require login to access the #{action} action" do
|
85
|
+
logout
|
86
|
+
lambda { send(method, action, :id => page_id(:site_css)) }.should require_login
|
87
|
+
end
|
88
|
+
|
89
|
+
if action == :show
|
90
|
+
it "should request authentication for API access on show" do
|
91
|
+
logout
|
92
|
+
send(method, action, :id => page_id(:site_css), :format => "xml")
|
93
|
+
response.response_code.should == 401
|
94
|
+
end
|
95
|
+
else
|
96
|
+
it "should allow access to designers for the #{action} action" do
|
97
|
+
lambda {
|
98
|
+
send(method, action, :id => page_id(:site_css))
|
99
|
+
}.should restrict_access(:allow => [users(:designer)],
|
100
|
+
:url => '/admin/pages')
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should allow access to admins for the #{action} action" do
|
104
|
+
lambda {
|
105
|
+
send(method, action, :id => page_id(:site_css))
|
106
|
+
}.should restrict_access(:allow => [users(:designer)],
|
107
|
+
:url => '/admin/pages')
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should deny non-designers and non-admins for the #{action} action" do
|
111
|
+
lambda {
|
112
|
+
send(method, action, :id => StylesheetPage.first.id)
|
113
|
+
}.should restrict_access(:deny => [users(:non_admin), users(:existing)],
|
114
|
+
:url => '/admin/pages')
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should clear the page cache when saved" do
|
121
|
+
Radiant::Cache.should_receive(:clear)
|
122
|
+
put :update, :id => page_id(:site_css), :stylesheet => {:content => "Foobar."}
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class JavascriptsDataset < Dataset::Base
|
2
|
+
uses :home_page
|
3
|
+
|
4
|
+
def load
|
5
|
+
create_page 'js', :slug => 'js', :class_name => 'JavascriptPage', :parent_id => pages(:home).id do
|
6
|
+
create_page 'site.js', :slug => 'site.js', :class_name => 'JavascriptPage' do
|
7
|
+
create_page_part 'site_js_body', :name => 'body', :content => 'alert("site!");'
|
8
|
+
end
|
9
|
+
create_page 'coffee.js', :slug => 'coffee.js', :class_name => 'JavascriptPage' do
|
10
|
+
create_page_part 'coffee_js_body', :name => 'body', :content => 'alert("site!");', :filter_id => 'Coffee'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class StylesheetsDataset < Dataset::Base
|
2
|
+
uses :home_page
|
3
|
+
|
4
|
+
def load
|
5
|
+
create_page "css", :slug => 'css', :class_name => 'StylesheetPage' do
|
6
|
+
create_page "site.css", :slug => 'site.css', :class_name => 'StylesheetPage' do
|
7
|
+
create_page_part 'site_css_body', :name => 'body', :content => 'p { color: blue; }'
|
8
|
+
end
|
9
|
+
create_page "sassy.sass", :slug => 'sassy.sass', :class_name => 'StylesheetPage' do
|
10
|
+
create_page_part 'sass_body', :name => 'body', :content => 'header
|
11
|
+
background: red', :filter_id => 'Sass'
|
12
|
+
end
|
13
|
+
create_page "container.css", :body => '<r:stylesheet slug="sassy.css" />', :class_name => 'StylesheetPage'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# require 'spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
3
|
+
|
4
|
+
describe "Javascript Tags" do
|
5
|
+
dataset :javascripts
|
6
|
+
|
7
|
+
let(:page){ pages(:home) }
|
8
|
+
let(:javascript_page){ pages(:site_js)}
|
9
|
+
|
10
|
+
describe "<r:javascript>" do
|
11
|
+
subject { page }
|
12
|
+
it { should render(%{<r:javascript />}).with_error("`javascript' tag must contain a `slug' attribute.") }
|
13
|
+
it { should render(%{<r:javascript slug="bogus" />}).with_error("javascript bogus not found") }
|
14
|
+
it { should render(%{<r:javascript slug="site.js" />}).as('alert("site!");') }
|
15
|
+
it { should render(%{<r:javascript slug="site.js" as="url" />}).as("/js/site.js?#{javascript_page.updated_at.to_i}") }
|
16
|
+
it { should render(%{<r:javascript slug="site.js" as="link" />}).as(%{<script type="#{javascript_page.headers['Content-Type']}" src="#{javascript_page.url.gsub(/\/$/,'')}?#{javascript_page.updated_at.to_i.to_s}"></script>}) }
|
17
|
+
it { should render(%{<r:javascript slug="site.js" as="link" type="special/type" />}).as(%{<script type="special/type" src="#{javascript_page.url.gsub(/\/$/,'')}?#{javascript_page.updated_at.to_i.to_s}"></script>}) }
|
18
|
+
it { should render(%{<r:javascript slug="site.js" as="link" something="custom" />}).as(%{<script type="#{javascript_page.headers['Content-Type']}" src="#{javascript_page.url.gsub(/\/$/,'')}?#{javascript_page.updated_at.to_i.to_s}" something="custom"></script>}) }
|
19
|
+
it { should render(%{<r:javascript slug="site.js" as="inline" />}).as(%{<script type="#{javascript_page.headers['Content-Type']}">
|
20
|
+
//<![CDATA[
|
21
|
+
alert("site!");
|
22
|
+
//]]>
|
23
|
+
</script>}) }
|
24
|
+
it { should render(%{<r:javascript slug="site.js" as="inline" type="special/type" />}).as(%{<script type="special/type">
|
25
|
+
//<![CDATA[
|
26
|
+
alert("site!");
|
27
|
+
//]]>
|
28
|
+
</script>}) }
|
29
|
+
it { should render(%{<r:javascript slug="site.js" as="inline" something="custom" />}).as(%{<script type="#{javascript_page.headers['Content-Type']}" something="custom">
|
30
|
+
//<![CDATA[
|
31
|
+
alert("site!");
|
32
|
+
//]]>
|
33
|
+
</script>}) }
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# require 'spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
3
|
+
|
4
|
+
describe "Stylesheet Tags" do
|
5
|
+
dataset :stylesheets
|
6
|
+
|
7
|
+
let(:page){ pages(:home) }
|
8
|
+
|
9
|
+
describe "<r:stylesheet>" do
|
10
|
+
let(:site_css){pages(:site_css)}
|
11
|
+
subject { page }
|
12
|
+
it { should render(%{<r:stylesheet />}).with_error("`stylesheet' tag must contain a `slug' attribute.") }
|
13
|
+
it { should render(%{<r:stylesheet slug="bogus" />}).with_error("stylesheet bogus not found") }
|
14
|
+
it { should render(%{<r:stylesheet slug="site.css" />}).as("p { color: blue; }") }
|
15
|
+
it { should render(%{<r:stylesheet slug="site.css" as="url" />}).as("/css/site.css?#{site_css.updated_at.to_i}") }
|
16
|
+
it { should render(%{<r:stylesheet slug="site.css" as="link" />}).as(%{<link rel="stylesheet" type="text/css" href="/css/site.css?#{site_css.updated_at.to_i.to_s}" />}) }
|
17
|
+
it { should render(%{<r:stylesheet slug="site.css" as="link" type="special/type" />}).as(%{<link rel="stylesheet" type="special/type" href="/css/site.css?#{site_css.updated_at.to_i.to_s}" />}) }
|
18
|
+
it { should render(%{<r:stylesheet slug="site.css" as="link" something="custom" />}).as(%{<link rel="stylesheet" type="text/css" href="/css/site.css?#{site_css.updated_at.to_i.to_s}" something="custom" />}) }
|
19
|
+
it { should render(%{<r:stylesheet slug="site.css" as="link" rel="alternate" />}).as(%{<link rel="alternate" type="text/css" href="/css/site.css?#{site_css.updated_at.to_i.to_s}" />}) }
|
20
|
+
it { should render(%{<r:stylesheet slug="site.css" as="inline" />}).as(%{<style type="text/css">
|
21
|
+
/*<![CDATA[*/
|
22
|
+
p { color: blue; }
|
23
|
+
/*]]>*/
|
24
|
+
</style>}) }
|
25
|
+
it { should render(%{<r:stylesheet slug="site.css" as="inline" type="special/type" />}).as(%{<style type="special/type">
|
26
|
+
/*<![CDATA[*/
|
27
|
+
p { color: blue; }
|
28
|
+
/*]]>*/
|
29
|
+
</style>}) }
|
30
|
+
it { should render(%{<r:stylesheet slug="site.css" as="inline" something="custom" />}).as(%{<style type="text/css" something="custom">
|
31
|
+
/*<![CDATA[*/
|
32
|
+
p { color: blue; }
|
33
|
+
/*]]>*/
|
34
|
+
</style>}) }
|
35
|
+
it "should apply text filters when outputing content" do
|
36
|
+
css_result_from_sass = Sass::Engine.new(pages(:sassy_sass).part('body').content, Compass.sass_engine_options || {}).render
|
37
|
+
site_css.should render(%{<r:stylesheet slug="sassy.sass" />}).as(css_result_from_sass)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe JavascriptPage do
|
4
|
+
dataset :javascripts
|
5
|
+
let(:javascript){ pages(:js) }
|
6
|
+
let(:site_js){ pages(:site_js) }
|
7
|
+
|
8
|
+
subject{ javascript }
|
9
|
+
its(:cache?) { should be_true }
|
10
|
+
its(:sheet?) { should be_true }
|
11
|
+
its(:virtual?) { should be_true }
|
12
|
+
its(:layout) { should be_nil }
|
13
|
+
|
14
|
+
describe '.root' do
|
15
|
+
subject{ JavascriptPage }
|
16
|
+
its(:root) { should == javascript }
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '.new_with_defaults' do
|
20
|
+
describe '#parts' do
|
21
|
+
let(:parts){ JavascriptPage.new_with_defaults.parts }
|
22
|
+
subject{ parts }
|
23
|
+
its(:length) { should == 1 }
|
24
|
+
it "should have a body part" do
|
25
|
+
parts[0].name.should == 'body'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#headers' do
|
31
|
+
it "should have a 'Content-Type' of 'text/javascript'" do
|
32
|
+
javascript.headers['Content-Type'].should == 'text/javascript'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#find_by_path' do
|
37
|
+
context 'with a valid url' do
|
38
|
+
it 'should return the child found by the given slug' do
|
39
|
+
javascript.find_by_path('/js/site.js').should == site_js
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when validating a new page' do
|
45
|
+
it "should automatically set the title to the given slug" do
|
46
|
+
j = JavascriptPage.new(:slug => 'site.js')
|
47
|
+
j.valid?
|
48
|
+
j.title.should == 'site.js'
|
49
|
+
end
|
50
|
+
it "should automatically set the breadcrumb to the given slug" do
|
51
|
+
j = JavascriptPage.new(:slug => 'site.js')
|
52
|
+
j.valid?
|
53
|
+
j.breadcrumb.should == 'site.js'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when saving a new page' do
|
58
|
+
subject { s = JavascriptPage.new_with_defaults
|
59
|
+
s.slug = 'test.js'
|
60
|
+
s.save!
|
61
|
+
s }
|
62
|
+
its(:status){ should == Status[:published] }
|
63
|
+
its(:status_id){ should == Status[:published].id }
|
64
|
+
|
65
|
+
its(:published_at){ should_not be_nil }
|
66
|
+
it "should have a published_at greater than or equal to the current time" do
|
67
|
+
subject.published_at.to_i.should <= Time.zone.now.to_i
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'with the default page status set to draft' do
|
71
|
+
it 'should save a new page with a published status' do
|
72
|
+
Radiant::Config['defaults.page.status'] = 'draft'
|
73
|
+
new_sheet = JavascriptPage.new_with_defaults
|
74
|
+
new_sheet.slug = 'published.js'
|
75
|
+
new_sheet.save!
|
76
|
+
new_sheet.status.should == Status[:published]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|