Commit 8a8319902b6521ea209326ec49ab6c656c5e4493

Authored by Aurélio A. Heckert
1 parent ce2d243c

Add global theme intervention spaces

When the env theme has `global.css`, `global_header.html.erb`, or `global_footer.html.erb`, that content is added to any customized profile theme.
HACKING.themes.md
@@ -3,6 +3,7 @@ Noosfero Instructions for Theme Developers @@ -3,6 +3,7 @@ Noosfero Instructions for Theme Developers
3 3
4 To build Noosfero themes you must to know HTML and CSS. You may also get some advantages with Ruby and Noosfero hacking knowledge because all customizable pieces of the theme's HTML structure are [erb](http://en.wikipedia.org/wiki/ERuby) files. 4 To build Noosfero themes you must to know HTML and CSS. You may also get some advantages with Ruby and Noosfero hacking knowledge because all customizable pieces of the theme's HTML structure are [erb](http://en.wikipedia.org/wiki/ERuby) files.
5 5
  6 +
6 Organization Basics 7 Organization Basics
7 ------------------- 8 -------------------
8 9
@@ -29,6 +30,7 @@ You can add more files like javascript and modularized CSS, but you must to refe @@ -29,6 +30,7 @@ You can add more files like javascript and modularized CSS, but you must to refe
29 30
30 To refer one of this files trough the web the path is `<domain>/designs/themes/<thistheme>/<somefile>`. 31 To refer one of this files trough the web the path is `<domain>/designs/themes/<thistheme>/<somefile>`.
31 32
  33 +
32 theme.yml 34 theme.yml
33 --------- 35 ---------
34 36
@@ -46,3 +48,13 @@ About non obvious: @@ -46,3 +48,13 @@ About non obvious:
46 * `icon_theme` point to something inside `public/designs/icons/`. 48 * `icon_theme` point to something inside `public/designs/icons/`.
47 * `gravatar` set the default gravatar *(avatar picture)* for people without picture. 49 * `gravatar` set the default gravatar *(avatar picture)* for people without picture.
48 50
  51 +
  52 +Theme Intervention from Environment Theme
  53 +-----------------------------------------
  54 +
  55 +Sometimes an environment (as instace http://cirandas.net) wants to allow profiles to set its own theme, but with some environment identification or functions, like a top bar with the social network logo and a top menu (as instace http://cirandas.net/rango-vegan).
  56 +To make the magic happens you can add some files to the environment theme.
  57 +All are optional:
  58 +* `global.css` — this must be used to style all extra html added by your intervention partials. As it is a free form css file you can style anything, but this is a conflict risk.
  59 +* `global_header.html.erb` — Will add content to `#global-header`.
  60 +* `global_footer.html.erb` — Will add content to `#global-footer`.
app/helpers/application_helper.rb
@@ -398,19 +398,27 @@ module ApplicationHelper @@ -398,19 +398,27 @@ module ApplicationHelper
398 end 398 end
399 end 399 end
400 400
401 - def theme_view_file(template) 401 + def theme_view_file(template, theme=nil)
402 # Since we cannot control what people are doing in external themes, we 402 # Since we cannot control what people are doing in external themes, we
403 # will keep looking for the deprecated .rhtml extension here. 403 # will keep looking for the deprecated .rhtml extension here.
404 - file = Rails.root.join('public', theme_path[1..-1], template + '.html.erb') 404 + addr = theme ? "designs/themes/#{theme}" : theme_path[1..-1]
  405 + file = Rails.root.join('public', addr, template + '.html.erb')
405 return file if File.exists?(file) 406 return file if File.exists?(file)
406 nil 407 nil
407 end 408 end
408 409
409 def theme_include(template, options = {}) 410 def theme_include(template, options = {})
410 - file = theme_view_file(template)  
411 - options.merge!({:file => file, :use_full_path => false}) 411 + from_theme_include(nil, template, options)
  412 + end
  413 +
  414 + def env_theme_include(template, options = {})
  415 + from_theme_include(environment.theme, template, options)
  416 + end
  417 +
  418 + def from_theme_include(theme, template, options = {})
  419 + file = theme_view_file(template, theme)
412 if file 420 if file
413 - render options 421 + render options.merge(file: file, use_full_path: false)
414 else 422 else
415 nil 423 nil
416 end 424 end
@@ -446,6 +454,14 @@ module ApplicationHelper @@ -446,6 +454,14 @@ module ApplicationHelper
446 @theme_extra_navigation ||= theme_include 'navigation' 454 @theme_extra_navigation ||= theme_include 'navigation'
447 end 455 end
448 456
  457 + def global_header
  458 + @global_header ||= env_theme_include 'global_header'
  459 + end
  460 +
  461 + def global_footer
  462 + @global_footer ||= env_theme_include 'global_footer'
  463 + end
  464 +
449 def is_testing_theme 465 def is_testing_theme
450 !controller.session[:theme].nil? 466 !controller.session[:theme].nil?
451 end 467 end
app/helpers/layout_helper.rb
@@ -51,22 +51,30 @@ module LayoutHelper @@ -51,22 +51,30 @@ module LayoutHelper
51 'chat', 51 'chat',
52 pngfix_stylesheet_path, 52 pngfix_stylesheet_path,
53 ] + tokeninput_stylesheets 53 ] + tokeninput_stylesheets
54 - plugins_stylesheets = @plugins.select(&:stylesheet?).map { |plugin| plugin.class.public_path('style.css') }  
55 -  
56 - output = ''  
57 - output += stylesheet_link_tag standard_stylesheets, :cache => 'cache/application'  
58 - output += stylesheet_link_tag template_stylesheet_path  
59 - output += stylesheet_link_tag icon_theme_stylesheet_path  
60 - output += stylesheet_link_tag jquery_ui_theme_stylesheet_path 54 + plugins_stylesheets = @plugins.select(&:stylesheet?).map { |plugin|
  55 + plugin.class.public_path('style.css')
  56 + }
  57 + global_css_pub = "/designs/themes/#{environment.theme}/global.css"
  58 + global_css_at_fs = Rails.root.join 'public' + global_css_pub
  59 +
  60 + output = []
  61 + output << stylesheet_link_tag(standard_stylesheets, :cache => 'cache/application')
  62 + output << stylesheet_link_tag(template_stylesheet_path)
  63 + output << stylesheet_link_tag(icon_theme_stylesheet_path)
  64 + output << stylesheet_link_tag(jquery_ui_theme_stylesheet_path)
