theme_support 3.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/Gemfile +1 -0
- data/LICENSE +20 -0
- data/README +223 -0
- data/VERSION +1 -0
- data/lib/generators/theme/USAGE +15 -0
- data/lib/generators/theme/templates/about.markdown +5 -0
- data/lib/generators/theme/templates/layout.html.erb +11 -0
- data/lib/generators/theme/templates/layout.liquid +11 -0
- data/lib/generators/theme/templates/preview.png +0 -0
- data/lib/generators/theme/templates/theme.css +7 -0
- data/lib/generators/theme/templates/theme.yml +4 -0
- data/lib/generators/theme/templates/views_readme +13 -0
- data/lib/generators/theme/theme_generator.rb +21 -0
- data/lib/theme_support/helpers/erb_theme_tags.rb +63 -0
- data/lib/theme_support/helpers/liquid_theme_tags.rb +31 -0
- data/lib/theme_support/patches/abstractcontroller_ex.rb +54 -0
- data/lib/theme_support/patches/actionmailer_ex.rb +93 -0
- data/lib/theme_support/patches/routeset_ex.rb +14 -0
- data/lib/theme_support/railtie.rb +10 -0
- data/lib/theme_support/tasks.rb +39 -0
- data/lib/theme_support/theme.rb +58 -0
- data/lib/theme_support/theme_controller.rb +59 -0
- data/lib/theme_support/theme_error.rb +4 -0
- data/lib/theme_support/theme_helper.rb +3 -0
- data/lib/theme_support.rb +23 -0
- data/theme_support.gemspec +28 -0
- metadata +92 -0
data/Gemfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
gem 'rails', '3.0.0'
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2005 Matt McCray, based on code from Typo by Tobias Luetke
|
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 SOFTWAR
|
data/README
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
This theme support has been turned into a gem and ported to Rails 3.0 by Pierre Yager and Sylvain Claudel (http://www.crisalid.com)
|
2
|
+
|
3
|
+
= Theme Support for Rails Applications
|
4
|
+
|
5
|
+
This plugin provides support for themes to the rails application environment.
|
6
|
+
It supports theme specific images, stylesheets, javascripts, and views. The
|
7
|
+
views can be in ERb (rhtml) or liquid formats. Optionally, you can configure
|
8
|
+
the theme system to ignore any templates except liquid ones.
|
9
|
+
|
10
|
+
|
11
|
+
== Creating a Theme using Theme Support ==
|
12
|
+
|
13
|
+
This plugin automatically makes any patches needed for theme support. Use the following command to create the file structure needed (replace YOUR_THEME_NAME with the name of your theme):
|
14
|
+
|
15
|
+
rails generate theme YOUR_THEME_NAME
|
16
|
+
|
17
|
+
The following theme folder structure will generate in your app code:
|
18
|
+
|
19
|
+
$app_root
|
20
|
+
themes/
|
21
|
+
[theme_name]
|
22
|
+
images/
|
23
|
+
stylesheets/
|
24
|
+
javascripts/
|
25
|
+
views/ <- you can override application views
|
26
|
+
layouts/ <- layout .rhtml or .liquid templates
|
27
|
+
about.markdown
|
28
|
+
preview.png
|
29
|
+
|
30
|
+
==Caveats==
|
31
|
+
When run in production mode, it will automatically cache the theme files so that
|
32
|
+
the web-server will deliver them in subsequent requests.
|
33
|
+
|
34
|
+
It bears noting that, like Typo, this will mean your apache/fcgi process will need
|
35
|
+
write permissions. This could be a possible security vulnerability. For those of us using passanger and mongrels, more information will follow.
|
36
|
+
|
37
|
+
With that in mind, it is best to pre-cache all of the theme files by using the
|
38
|
+
included rake tasks:
|
39
|
+
|
40
|
+
$ rake themes:cache:create
|
41
|
+
|
42
|
+
The theme file cache generates the following file structure:
|
43
|
+
|
44
|
+
$app_root
|
45
|
+
public/
|
46
|
+
themes/
|
47
|
+
[theme_name]/
|
48
|
+
images/
|
49
|
+
stylesheets/
|
50
|
+
javascripts/
|
51
|
+
|
52
|
+
There are other rake tasks available:
|
53
|
+
|
54
|
+
- themes:cache:create
|
55
|
+
- themes:cache:remove
|
56
|
+
- themes:cache:update
|
57
|
+
|
58
|
+
== Using your new Theme in your app code ==
|
59
|
+
You specify which theme to use in your controller by using the 'theme' helper.
|
60
|
+
It's used just like the 'layout' helper. In fact, you can use them both
|
61
|
+
simultaneously. The following will render actions using the 'application' layout
|
62
|
+
in the 'blue_bird' theme (theme/blue_bird/views/layouts/application.html.erb):
|
63
|
+
|
64
|
+
class ApplicationController < ActionController::Base
|
65
|
+
layout 'application'
|
66
|
+
|
67
|
+
theme 'blue_bird'
|
68
|
+
|
69
|
+
...
|
70
|
+
end
|
71
|
+
|
72
|
+
You can also defer the theme lookup to a controller method:
|
73
|
+
|
74
|
+
class ApplicationController < ActionController::Base
|
75
|
+
layout 'application'
|
76
|
+
|
77
|
+
theme :get_theme
|
78
|
+
|
79
|
+
def get_theme
|
80
|
+
|
81
|
+
# If you let the user choose their own theme, you might
|
82
|
+
# add a User#theme field...
|
83
|
+
|
84
|
+
current_user.theme
|
85
|
+
end
|
86
|
+
|
87
|
+
...
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
Note: By setting the theme in the ApplicationController you can set
|
92
|
+
the theme for the whole application.
|
93
|
+
|
94
|
+
== Using themes in your Views ==
|
95
|
+
|
96
|
+
ERb:
|
97
|
+
----
|
98
|
+
In your application views, there are theme specific helper tags
|
99
|
+
available to you.
|
100
|
+
|
101
|
+
- theme_image_tag
|
102
|
+
- theme_image_path
|
103
|
+
- theme_javascript_include_tag
|
104
|
+
- theme_javascript_path
|
105
|
+
- theme_stylesheet_link_tag
|
106
|
+
- theme_stylesheet_path
|
107
|
+
|
108
|
+
ERB Example :
|
109
|
+
# Note my theme is called "blue_bird" and my css file is called "style.css"
|
110
|
+
<%= theme_stylesheet_link_tag 'blue_bird', 'style' %>
|
111
|
+
|
112
|
+
LIQUID:
|
113
|
+
-------
|
114
|
+
Liquid templates there is a single helper, themeitem, that will determine
|
115
|
+
the path base on the theme file extension. Here's how you'd use it:
|
116
|
+
|
117
|
+
<link rel="StyleSheet" href="{% themeitem %} default.css {% endthemeitem %}" />
|
118
|
+
...
|
119
|
+
<img src="{% themeitem %} logo.png {% endthemeitem %}" />
|
120
|
+
|
121
|
+
The output from those two calls are:
|
122
|
+
|
123
|
+
<link rel="StyleSheet" href="/themes/[current_theme]/stylesheets/default.css" />
|
124
|
+
...
|
125
|
+
<img src="/themes/[current_theme]/images/logo.png" />
|
126
|
+
|
127
|
+
== ActionMailer Support ==
|
128
|
+
|
129
|
+
New in version 1.4 is ActionMailer support. Note, this is still experimental. However,
|
130
|
+
if you would like your themes to be able to override your ActionMailer views, you can
|
131
|
+
send the theme in your deliver_* method call. For example, assuming we have an ActionMailer
|
132
|
+
class named Mailer, and have implemented theme_support as shown above, here's how you would
|
133
|
+
allowing themes in emails:
|
134
|
+
|
135
|
+
|
136
|
+
def send_email
|
137
|
+
Mailer.deliver_my_email( 'a variable', :theme=>get_theme )
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
== Contributors
|
142
|
+
|
143
|
+
The theme_support plugin includes patches from the following:
|
144
|
+
|
145
|
+
* agkr
|
146
|
+
* D.J. Vogel
|
147
|
+
* Pierre Yager (zedalaye) and Sylvain Claudel (riven)
|
148
|
+
|
149
|
+
Thanks guys!
|
150
|
+
|
151
|
+
== Changelog
|
152
|
+
|
153
|
+
3.0.0 - Converted theme_support plugin into a Rails 3.0 compatible gem.
|
154
|
+
|
155
|
+
1.5.0 - Using jystewart's fork of the original github theme_support, the helper methods should
|
156
|
+
work in Rails 2.2.2. ReadME fixed. Layouts changed to reflect Rails conventions: e.g.,
|
157
|
+
layouts are now under views, application.html.erb used to reflect standard naming
|
158
|
+
conventions.
|
159
|
+
|
160
|
+
1.4.0 - Better support for Rails 1.1+. Updated the liquid themeitem tag.
|
161
|
+
Liquid layouts are no longer generated by default.Added a couple
|
162
|
+
of patches. One allows theme sub-directories. For example, you
|
163
|
+
can have:
|
164
|
+
|
165
|
+
[theme_dir]
|
166
|
+
stylesheets/
|
167
|
+
admin/
|
168
|
+
main.css
|
169
|
+
|
170
|
+
Added experimental support for themeing ActionMailer classes.
|
171
|
+
They work as normal, if you want to all theme's to override the
|
172
|
+
.rhtml|.liquid views, send the theme in the deliver_* method. For
|
173
|
+
example:
|
174
|
+
|
175
|
+
Mailer.deliver_signup( user, :theme=>get_theme() )
|
176
|
+
|
177
|
+
In that example, `get_theme` is a method on the controller and at
|
178
|
+
the top we've used:
|
179
|
+
|
180
|
+
layout 'default'
|
181
|
+
theme :get_theme
|
182
|
+
|
183
|
+
1.3.0 - The theme_system component is no longer needed. All of the
|
184
|
+
theme support is driven by a single plugin named, oddly enough,
|
185
|
+
'theme_support'. Also improved theme route support. Instead of
|
186
|
+
overriding RouteSet#reload, RouteSet#draw is overridden, making
|
187
|
+
the theme support entirely transparent -- hopefully ;-)
|
188
|
+
|
189
|
+
1.2.2 - More Bug-fixes.
|
190
|
+
|
191
|
+
1.2.1 - Bug-fixes and documentation clean up.
|
192
|
+
|
193
|
+
1.2.0 - Updated actionview_ex with the new render_file additions from
|
194
|
+
Typo. Renamed the rake tasks so that they all start with
|
195
|
+
'theme' (theme_create_cache, theme_remove_cache,
|
196
|
+
theme_update_cache). You can update the system files by running:
|
197
|
+
|
198
|
+
$ ./script/generate theme _system_
|
199
|
+
|
200
|
+
Full support for Liquid templates, as well as an option to only
|
201
|
+
allow Liquid templates in a theme.
|
202
|
+
|
203
|
+
1.1.1 - Added rake tasks for pre-caching the themes, and removing the
|
204
|
+
cached themes.
|
205
|
+
|
206
|
+
1.1.0 - [Breaking] Abstraction of the Typo theme system. The themes are
|
207
|
+
now the same as is used in Typo. The theme engine itself is a
|
208
|
+
combination of plugins and a component. No more symlinks, thank
|
209
|
+
goodness.
|
210
|
+
|
211
|
+
1.0.2 - The current_theme is now retrieved from the controller. Where
|
212
|
+
symlinks are created on *nix systems, shortcuts are created
|
213
|
+
on Windows if Win32Utils is installed.
|
214
|
+
|
215
|
+
1.0.1 - Added 'themes' directory, theme definition file, and symlinks
|
216
|
+
to the appropriate directories on supported platforms.
|
217
|
+
|
218
|
+
1.0.0 - Initial release
|
219
|
+
|
220
|
+
|
221
|
+
---
|
222
|
+
Copyright (c) 2005 Matt McCray, based on code from Typo by Tobias Luetke
|
223
|
+
released under the MIT license
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.0.0
|
@@ -0,0 +1,15 @@
|
|
1
|
+
NAME
|
2
|
+
theme - Creates the folder structure for a new theme
|
3
|
+
|
4
|
+
SYNOPSIS
|
5
|
+
theme [theme_name]
|
6
|
+
|
7
|
+
DESCRIPTION
|
8
|
+
This generator creates the folder structure for a new theme. It creates all of
|
9
|
+
the folders for your theme content (images, stylesheets, javascripts, layouts,
|
10
|
+
and views).
|
11
|
+
|
12
|
+
EXAMPLE
|
13
|
+
rails generate theme default
|
14
|
+
|
15
|
+
This will generate the file structure for a theme named 'default'.
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html>
|
4
|
+
<head>
|
5
|
+
<title></title>
|
6
|
+
<%%= theme_stylesheet_link_tag '<%= file_name %>' %>
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<%= yield %>
|
10
|
+
</body>
|
11
|
+
</html>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html>
|
4
|
+
<head>
|
5
|
+
<title></title>
|
6
|
+
<link href="{% themeitem %} <%= file_name %>.css {% endthemeitem %}" media="screen" rel="Stylesheet" type="text/css" />
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
{{ content_for_layout }}
|
10
|
+
</body>
|
11
|
+
</html>
|
Binary file
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Overriding Views
|
2
|
+
|
3
|
+
You can override views by putting custom `html.erb` or `liquid`
|
4
|
+
templates in this directory. You use the same folder structure
|
5
|
+
as Rails:
|
6
|
+
|
7
|
+
views/
|
8
|
+
[controller_name]/
|
9
|
+
_overriden_partial.html.erb
|
10
|
+
overriden_action.html.erb
|
11
|
+
|
12
|
+
*Note:* These are overrides! They will only work if they have
|
13
|
+
a matching view in the main rails `app/views` folder.
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class ThemeGenerator < Rails::Generators::NamedBase
|
2
|
+
source_root File.expand_path("../templates", __FILE__)
|
3
|
+
|
4
|
+
def create_theme_directories
|
5
|
+
theme_root = File.join("themes", file_name)
|
6
|
+
|
7
|
+
empty_directory theme_root
|
8
|
+
|
9
|
+
%w{images javascripts views stylesheets}.each do |d|
|
10
|
+
empty_directory File.join(theme_root, d)
|
11
|
+
end
|
12
|
+
|
13
|
+
empty_directory File.join(theme_root, "views", "layouts")
|
14
|
+
|
15
|
+
copy_file 'about.markdown', File.join(theme_root, "about.markdown")
|
16
|
+
copy_file 'preview.png', File.join(theme_root, 'images', 'preview.png' )
|
17
|
+
copy_file 'theme.css', File.join(theme_root, 'stylesheets', "#{file_name}.css" )
|
18
|
+
copy_file 'layout.html.erb', File.join(theme_root, 'views', 'layouts', 'application.html.erb' )
|
19
|
+
copy_file 'views_readme', File.join(theme_root, 'views', 'views_readme.txt' )
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#
|
2
|
+
# These are theme helper tags
|
3
|
+
#
|
4
|
+
module ActionView::Helpers::AssetTagHelper
|
5
|
+
# returns the public path to a theme stylesheet
|
6
|
+
def theme_stylesheet_path( source=nil, theme=nil )
|
7
|
+
theme = theme || controller.current_theme
|
8
|
+
compute_public_path(source || "theme", "themes/#{theme}/stylesheets", 'css')
|
9
|
+
end
|
10
|
+
|
11
|
+
# returns the path to a theme image
|
12
|
+
def theme_image_path( source, theme=nil )
|
13
|
+
theme = theme || controller.current_theme
|
14
|
+
compute_public_path(source, "themes/#{theme}/images", 'png')
|
15
|
+
end
|
16
|
+
|
17
|
+
# returns the path to a theme javascript
|
18
|
+
def theme_javascript_path( source, theme=nil )
|
19
|
+
theme = theme || controller.current_theme
|
20
|
+
compute_public_path(source, "themes/#{theme}/javascripts", 'js')
|
21
|
+
end
|
22
|
+
|
23
|
+
# This tag it will automatially include theme specific css files
|
24
|
+
def theme_stylesheet_link_tag(*sources)
|
25
|
+
sources.uniq!
|
26
|
+
options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { }
|
27
|
+
sources.collect { |source|
|
28
|
+
source = theme_stylesheet_path(source)
|
29
|
+
tag("link", { "rel" => "Stylesheet", "type" => "text/css", "media" => "screen", "href" => source }.merge(options))
|
30
|
+
}.join("\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
# This tag will return a theme-specific IMG
|
34
|
+
def theme_image_tag(source, options = {})
|
35
|
+
options.symbolize_keys
|
36
|
+
|
37
|
+
options[:src] = theme_image_path(source)
|
38
|
+
options[:alt] ||= File.basename(options[:src], '.*').split('.').first.capitalize
|
39
|
+
|
40
|
+
if options[:size]
|
41
|
+
options[:width], options[:height] = options[:size].split("x")
|
42
|
+
options.delete :size
|
43
|
+
end
|
44
|
+
|
45
|
+
tag("img", options)
|
46
|
+
end
|
47
|
+
|
48
|
+
# This tag can be used to return theme-specific javscripts
|
49
|
+
def theme_javascript_include_tag(*sources)
|
50
|
+
options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { }
|
51
|
+
if sources.include?(:defaults)
|
52
|
+
sources = sources[0..(sources.index(:defaults))] +
|
53
|
+
@@javascript_default_sources.dup +
|
54
|
+
sources[(sources.index(:defaults) + 1)..sources.length]
|
55
|
+
sources.delete(:defaults)
|
56
|
+
sources << "application" if defined?(Rails.root) && File.exists?("#{Rails.root}/public/javascripts/application.js")
|
57
|
+
end
|
58
|
+
sources.collect { |source|
|
59
|
+
source = theme_javascript_path(source)
|
60
|
+
content_tag("script", "", { "type" => "text/javascript", "src" => source }.merge(options))
|
61
|
+
}.join("\n")
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# A Liquid Tag for retrieving path information for theme specific media
|
2
|
+
#
|
3
|
+
# Returns the path based on the file extension
|
4
|
+
#
|
5
|
+
class Themeitem < Liquid::Block
|
6
|
+
|
7
|
+
@@image_exts = %w( .png .jpg .jpeg .jpe .gif )
|
8
|
+
@@stylesheet_exts = %w( .css )
|
9
|
+
@@javascript_exts = %w( .js .htc )
|
10
|
+
|
11
|
+
def render(context)
|
12
|
+
# Which, if either, of these are correct?
|
13
|
+
base_url = context['request'].relative_url_root || ActionController::Base.asset_host.to_s
|
14
|
+
theme_name = @theme_name || context['active_theme']
|
15
|
+
|
16
|
+
filename = @nodelist.join('').strip
|
17
|
+
ext = File.extname( filename )
|
18
|
+
|
19
|
+
if @@image_exts.include?( ext )
|
20
|
+
"#{base_url}/themes/#{theme_name}/images/#{filename}"
|
21
|
+
|
22
|
+
elsif @@stylesheet_exts.include?( ext )
|
23
|
+
"#{base_url}/themes/#{theme_name}/stylesheets/#{filename}"
|
24
|
+
|
25
|
+
elsif @@javascript_exts.include?( ext )
|
26
|
+
"#{base_url}/themes/#{theme_name}/javascripts/#{filename}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
Liquid::Template.register_tag( 'themeitem', Themeitem )
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class AbstractController::Base
|
2
|
+
attr_accessor :current_theme
|
3
|
+
attr_accessor :force_liquid_template
|
4
|
+
|
5
|
+
# Use this in your controller just like the <tt>layout</tt> macro.
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# theme 'theme_name'
|
9
|
+
#
|
10
|
+
# -or-
|
11
|
+
#
|
12
|
+
# theme :get_theme
|
13
|
+
#
|
14
|
+
# def get_theme
|
15
|
+
# 'theme_name'
|
16
|
+
# end
|
17
|
+
|
18
|
+
#class << self
|
19
|
+
def self.theme(theme_name, conditions = {})
|
20
|
+
# TODO: Allow conditions... (?)
|
21
|
+
write_inheritable_attribute "theme", theme_name
|
22
|
+
theme = ActionView::Base.process_view_paths(File.join("themes", theme_name, "views"))
|
23
|
+
prepend_view_path(theme)
|
24
|
+
end
|
25
|
+
#end
|
26
|
+
|
27
|
+
# Retrieves the current set theme
|
28
|
+
def current_theme(passed_theme=nil)
|
29
|
+
theme = passed_theme || self.class.read_inheritable_attribute("theme")
|
30
|
+
|
31
|
+
@active_theme = case theme
|
32
|
+
when Symbol then send(theme)
|
33
|
+
when Proc then theme.call(self)
|
34
|
+
when String then theme
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
AbstractController::Layouts.module_eval do
|
40
|
+
def _normalize_options(options)
|
41
|
+
super
|
42
|
+
|
43
|
+
if _include_layout?(options)
|
44
|
+
layout = options.key?(:layout) ? options.delete(:layout) : :default
|
45
|
+
value = _layout_for_option(layout)
|
46
|
+
options[:layout] = (value =~ /\blayouts/ ? value : "layouts/#{value}") if value
|
47
|
+
end
|
48
|
+
|
49
|
+
if current_theme
|
50
|
+
theme_path = File.join(Rails.root, "themes", current_theme, "views")
|
51
|
+
options[:layout] = File.join(theme_path, options[:layout])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# Extend the Base ActionController to support themes
|
2
|
+
module ActionMailer
|
3
|
+
class Base
|
4
|
+
|
5
|
+
alias_method :__render, :render
|
6
|
+
alias_method :__initialize, :initialize
|
7
|
+
|
8
|
+
@current_theme = nil
|
9
|
+
|
10
|
+
attr_accessor :current_theme
|
11
|
+
|
12
|
+
def initialize(method_name=nil, *parameters)
|
13
|
+
if parameters[-1].is_a? Hash and (parameters[-1].include? :theme)
|
14
|
+
@current_theme = parameters[-1][:theme]
|
15
|
+
parameters[-1].delete :theme
|
16
|
+
parameters[-1][:current_theme] = @current_theme
|
17
|
+
end
|
18
|
+
create!(method_name, *parameters) if method_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def create!(method_name, *parameters) #:nodoc:
|
22
|
+
initialize_defaults(method_name)
|
23
|
+
__send__(method_name, *parameters)
|
24
|
+
|
25
|
+
tpaths = []
|
26
|
+
tpaths << File.join(Rails.root, "themes", self.current_theme, "views", mailer_name) if self.current_theme
|
27
|
+
tpaths << template_path
|
28
|
+
|
29
|
+
# If an explicit, textual body has not been set, we check assumptions.
|
30
|
+
unless String === @body
|
31
|
+
# First, we look to see if there are any likely templates that match,
|
32
|
+
# which include the content-type in their file name (i.e.,
|
33
|
+
# "the_template_file.text.html.erb", etc.). Only do this if parts
|
34
|
+
# have not already been specified manually.
|
35
|
+
|
36
|
+
if @parts.empty?
|
37
|
+
|
38
|
+
tpaths.each do |tpath|
|
39
|
+
Dir.glob("#{tpath}/#{@template}.*").each do |path|
|
40
|
+
template = template_root["#{tpath}/#{File.basename(path)}"]
|
41
|
+
|
42
|
+
# Skip unless template has a multipart format
|
43
|
+
next unless template && template.multipart?
|
44
|
+
|
45
|
+
@parts << Part.new(
|
46
|
+
:content_type => template.content_type,
|
47
|
+
:disposition => "inline",
|
48
|
+
:charset => charset,
|
49
|
+
:body => render_message(template, @body)
|
50
|
+
)
|
51
|
+
end
|
52
|
+
break if @parts.any?
|
53
|
+
end
|
54
|
+
unless @parts.empty?
|
55
|
+
@content_type = "multipart/alternative"
|
56
|
+
@parts = sort_parts(@parts, @implicit_parts_order)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Then, if there were such templates, we check to see if we ought to
|
61
|
+
# also render a "normal" template (without the content type). If a
|
62
|
+
# normal template exists (or if there were no implicit parts) we render
|
63
|
+
# it.
|
64
|
+
template_exists = @parts.empty?
|
65
|
+
tpaths.each do |tpath|
|
66
|
+
template_exists ||= template_root["#{tpath}/#{@template}"]
|
67
|
+
if template_exists
|
68
|
+
@body = render_message(@template, @body)
|
69
|
+
break
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Finally, if there are other message parts and a textual body exists,
|
74
|
+
# we shift it onto the front of the parts and set the body to nil (so
|
75
|
+
# that create_mail doesn't try to render it in addition to the parts).
|
76
|
+
if !@parts.empty? && String === @body
|
77
|
+
@parts.unshift Part.new(:charset => charset, :body => @body)
|
78
|
+
@body = nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# If this is a multipart e-mail add the mime_version if it is not
|
83
|
+
# already set.
|
84
|
+
@mime_version ||= "1.0" if !@parts.empty?
|
85
|
+
|
86
|
+
# build the mail object itself
|
87
|
+
@mail = create_mail
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ThemeSupport
|
2
|
+
module RoutingExtensions
|
3
|
+
def themeing
|
4
|
+
@set.with_options :controller => 'theme' do |theme|
|
5
|
+
match '/themes/:theme/images/*filename', :to => 'theme#images', :as => 'theme_images'
|
6
|
+
match '/themes/:theme/stylesheets/*filename', :to => 'theme#stylesheets', :as => 'theme_stylesheets'
|
7
|
+
match '/themes/:theme/javascripts/*filename', :to => 'theme#javascript', :as => 'theme_javascript'
|
8
|
+
match '/themes/*whatever', :to => 'theme#error'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
ActionDispatch::Routing::Mapper.send :include, ThemeSupport::RoutingExtensions
|
@@ -0,0 +1,39 @@
|
|
1
|
+
namespace :themes do
|
2
|
+
namespace :cache do
|
3
|
+
|
4
|
+
desc "Creates the cached (public) theme folders"
|
5
|
+
task :create do
|
6
|
+
themes_root = File.join(Rails.root, "themes")
|
7
|
+
for theme in Dir.glob(File.join(themes_root, '*'))
|
8
|
+
|
9
|
+
theme_name = theme.split( File::Separator )[-1]
|
10
|
+
theme_root = File.join(Rails.root, "public", "themes", theme_name)
|
11
|
+
|
12
|
+
puts "Creating #{theme_root}"
|
13
|
+
FileUtils.mkdir_p "#{theme_root}"
|
14
|
+
|
15
|
+
%w{ images stylesheets javascripts }.each do |d|
|
16
|
+
FileUtils.mkdir_p File.join(theme_root, d)
|
17
|
+
Dir.glob(File.join(theme, d, '**', '*')) do |f|
|
18
|
+
final_path = f[theme.length + 1, f.length]
|
19
|
+
if File.stat(f).directory? then
|
20
|
+
FileUtils.mkdir_p File.join(theme_root, final_path) #, :verbose => true
|
21
|
+
else
|
22
|
+
FileUtils.cp f, File.join(theme_root, final_path) #, :verbose => true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Removes the cached (public) theme folders"
|
30
|
+
task :remove do
|
31
|
+
themes_cache = File.join(Rails.root, 'public', 'themes')
|
32
|
+
puts "Removing #{themes_cache}"
|
33
|
+
FileUtils.rm_r themes_cache, :force => true
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "Updates the cached (public) theme folders"
|
37
|
+
task :update => [:remove, :create]
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class Theme
|
2
|
+
cattr_accessor :cache_theme_lookup
|
3
|
+
@@cache_theme_lookup = false
|
4
|
+
|
5
|
+
attr_accessor :name, :title, :description, :preview_image
|
6
|
+
|
7
|
+
def initialize(name)
|
8
|
+
@name = name
|
9
|
+
@title = name.underscore.humanize.titleize
|
10
|
+
@description_html = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def description
|
14
|
+
if @description_html.nil?
|
15
|
+
@description_html = RedCloth.new(File.read( File.join(Theme.path_to_theme(name), "about.markdown") )).to_html(:markdown, :textile) rescue "#{title}"
|
16
|
+
end
|
17
|
+
@description_html
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_preview?
|
21
|
+
File.exists?( File.join( Theme.path_to_theme(name), 'images', 'preview.png' ) ) rescue false
|
22
|
+
end
|
23
|
+
|
24
|
+
def preview_image
|
25
|
+
'preview.png'
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.find_all
|
29
|
+
installed_themes.inject([]) do |array, path|
|
30
|
+
array << theme_from_path(path)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def self.themes_root
|
37
|
+
File.join(Rails.root, "themes")
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.path_to_theme(theme)
|
41
|
+
File.join(themes_root, theme)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.theme_from_path(path)
|
45
|
+
name = path.scan(/[-\w]+$/i).flatten.first
|
46
|
+
self.new(name)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.installed_themes
|
50
|
+
cache_theme_lookup ? @theme_cache ||= search_theme_directory : search_theme_directory
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.search_theme_directory
|
54
|
+
Dir.glob("#{themes_root}/[-_a-zA-Z0-9]*").collect do |file|
|
55
|
+
file if File.directory?(file)
|
56
|
+
end.compact
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# The controller for serving/cacheing theme content...
|
2
|
+
#
|
3
|
+
class ThemeController < ActionController::Base
|
4
|
+
|
5
|
+
after_filter :cache_theme_files
|
6
|
+
|
7
|
+
def stylesheets
|
8
|
+
render_theme_item(:stylesheets, params[:filename].join('/'), params[:theme])
|
9
|
+
end
|
10
|
+
|
11
|
+
def javascript
|
12
|
+
render_theme_item(:javascripts, params[:filename].join('/'), params[:theme], 'text/javascript')
|
13
|
+
end
|
14
|
+
|
15
|
+
def images
|
16
|
+
render_theme_item(:images, params[:filename].to_s, params[:theme])
|
17
|
+
end
|
18
|
+
|
19
|
+
def error
|
20
|
+
render :nothing => true, :status => 404
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def render_theme_item(type, file, theme, mime = mime_for(file))
|
26
|
+
render :text => "Not Found", :status => 404 and return if file.split(%r{[\\/]}).include?("..")
|
27
|
+
send_file "#{Theme.path_to_theme(theme)}/#{type}/#{file}", :type => mime, :content_type => mime, :disposition => 'inline'
|
28
|
+
end
|
29
|
+
|
30
|
+
def cache_theme_files
|
31
|
+
path = request.fullpath
|
32
|
+
begin
|
33
|
+
ThemeController.cache_page( response.body, path )
|
34
|
+
rescue
|
35
|
+
STERR.puts "Cache Exception: #{$!}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def mime_for(filename)
|
40
|
+
case filename.downcase
|
41
|
+
when /\.js$/
|
42
|
+
'text/javascript'
|
43
|
+
when /\.css$/
|
44
|
+
'text/css'
|
45
|
+
when /\.gif$/
|
46
|
+
'image/gif'
|
47
|
+
when /(\.jpg|\.jpeg)$/
|
48
|
+
'image/jpeg'
|
49
|
+
when /\.png$/
|
50
|
+
'image/png'
|
51
|
+
when /\.swf$/
|
52
|
+
'application/x-shockwave-flash'
|
53
|
+
else
|
54
|
+
'application/binary'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Load rake tasks
|
2
|
+
require 'theme_support/railtie'
|
3
|
+
|
4
|
+
# Load the Theme model and ThemeController
|
5
|
+
require 'theme_support/theme'
|
6
|
+
require 'theme_support/theme_controller'
|
7
|
+
|
8
|
+
# Initializes theme support by extending some of the core Rails classes
|
9
|
+
require 'theme_support/patches/abstractcontroller_ex'
|
10
|
+
require 'theme_support/patches/routeset_ex'
|
11
|
+
|
12
|
+
# Add the tag helpers for rhtml and, optionally, liquid templates
|
13
|
+
require 'theme_support/helpers/erb_theme_tags'
|
14
|
+
|
15
|
+
# Commented out to remove the message
|
16
|
+
# "Liquid doesn't seem to be loaded... uninitialized constant Liquid"
|
17
|
+
#begin
|
18
|
+
# require 'helpers/liquid_theme_tags'
|
19
|
+
#rescue
|
20
|
+
# # I guess Liquid isn't being used...
|
21
|
+
# STDERR.puts "Liquid doesn't seem to be loaded... #{$!}"
|
22
|
+
#end
|
23
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
spec = Gem::Specification.new do |s|
|
4
|
+
## package information
|
5
|
+
s.name = 'theme_support'
|
6
|
+
s.author = 'Pierre Yager and Sylvain Claudel'
|
7
|
+
s.email = "pierre@levosgien.net"
|
8
|
+
s.version = ("$Release: 3.0.0" =~ /[\.\d]+/) && $&
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.homepage = 'http://github.com/zedalaye/theme_support'
|
11
|
+
s.rubyforge_project = 'theme-support'
|
12
|
+
s.summary = "add multi-themes support to Rails 3.0"
|
13
|
+
s.description = <<-END
|
14
|
+
ThemeSupport adds support for multi-themes to your Rails 3.0 application
|
15
|
+
END
|
16
|
+
|
17
|
+
## files
|
18
|
+
files = []
|
19
|
+
files += Dir.glob('lib/**/*')
|
20
|
+
files += %w[Gemfile LICENSE README VERSION theme_support.gemspec]
|
21
|
+
s.files = files.delete_if { |path| path =~ /\.svn/ }
|
22
|
+
s.files = files
|
23
|
+
end
|
24
|
+
|
25
|
+
if $0 == __FILE__
|
26
|
+
Gem::manage_gems
|
27
|
+
Gem::Builder.new(spec).build
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: theme_support
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 7
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 3
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 3.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Pierre Yager and Sylvain Claudel
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-09-08 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: " ThemeSupport adds support for multi-themes to your Rails 3.0 application\n"
|
23
|
+
email: pierre@levosgien.net
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- lib/generators/theme/USAGE
|
32
|
+
- lib/generators/theme/templates/theme.css
|
33
|
+
- lib/generators/theme/templates/theme.yml
|
34
|
+
- lib/generators/theme/templates/layout.liquid
|
35
|
+
- lib/generators/theme/templates/views_readme
|
36
|
+
- lib/generators/theme/templates/layout.html.erb
|
37
|
+
- lib/generators/theme/templates/about.markdown
|
38
|
+
- lib/generators/theme/templates/preview.png
|
39
|
+
- lib/generators/theme/theme_generator.rb
|
40
|
+
- lib/theme_support.rb
|
41
|
+
- lib/theme_support/patches/abstractcontroller_ex.rb
|
42
|
+
- lib/theme_support/patches/routeset_ex.rb
|
43
|
+
- lib/theme_support/patches/actionmailer_ex.rb
|
44
|
+
- lib/theme_support/theme_controller.rb
|
45
|
+
- lib/theme_support/theme_helper.rb
|
46
|
+
- lib/theme_support/tasks.rb
|
47
|
+
- lib/theme_support/theme.rb
|
48
|
+
- lib/theme_support/railtie.rb
|
49
|
+
- lib/theme_support/theme_error.rb
|
50
|
+
- lib/theme_support/helpers/erb_theme_tags.rb
|
51
|
+
- lib/theme_support/helpers/liquid_theme_tags.rb
|
52
|
+
- Gemfile
|
53
|
+
- LICENSE
|
54
|
+
- README
|
55
|
+
- VERSION
|
56
|
+
- theme_support.gemspec
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: http://github.com/zedalaye/theme_support
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
hash: 3
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
requirements: []
|
85
|
+
|
86
|
+
rubyforge_project: theme-support
|
87
|
+
rubygems_version: 1.3.7
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: add multi-themes support to Rails 3.0
|
91
|
+
test_files: []
|
92
|
+
|