##// END OF EJS Templates
Support for Javascript in Themes (#2803)....
Jean-Philippe Lang -
r4444:523febf9c1b3
parent child
Show More
@@ -0,0 +1,60
1 # Redmine - project management software
2 # Copyright (C) 2006-2010 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require File.expand_path('../../../../test_helper', __FILE__)
19
20 class ThemesTest < ActionController::IntegrationTest
21 fixtures :all
22
23 def setup
24 @theme = Redmine::Themes.themes.last
25 Setting.ui_theme = @theme.id
26 end
27
28 def teardown
29 Setting.ui_theme = ''
30 end
31
32 def test_application_css
33 get '/'
34
35 assert_response :success
36 assert_tag :tag => 'link',
37 :attributes => {:href => %r{^/themes/#{@theme.dir}/stylesheets/application.css}}
38 end
39
40 def test_without_theme_js
41 get '/'
42
43 assert_response :success
44 assert_no_tag :tag => 'script',
45 :attributes => {:src => %r{^/themes/#{@theme.dir}/javascripts/theme.js}}
46 end
47
48 def test_with_theme_js
49 # Simulates a theme.js
50 @theme.javascripts << 'theme'
51 get '/'
52
53 assert_response :success
54 assert_tag :tag => 'script',
55 :attributes => {:src => %r{^/themes/#{@theme.dir}/javascripts/theme.js}}
56
57 ensure
58 @theme.javascripts.delete 'theme'
59 end
60 end
@@ -1,78 +1,79
1 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2 2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3 3 <head>
4 4 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
5 5 <title><%=h html_title %></title>
6 6 <meta name="description" content="<%= Redmine::Info.app_name %>" />
7 7 <meta name="keywords" content="issue,bug,tracker" />
8 8 <%= favicon %>
9 9 <%= stylesheet_link_tag 'application', :media => 'all' %>
10 10 <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
11 11 <%= javascript_include_tag :defaults %>
12 <%= heads_for_theme %>
12 13 <%= heads_for_wiki_formatter %>
13 14 <!--[if IE]>
14 15 <style type="text/css">
15 16 * html body{ width: expression( document.documentElement.clientWidth < 900 ? '900px' : '100%' ); }
16 17 body {behavior: url(<%= stylesheet_path "csshover.htc" %>);}
17 18 </style>
18 19 <![endif]-->
19 20 <%= call_hook :view_layouts_base_html_head %>
20 21 <!-- page specific tags -->
21 22 <%= yield :header_tags -%>
22 23 </head>
23 24 <body class="<%= body_css_classes %>">
24 25 <div id="wrapper">
25 26 <div id="wrapper2">
26 27 <div id="top-menu">
27 28 <div id="account">
28 29 <%= render_menu :account_menu -%>
29 30 </div>
30 31 <%= content_tag('div', "#{l(:label_logged_as)} #{link_to_user(User.current, :format => :username)}", :id => 'loggedas') if User.current.logged? %>
31 32 <%= render_menu :top_menu -%>
32 33 </div>
33 34
34 35 <div id="header">
35 36 <div id="quick-search">
36 37 <% form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
37 38 <%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %>
38 39 <%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project}, :accesskey => accesskey(:search) %>:
39 40 <%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %>
40 41 <% end %>
41 42 <%= render_project_jump_box %>
42 43 </div>
43 44
44 45 <h1><%= page_header_title %></h1>
45 46
46 47 <% if display_main_menu?(@project) %>
47 48 <div id="main-menu">
48 49 <%= render_main_menu(@project) %>
49 50 </div>
50 51 <% end %>
51 52 </div>
52 53
53 54 <%= tag('div', {:id => 'main', :class => (has_content?(:sidebar) ? '' : 'nosidebar')}, true) %>
54 55 <div id="sidebar">
55 56 <%= yield :sidebar %>
56 57 <%= call_hook :view_layouts_base_sidebar %>
57 58 </div>
58 59
59 60 <div id="content">
60 61 <%= render_flash_messages %>
61 62 <%= yield %>
62 63 <%= call_hook :view_layouts_base_content %>
63 64 <div style="clear:both;"></div>
64 65 </div>
65 66 </div>
66 67
67 68 <div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div>
68 69
69 70 <div id="footer">
70 71 <div class="bgl"><div class="bgr">
71 72 Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2010 Jean-Philippe Lang
72 73 </div></div>
73 74 </div>
74 75 </div>
75 76 </div>
76 77 <%= call_hook :view_layouts_base_body_bottom %>
77 78 </body>
78 79 </html>
@@ -1,85 +1,125
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 module Redmine
19 19 module Themes
20 20
21 21 # Return an array of installed themes
22 22 def self.themes
23 23 @@installed_themes ||= scan_themes
24 24 end
25 25
26 26 # Rescan themes directory
27 27 def self.rescan
28 28 @@installed_themes = scan_themes
29 29 end
30 30
31 31 # Return theme for given id, or nil if it's not found
32 32 def self.theme(id, options={})
33 33 found = themes.find {|t| t.id == id}
34 34 if found.nil? && options[:rescan] != false
35 35 rescan
36 36 found = theme(id, :rescan => false)
37 37 end
38 38 found
39 39 end
40 40
41 41 # Class used to represent a theme
42 42 class Theme
43 attr_reader :name, :dir, :stylesheets
43 attr_reader :path, :name, :dir
44 44
45 45 def initialize(path)
46 @path = path
46 47 @dir = File.basename(path)
47 48 @name = @dir.humanize
48 @stylesheets = Dir.glob("#{path}/stylesheets/*.css").collect {|f| File.basename(f).gsub(/\.css$/, '')}
49 @stylesheets = nil
50 @javascripts = nil
49 51 end
50 52
51 53 # Directory name used as the theme id
52 54 def id; dir end
53 55
54 56 def ==(theme)
55 57 theme.is_a?(Theme) && theme.dir == dir
56 58 end
57 59
58 60 def <=>(theme)
59 61 name <=> theme.name
60 62 end
63
64 def stylesheets
65 @stylesheets ||= assets("stylesheets", "css")
66 end
67
68 def javascripts
69 @javascripts ||= assets("javascripts", "js")
70 end
71
72 def stylesheet_path(source)
73 "/themes/#{dir}/stylesheets/#{source}"
74 end
75
76 def javascript_path(source)
77 "/themes/#{dir}/javascripts/#{source}"
78 end
79
80 private
81
82 def assets(dir, ext)
83 Dir.glob("#{path}/#{dir}/*.#{ext}").collect {|f| File.basename(f).gsub(/\.#{ext}$/, '')}
84 end
61 85 end
62 86
63 87 private
64
88
65 89 def self.scan_themes
66 90 dirs = Dir.glob("#{Rails.public_path}/themes/*").select do |f|
67 91 # A theme should at least override application.css
68 92 File.directory?(f) && File.exist?("#{f}/stylesheets/application.css")
69 93 end
70 94 dirs.collect {|dir| Theme.new(dir)}.sort
71 95 end
72 96 end
73 97 end
74 98
75 99 module ApplicationHelper
100 def current_theme
101 unless instance_variable_defined?(:@current_theme)
102 @current_theme = Redmine::Themes.theme(Setting.ui_theme)
103 end
104 @current_theme
105 end
106
76 107 def stylesheet_path(source)
77 @current_theme ||= Redmine::Themes.theme(Setting.ui_theme)
78 super((@current_theme && @current_theme.stylesheets.include?(source)) ?
79 "/themes/#{@current_theme.dir}/stylesheets/#{source}" : source)
108 if current_theme && current_theme.stylesheets.include?(source)
109 super current_theme.stylesheet_path(source)
110 else
111 super
112 end
80 113 end
81 114
82 115 def path_to_stylesheet(source)
83 116 stylesheet_path source
84 117 end
118
119 # Returns the header tags for the current theme
120 def heads_for_theme
121 if current_theme && current_theme.javascripts.include?('theme')
122 javascript_include_tag current_theme.javascript_path('theme')
123 end
124 end
85 125 end
General Comments 0
You need to be logged in to leave comments. Login now