61 unless plugins_stylesheets.empty? 65 unless plugins_stylesheets.empty?
62 - output += stylesheet_link_tag plugins_stylesheets, :cache => "cache/plugins-#{Digest::MD5.hexdigest plugins_stylesheets.to_s}" 66 + cacheid = "cache/plugins-#{Digest::MD5.hexdigest plugins_stylesheets.to_s}"
  67 + output << stylesheet_link_tag(plugins_stylesheets, :cache => cacheid)
63 end 68 end
64 - output += stylesheet_link_tag theme_stylesheet_path  
65 - output 69 + if File.exists? global_css_at_fs
  70 + output << stylesheet_link_tag(global_css_pub)
  71 + end
  72 + output << stylesheet_link_tag(theme_stylesheet_path)
  73 + output.join "\n"
66 end 74 end
67 75
68 def pngfix_stylesheet_path 76 def pngfix_stylesheet_path
69 - 'iepngfix/iepngfix.css' 77 + 'iepngfix/iepngfix.css' #TODO: deprecate it
70 end 78 end
71 79
72 def tokeninput_stylesheets 80 def tokeninput_stylesheets
app/views/layouts/application-ng.html.erb
@@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
22 end.join("\n") 22 end.join("\n")
23 %> 23 %>
24 24
25 - <script type='text/javascript'> 25 + <script type="text/javascript">
26 DEFAULT_LOADING_MESSAGE = <%="'#{ _('loading...') }'" %>; 26 DEFAULT_LOADING_MESSAGE = <%="'#{ _('loading...') }'" %>;
27 </script> 27 </script>
28 28
@@ -35,9 +35,12 @@ @@ -35,9 +35,12 @@
35 if content.respond_to?(:call) then instance_exec(&content).to_s.html_safe else content.to_s.html_safe end 35 if content.respond_to?(:call) then instance_exec(&content).to_s.html_safe else content.to_s.html_safe end
36 end.join("\n") 36 end.join("\n")
37 %> 37 %>
  38 + <div id="global-header">
  39 + <%= global_header %>
  40 + </div>
