theme_support 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|