38 41
39 <div id="wrap-1"> 42 <div id="wrap-1">
40 - <div id='theme-header'> 43 + <div id="theme-header">
41 <%= theme_header %> 44 <%= theme_header %>
42 </div> 45 </div>
43 <div id="wrap-2"> 46 <div id="wrap-2">
@@ -57,9 +60,14 @@ @@ -57,9 +60,14 @@
57 </div><!-- end id="wrap-2" --> 60 </div><!-- end id="wrap-2" -->
58 </div><!-- end id="wrap-1" --> 61 </div><!-- end id="wrap-1" -->
59 <%= render_environment_features(:logged_in) if logged_in? %> 62 <%= render_environment_features(:logged_in) if logged_in? %>
60 - <div id="theme-footer">  
61 - <%= theme_footer %>  
62 - </div><!-- end id="theme-footer" --> 63 + <div id="footer">
  64 + <div id="theme-footer">
  65 + <%= theme_footer %>
  66 + </div><!-- end id="theme-footer" -->
  67 + <div id="global-footer">
  68 + <%= global_footer %>
  69 + </div><!-- end id="global-footer" -->
  70 + </div><!-- end id="footer" -->
63 <%= noosfero_layout_features %> 71 <%= noosfero_layout_features %>
64 <%= addthis_javascript %> 72 <%= addthis_javascript %>
65 <%= 73 <%=
app/views/layouts/application.html.erb
@@ -52,6 +52,9 @@ @@ -52,6 +52,9 @@
52 registerDocumentSize(); 52 registerDocumentSize();
53 // --> 53 // -->
54 </script> 54 </script>
  55 + <div id="global-header">
  56 + <%= global_header %>
  57 + </div>
55 58
56 <div id="accessibility_menu"> 59 <div id="accessibility_menu">
57 <a href="#content" id="link_go_content"><span><%= _('Go to content') %></span></a> 60 <a href="#content" id="link_go_content"><span><%= _('Go to content') %></span></a>
@@ -112,8 +115,13 @@ @@ -112,8 +115,13 @@
112 </div><!-- id="wrap" --> 115 </div><!-- id="wrap" -->
113 116
114 <div id="footer"> 117 <div id="footer">
115 - <%= theme_footer %>  
116 - </div><!-- id="footer" --> 118 + <div id="theme-footer">
  119 + <%= theme_footer %>
  120 + </div><!-- end id="theme-footer" -->
  121 + <div id="global-footer">
  122 + <%= global_footer %>
  123 + </div><!-- end id="global-footer" -->
  124 + </div><!-- end id="footer" -->
117 125
118 <%# if you need to add HTML stuff to the layout, include it in 126 <%# if you need to add HTML stuff to the layout, include it in
119 app/views/shared/noosfero_layout_features.html.erb! %> 127 app/views/shared/noosfero_layout_features.html.erb! %>
public/stylesheets/application.css
@@ -77,19 +77,6 @@ div#errorExplanation h2 { @@ -77,19 +77,6 @@ div#errorExplanation h2 {
77 #footer_content { 77 #footer_content {
78 clear: both; 78 clear: both;
79 } 79 }
80 -#footer {  
81 - text-align: center;  
82 - clear: both;  
83 - font-size: 10px;  
84 - color: #777;  
85 -}  
86 -#footer a {  
87 - color: #777;  
88 - text-decoration: none;  
89 -}  
90 -#footer a:hover {  
91 - color: #555;  
92 -}  
93 div#profile-disabled { 80 div#profile-disabled {
94 border: 2px solid #944; 81 border: 2px solid #944;
95 text-align: center; 82 text-align: center;
test/unit/application_helper_test.rb
@@ -961,6 +961,47 @@ class ApplicationHelperTest &lt; ActionView::TestCase @@ -961,6 +961,47 @@ class ApplicationHelperTest &lt; ActionView::TestCase
961 assert_equal '', manage_communities 961 assert_equal '', manage_communities
962 end 962 end
963 963
  964 + should 'include file from current theme out of a profile page' do
  965 + def profile; nil; end
  966 + def environment; e={}; def e.theme; 'env-theme'; end; e; end
  967 + def render(opt); opt; end
  968 + File.stubs(:exists?).returns(false)
  969 + file = Rails.root.join 'public/designs/themes/env-theme/somefile.html.erb'
  970 + assert_nil theme_include('somefile') # exists? = false
  971 + File.expects(:exists?).with(file).returns(true).at_least_once
  972 + assert_equal file, theme_include('somefile')[:file] # exists? = true
  973 + end
  974 +
  975 + should 'include file from current theme inside a profile page' do
  976 + def profile; p={}; def p.theme; 'my-theme'; end; p; end
  977 + def render(opt); opt; end
  978 + File.stubs(:exists?).returns(false)
  979 + file = Rails.root.join 'public/designs/themes/my-theme/otherfile.html.erb'
  980 + assert_nil theme_include('otherfile') # exists? = false
  981 + File.expects(:exists?).with(file).returns(true).at_least_once
  982 + assert_equal file, theme_include('otherfile')[:file] # exists? = true
  983 + end
  984 +
  985 + should 'include file from env theme' do
  986 + def profile; p={}; def p.theme; 'my-theme'; end; p; end
  987 + def environment; e={}; def e.theme; 'env-theme'; end; e; end
  988 + def render(opt); opt; end
  989 + File.stubs(:exists?).returns(false)
  990 + file = Rails.root.join 'public/designs/themes/env-theme/afile.html.erb'
  991 + assert_nil env_theme_include('afile') # exists? = false
  992 + File.expects(:exists?).with(file).returns(true).at_least_once
  993 + assert_equal file, env_theme_include('afile')[:file] # exists? = true
  994 + end
  995 +
  996 + should 'include file from some theme' do
  997 + def render(opt); opt; end
  998 + File.stubs(:exists?).returns(false)
  999 + file = Rails.root.join 'public/designs/themes/atheme/afile.html.erb'
  1000 + assert_nil from_theme_include('atheme', 'afile') # exists? = false
  1001 + File.expects(:exists?).with(file).returns(true).at_least_once
  1002 + assert_equal file, from_theme_include('atheme', 'afile')[:file] # exists? = true
  1003 + end
  1004 +
964 protected 1005 protected
965 include NoosferoTestHelper 1006 include NoosferoTestHelper
966 1007
test/unit/layout_helper_test.rb
1 require_relative "../test_helper" 1 require_relative "../test_helper"
2 2
3 class LayoutHelperTest < ActionView::TestCase 3 class LayoutHelperTest < ActionView::TestCase
  4 + include ApplicationHelper
4 5
5 should 'append logged-in class in body when user is logged-in' do 6 should 'append logged-in class in body when user is logged-in' do
6 expects(:logged_in?).returns(true) 7 expects(:logged_in?).returns(true)
@@ -14,4 +15,19 @@ class LayoutHelperTest &lt; ActionView::TestCase @@ -14,4 +15,19 @@ class LayoutHelperTest &lt; ActionView::TestCase
14 assert_not_includes body_classes.split, 'logged-in' 15 assert_not_includes body_classes.split, 'logged-in'
15 end 16 end
16 17
  18 + should 'add global.css to noosfero_stylesheets if env theme has it' do
  19 + env = fast_create Environment
  20 + env.theme = 'my-theme'
  21 + @plugins = []
  22 + expects(:profile).returns(nil).at_least_once
  23 + expects(:environment).returns(env).at_least_once
  24 + expects(:theme_option).with(:icon_theme).returns(['my-icons']).at_least_once
  25 + expects(:jquery_theme).returns('jquery-nice').at_least_once
  26 + global_css = Rails.root.join "public/designs/themes/#{env.theme}/global.css"
  27 + File.stubs(:exists?).returns(false)
  28 + File.expects(:exists?).with(global_css).returns(true).at_least_once
  29 + css = noosfero_stylesheets
  30 + assert_match /<link [^<]*href="\/designs\/themes\/my-theme\/global.css"/, css
  31 + end
  32 +
17 end 33 end