Commit f2258e83ec5bb7a0a8411b389b60618df0d9e227
1 parent
9f8b8883
Exists in
master
and in
29 other branches
ActionItem122: implementing theme editor
git-svn-id: https://svn.colivre.coop.br/svn/noosfero/trunk@2425 3f533792-8f58-4932-b0fe-aaf55b0a4547
Showing
22 changed files
with
801 additions
and
89 deletions
Show diff stats
app/controllers/my_profile/themes_controller.rb
1 | 1 | class ThemesController < MyProfileController |
2 | 2 | |
3 | + protect 'edit_appearance', :profile | |
3 | 4 | no_design_blocks |
4 | 5 | |
5 | 6 | def set |
... | ... | @@ -12,4 +13,66 @@ class ThemesController < MyProfileController |
12 | 13 | @selected_theme = profile.theme |
13 | 14 | end |
14 | 15 | |
16 | + def new | |
17 | + if !request.xhr? | |
18 | + id = params[:name].to_slug | |
19 | + t = Theme.new(id, :name => params[:name], :owner => profile) | |
20 | + t.save | |
21 | + redirect_to :action => 'index' | |
22 | + else | |
23 | + render :action => 'new', :layout => false | |
24 | + end | |
25 | + end | |
26 | + | |
27 | + def edit | |
28 | + @theme = profile.find_theme(params[:id]) | |
29 | + @css_files = @theme.css_files | |
30 | + @image_files = @theme.image_files | |
31 | + end | |
32 | + | |
33 | + def add_css | |
34 | + @theme = profile.find_theme(params[:id]) | |
35 | + if request.xhr? | |
36 | + render :action => 'add_css', :layout => false | |
37 | + else | |
38 | + @theme.add_css(params[:css]) | |
39 | + redirect_to :action => 'edit', :id => @theme.id | |
40 | + end | |
41 | + end | |
42 | + | |
43 | + def css_editor | |
44 | + @theme = profile.find_theme(params[:id]) | |
45 | + @css = params[:css] | |
46 | + | |
47 | + @code = @theme.read_css(@css) | |
48 | + render :action => 'css_editor', :layout => false | |
49 | + end | |
50 | + | |
51 | + post_only :update_css | |
52 | + def update_css | |
53 | + @theme = profile.find_theme(params[:id]) | |
54 | + @theme.update_css(params[:css], params[:csscode]) | |
55 | + redirect_to :action => 'edit', :id => @theme.id | |
56 | + end | |
57 | + | |
58 | + def add_image | |
59 | + @theme = profile.find_theme(params[:id]) | |
60 | + if request.xhr? | |
61 | + render :action => 'add_image', :layout => false | |
62 | + else | |
63 | + @theme.add_image(params[:image].original_filename, params[:image].read) | |
64 | + redirect_to :action => 'edit', :id => @theme.id | |
65 | + end | |
66 | + end | |
67 | + | |
68 | + def start_test | |
69 | + session[:theme] = params[:id] | |
70 | + redirect_to :controller => 'content_viewer', :profile => profile.identifier, :action => 'view_page' | |
71 | + end | |
72 | + | |
73 | + def stop_test | |
74 | + session[:theme] = nil | |
75 | + redirect_to :action => 'index' | |
76 | + end | |
77 | + | |
15 | 78 | end | ... | ... |
app/helpers/application_helper.rb
... | ... | @@ -220,24 +220,12 @@ module ApplicationHelper |
220 | 220 | link_to(content_tag('span', label), url, html_options.merge(:class => the_class )) |
221 | 221 | end |
222 | 222 | |
223 | - def button_to_function(type, label, js_code, html_options = {}) | |
224 | - html_options[:class] = "button with-text" unless html_options[:class] | |
225 | - html_options[:class] << " icon-#{type}" | |
226 | - link_to_function(label, js_code, html_options) | |
227 | - end | |
228 | - | |
229 | 223 | def button_to_function(type, label, js_code, html_options = {}, &block) |
230 | 224 | html_options[:class] = "button with-text" unless html_options[:class] |
231 | 225 | html_options[:class] << " icon-#{type}" |
232 | 226 | link_to_function(label, js_code, html_options, &block) |
233 | 227 | end |
234 | 228 | |
235 | - def button_to_function_without_text(type, label, js_code, html_options = {}) | |
236 | - html_options[:class] = "" unless html_options[:class] | |
237 | - html_options[:class] << " button icon-#{type}" | |
238 | - link_to_function(content_tag('span', label), js_code, html_options) | |
239 | - end | |
240 | - | |
241 | 229 | def button_to_function_without_text(type, label, js_code, html_options = {}, &block) |
242 | 230 | html_options[:class] = "" unless html_options[:class] |
243 | 231 | html_options[:class] << " button icon-#{type}" |
... | ... | @@ -323,12 +311,21 @@ module ApplicationHelper |
323 | 311 | def filename_for_stylesheet(name, in_theme) |
324 | 312 | result = '' |
325 | 313 | if in_theme |
326 | - result << '/designs/themes/' + current_theme | |
314 | + result << theme_path | |
327 | 315 | end |
328 | 316 | result << '/stylesheets/' << name << '.css' |
329 | 317 | end |
330 | 318 | |
319 | + def theme_path | |
320 | + if session[:theme] | |
321 | + '/user_themes/' + current_theme | |
322 | + else | |
323 | + '/designs/themes/' + current_theme | |
324 | + end | |
325 | + end | |
326 | + | |
331 | 327 | def current_theme |
328 | + return session[:theme] if (session[:theme]) | |
332 | 329 | p = profile |
333 | 330 | if p |
334 | 331 | p.theme |
... | ... | @@ -337,6 +334,21 @@ module ApplicationHelper |
337 | 334 | end |
338 | 335 | end |
339 | 336 | |
337 | + def theme_footer | |
338 | + footer = ('../../public' + theme_path + '/footer.rhtml') | |
339 | + if File.exists?(RAILS_ROOT + '/app/views/' + footer) | |
340 | + render :file => footer | |
341 | + end | |
342 | + end | |
343 | + | |
344 | + def is_testing_theme | |
345 | + !@controller.session[:theme].nil? | |
346 | + end | |
347 | + | |
348 | + def theme_owner | |
349 | + Theme.find(current_theme).owner.identifier | |
350 | + end | |
351 | + | |
340 | 352 | # generates a image tag for the profile. |
341 | 353 | # |
342 | 354 | # If the profile has no image set yet, then a default image is used. |
... | ... | @@ -492,8 +504,7 @@ module ApplicationHelper |
492 | 504 | |
493 | 505 | def theme_option(opt = nil) |
494 | 506 | conf = RAILS_ROOT.to_s() + |
495 | - '/public/designs/themes/' + | |
496 | - current_theme.to_s() + | |
507 | + '/public' + theme_path + | |
497 | 508 | '/theme.yml' |
498 | 509 | if File.exists?(conf) |
499 | 510 | opt ? YAML.load_file(conf)[opt.to_s()] : YAML.load_file(conf) |
... | ... | @@ -529,7 +540,7 @@ module ApplicationHelper |
529 | 540 | return if option.nil? |
530 | 541 | html = [] |
531 | 542 | option.each do |file| |
532 | - file = '/designs/themes/'+ current_theme.to_s() + | |
543 | + file = theme_path + | |
533 | 544 | '/javascript/'+ file +'.js' |
534 | 545 | if File.exists? RAILS_ROOT.to_s() +'/public'+ file |
535 | 546 | html << javascript_src_tag( file, {} ) | ... | ... |
app/models/profile.rb
... | ... | @@ -35,6 +35,7 @@ class Profile < ActiveRecord::Base |
35 | 35 | 'validate_enterprise' => N_('Validate enterprise'), |
36 | 36 | 'perform_task' => N_('Perform task'), |
37 | 37 | 'moderate_comments' => N_('Moderate comments'), |
38 | + 'edit_appearance' => N_('Edit appearance'), | |
38 | 39 | } |
39 | 40 | |
40 | 41 | acts_as_accessible |
... | ... | @@ -450,4 +451,13 @@ class Profile < ActiveRecord::Base |
450 | 451 | def public? |
451 | 452 | public_profile |
452 | 453 | end |
454 | + | |
455 | + def themes | |
456 | + Theme.find_by_owner(self) | |
457 | + end | |
458 | + | |
459 | + def find_theme(the_id) | |
460 | + themes.find { |item| item.id == the_id } | |
461 | + end | |
462 | + | |
453 | 463 | end | ... | ... |
app/models/theme.rb
1 | 1 | class Theme |
2 | 2 | |
3 | + class << self | |
4 | + def system_themes | |
5 | + Dir.glob(RAILS_ROOT + '/public/designs/themes/*').map do |item| | |
6 | + File.basename(item) | |
7 | + end.map do |item| | |
8 | + new(item) | |
9 | + end | |
10 | + end | |
11 | + | |
12 | + def user_themes_dir | |
13 | + File.join(RAILS_ROOT, 'public', 'user_themes') | |
14 | + end | |
15 | + | |
16 | + def create(id, attributes = {}) | |
17 | + if find(id) || system_themes.map(&:id).include?(id) | |
18 | + raise DuplicatedIdentifier | |
19 | + end | |
20 | + Theme.new(id, attributes).save | |
21 | + end | |
22 | + | |
23 | + def find(the_id) | |
24 | + if File.directory?(File.join(user_themes_dir, the_id)) | |
25 | + Theme.new(the_id) | |
26 | + else | |
27 | + nil | |
28 | + end | |
29 | + end | |
30 | + | |
31 | + def find_by_owner(owner) | |
32 | + Dir.glob(File.join(user_themes_dir, '*', 'theme.yml')).select do |desc| | |
33 | + config = YAML.load_file(desc) | |
34 | + (config['owner_type'] == owner.class.base_class.name) && (config['owner_id'] == owner.id) | |
35 | + end.map do |desc| | |
36 | + Theme.find(File.basename(File.dirname(desc))) | |
37 | + end | |
38 | + end | |
39 | + | |
40 | + end | |
41 | + | |
42 | + class DuplicatedIdentifier < Exception; end | |
43 | + | |
3 | 44 | attr_reader :id |
45 | + attr_reader :config | |
4 | 46 | |
5 | - def initialize(id) | |
47 | + def initialize(id, attributes = {}) | |
6 | 48 | @id = id |
49 | + load_config | |
50 | + attributes.each do |k,v| | |
51 | + self.send("#{k}=", v) | |
52 | + end | |
7 | 53 | end |
8 | 54 | |
9 | 55 | def name |
10 | - id | |
56 | + config['name'] || id | |
11 | 57 | end |
12 | 58 | |
13 | - class << self | |
14 | - def system_themes | |
15 | - Dir.glob(RAILS_ROOT + '/public/designs/themes/*').map do |item| | |
16 | - File.basename(item) | |
17 | - end.map do |item| | |
18 | - new(item) | |
19 | - end | |
59 | + def name=(value) | |
60 | + config['name'] = value | |
61 | + end | |
62 | + | |
63 | + def ==(other) | |
64 | + other.is_a?(self.class) && (other.id == self.id) | |
65 | + end | |
66 | + | |
67 | + def add_css(filename) | |
68 | + FileUtils.mkdir_p(stylesheets_directory) | |
69 | + FileUtils.touch(stylesheet_path(filename)) | |
70 | + end | |
71 | + | |
72 | + def update_css(filename, content) | |
73 | + add_css(filename) | |
74 | + File.open(stylesheet_path(filename), 'w') do |f| | |
75 | + f.write(content) | |
76 | + end | |
77 | + end | |
78 | + | |
79 | + def read_css(filename) | |
80 | + File.read(stylesheet_path(filename)) | |
81 | + end | |
82 | + | |
83 | + def css_files | |
84 | + Dir.glob(File.join(stylesheets_directory, '*.css')).map { |f| File.basename(f) } | |
85 | + end | |
86 | + | |
87 | + def add_image(filename, data) | |
88 | + FileUtils.mkdir_p(images_directory) | |
89 | + File.open(image_path(filename), 'w') do |f| | |
90 | + f.write(data) | |
20 | 91 | end |
21 | 92 | end |
93 | + | |
94 | + def image_files | |
95 | + Dir.glob(image_path('*')).map {|item| File.basename(item)} | |
96 | + end | |
97 | + | |
98 | + def stylesheet_path(filename) | |
99 | + suffix = '' | |
100 | + unless filename =~ /\.css$/ | |
101 | + suffix = '.css' | |
102 | + end | |
103 | + File.join(stylesheets_directory, filename + suffix) | |
104 | + end | |
105 | + | |
106 | + def stylesheets_directory | |
107 | + File.join(Theme.user_themes_dir, self.id, 'stylesheets') | |
108 | + end | |
109 | + | |
110 | + def image_path(filename) | |
111 | + File.join(images_directory, filename) | |
112 | + end | |
113 | + | |
114 | + def images_directory | |
115 | + File.join(self.class.user_themes_dir, id, 'images') | |
116 | + end | |
117 | + | |
118 | + def save | |
119 | + FileUtils.mkdir_p(self.class.user_themes_dir) | |
120 | + FileUtils.mkdir_p(File.join(self.class.user_themes_dir, id)) | |
121 | + %w[ common help menu article button search blocks forms login-box ].each do |item| | |
122 | + add_css(item) | |
123 | + end | |
124 | + write_config | |
125 | + self | |
126 | + end | |
127 | + | |
128 | + def owner | |
129 | + return nil unless config['owner_type'] && config['owner_id'] | |
130 | + @owner ||= config['owner_type'].constantize.find(config['owner_id']) | |
131 | + end | |
132 | + | |
133 | + def owner=(model) | |
134 | + config['owner_type'] = model.class.base_class.name | |
135 | + config['owner_id'] = model.id | |
136 | + @owner = model | |
137 | + end | |
138 | + | |
139 | + protected | |
140 | + | |
141 | + def write_config | |
142 | + File.open(File.join(self.class.user_themes_dir, self.id, 'theme.yml'), 'w') do |f| | |
143 | + f.write(config.to_yaml) | |
144 | + end | |
145 | + end | |
146 | + | |
147 | + def load_config | |
148 | + if File.exists?(File.join(self.class.user_themes_dir, self.id, 'theme.yml')) | |
149 | + @config = YAML.load_file(File.join(self.class.user_themes_dir, self.id, 'theme.yml')) | |
150 | + else | |
151 | + @config = {} | |
152 | + end | |
153 | + end | |
154 | + | |
22 | 155 | end | ... | ... |
app/views/layouts/application.rhtml
... | ... | @@ -154,7 +154,7 @@ |
154 | 154 | </div><!-- id="wrap" --> |
155 | 155 | |
156 | 156 | <div id="footer"> |
157 | - <%= render :file => ('../../public/designs/themes/' + current_theme + '/footer.rhtml' ) %> | |
157 | + <%= theme_footer %> | |
158 | 158 | </div><!-- id="footer" --> |
159 | 159 | |
160 | 160 | <div id="helpBox" style="display:none"> |
... | ... | @@ -177,5 +177,9 @@ |
177 | 177 | </div> |
178 | 178 | <%= javascript_include_tag 'better-browser-promotion' %> |
179 | 179 | |
180 | + <% if is_testing_theme %> | |
181 | + <%= render :file => 'shared/theme_test_panel' %> | |
182 | + <% end %> | |
183 | + | |
180 | 184 | </body> |
181 | 185 | </html> | ... | ... |
app/views/shared/codepress.rhtml
... | ... | @@ -0,0 +1,13 @@ |
1 | +<div id='theme-test-panel'> | |
2 | + <h4> | |
3 | + <%= _('Testing theme "%s"') % current_theme %> | |
4 | + </h4> | |
5 | + | |
6 | + <p><small><em><%= _('You can move this window away to have a better visualization of specific parts of screen.') %></em></small></p> | |
7 | + | |
8 | + <% button_bar do %> | |
9 | + <%= button(:ok, _('Finished testing'), :controller => 'themes', :profile => theme_owner, :action => 'stop_test', :id => current_theme) %> | |
10 | + <%= button(:edit, _('Edit theme'), :controller => 'themes', :profile => theme_owner, :action => 'edit', :id => current_theme) %> | |
11 | + <% end %> | |
12 | +</div> | |
13 | +<%= draggable_element('theme-test-panel') %> | ... | ... |
... | ... | @@ -0,0 +1,11 @@ |
1 | +<h2><%= _('Add a CSS file') %></h2> | |
2 | + | |
3 | +<% form_tag do %> | |
4 | + <%= labelled_form_field(_('File name'), text_field_tag('css')) %> | |
5 | + | |
6 | + <% button_bar do %> | |
7 | + <%= submit_button(:add, _('Add')) %> | |
8 | + <%= lightbox_close_button(_('Cancel')) %> | |
9 | + <% end %> | |
10 | + | |
11 | +<% end %> | ... | ... |
... | ... | @@ -0,0 +1,13 @@ |
1 | +<h2><%= _('CSS code: "%s"') % @css %></h2> | |
2 | + | |
3 | +<% form_tag({:action => 'update_css', :id => @theme.id }, :name => 'csscode_form') do %> | |
4 | + <%= hidden_field_tag('css', @css) %> | |
5 | + <%= text_area_tag('csscode', @code, :id => "codepressWindow", :class => 'codepress css') %> | |
6 | + <% button_bar do %> | |
7 | + <%= submit_button(:save, _('Save')) %> | |
8 | + <% end %> | |
9 | +<% end %> | |
10 | + | |
11 | +<!--<script type='text/javascript'>--> | |
12 | + <!--CodePress.run();--> | |
13 | +<!--</script>--> | ... | ... |
app/views/themes/edit.rhtml
1 | 1 | <form> |
2 | 2 | <%# FIXME %> |
3 | 3 | <h1 id='theme-name'> |
4 | - <%= _('Editing theme %s') % text_field_tag('theme_name', 'my theme') %> | |
5 | - <%= icon_button(:save, _('Save'), '#') %> | |
4 | + <%= _('Editing theme "%s"') % @theme.name %> | |
5 | + <%= button(:search, _('Preview this theme'), :action => 'index') %> | |
6 | + <%= button(:back, _('Back'), :action => 'index') %> | |
6 | 7 | </h1> |
7 | 8 | </form> |
8 | 9 | |
10 | + | |
9 | 11 | <div id='css-files-list'> |
10 | 12 | <h2><%= _('CSS files') %></h2> |
11 | 13 | <ul> |
12 | - <li><a href="">common.css</a></li> | |
13 | - <li><a href="">header.css</a></li> | |
14 | - <li><a href="">footer.css</a></li> | |
15 | - <li><a href="">sideboxes.css</a></li> | |
16 | - <li><a href="">otherstuff1.css</a></li> | |
17 | - <li><a href="">otherstuff1.css</a></li> | |
18 | - <li><a href="">otherstuff1.css</a></li> | |
19 | - <li><a href="">otherstuff2css</a></li> | |
20 | - <li><a href="">otherstuff3.css</a></li> | |
21 | - <li><a href="">otherstuff4.css</a></li> | |
22 | - <li><a href="">otherstuff2css</a></li> | |
23 | - <li><a href="">otherstuff3.css</a></li> | |
24 | - <li><a href="">otherstuff4.css</a></li> | |
25 | - <li><a href="">otherstuff2css</a></li> | |
26 | - <li><a href="">otherstuff3.css</a></li> | |
27 | - <li><a href="">otherstuff4.css</a></li> | |
14 | + <% for css in @css_files %> | |
15 | + <li><%= link_to_remote(css, :url => { :action => 'css_editor', :id => @theme.id, :css => css }, :update => { :success => 'css-code' }) %></li> | |
16 | + <% end %> | |
28 | 17 | </ul> |
29 | 18 | <% button_bar do %> |
30 | - <%= button(:add, _('New CSS'), '') %> | |
19 | + <%= lightbox_button(:add, _('New CSS'), :action => 'add_css', :id => @theme.id) %> | |
31 | 20 | <% end %> |
32 | 21 | </div> |
33 | 22 | |
34 | 23 | <div id='image-files-list'> |
35 | 24 | <h2><%= _('Images') %></h2> |
36 | 25 | <ul> |
37 | - <li><img src='/images/icons-app/gtk-cancel.png' /><span>Image 1</span></li> | |
38 | - <li><img src='/images/icons-app/cms.png' /><span>Image_2_a_very_large_filename</span></li> | |
39 | - <li><img src='http://www.ynternet.org/banner/' /><span>a wide image</span></li> | |
40 | - <li><img src='/images/icons-app/enable.png' /><span>Image 3</span></li> | |
41 | - <li><img src='/images/icons-app/gnome-globe.png' /><span>Image 4</span></li> | |
42 | - <li><img src='/images/icons-app/gtk-yes.png' /><span>Image 5</span></li> | |
43 | - <li><img src='/images/icons-app/mail.png' /><span>Image 6</span></li> | |
26 | + <% for image in @image_files %> | |
27 | + <li><%= image_tag("/user_themes/#{@theme.id}/images/#{image}") %></li> | |
28 | + <% end %> | |
44 | 29 | </ul> |
45 | 30 | <% button_bar do %> |
46 | - <%= button(:add, _('Upload image'), '') %> | |
31 | + <%= lightbox_button(:add, _('Add image'), :action => 'add_image', :id => @theme.id) %> | |
47 | 32 | <% end %> |
48 | 33 | </div> |
49 | 34 | |
50 | -<form id='css-code'> | |
51 | - <%# FIXME %> | |
52 | - <h2><%= _('CSS code: "%s"') % 'common.css' %></h2> | |
53 | - <textarea name='csscode' id="csscode" class='codepress css'> | |
54 | - body { | |
55 | - background: yellow; | |
56 | - } | |
57 | - </textarea> | |
58 | - <% button_bar do %> | |
59 | - <%= button(:save, _('Save'), '') %> | |
60 | - <% end %> | |
61 | -</form> | |
35 | +<div id='css-code'> | |
36 | + <center style='padding-top: 5em;'> | |
37 | + <em><%= _('Select a CSS file to edit') %></em> | |
38 | + </center> | |
39 | +</div> | |
62 | 40 | |
63 | -<%= render :file => 'shared/codepress' %> | |
41 | +<%# javascript_include_tag 'codepress/codepress' %> | ... | ... |
app/views/themes/index.rhtml
... | ... | @@ -2,13 +2,53 @@ |
2 | 2 | |
3 | 3 | <h2><%= _('Select theme') %></h2> |
4 | 4 | |
5 | -<div> | |
6 | - <% for theme in @themes %> | |
7 | - <%= link_to image_tag('/images/icons-app/design-editor.png', :alt => (_('Select the "%s" theme.') % theme.name)) + content_tag('span', theme.name), :action => 'set', :id => theme.id %> | |
5 | +<table style='margin: auto'> | |
6 | + <% for themes in @themes.in_groups_of(3) %> | |
7 | + <tr> | |
8 | + <% for theme in themes %> | |
9 | + <td> | |
10 | + <%# FIXME add proper thumbnails %> | |
11 | + <%= image_tag('/images/icons-app/design-editor.png', :alt => (_('The "%s" theme.') % theme.name)) if theme %> | |
12 | + </td> | |
13 | + <td> | |
14 | + <strong><%= theme.name if theme%></strong><br/> | |
15 | + <%= link_to(_('Use this theme'), :action => 'set', :id => theme.id) if theme %> | |
16 | + </td> | |
17 | + <td> | |
18 | + | |
19 | + </td> | |
20 | + <% end %> | |
21 | + </tr> | |
22 | + <tr><td colspan='3'> </td></tr> | |
8 | 23 | <% end %> |
9 | -</div> | |
24 | +</table> | |
10 | 25 | |
26 | +<h2><%= _('My themes') %></h2> | |
27 | + | |
28 | +<table style='margin: auto'> | |
29 | + <% for themes in profile.themes.in_groups_of(3) %> | |
30 | + <tr> | |
31 | + <% for theme in themes %> | |
32 | + <td> | |
33 | + <%# FIXME add proper thumbnails %> | |
34 | + <%= image_tag('/images/icons-app/design-editor.png', :alt => (_('The "%s" theme.') % theme.name)) if theme %> | |
35 | + </td> | |
36 | + <td> | |
37 | + <strong><%= theme.name if theme%></strong><br/> | |
38 | + <%= link_to(_('Edit this theme'), :action => 'edit', :id => theme.id) if theme %> | |
39 | + <br/> | |
40 | + <%= link_to(_('Test this theme'), :action => 'start_test', :id => theme.id) if theme %> | |
41 | + </td> | |
42 | + <td> | |
43 | + | |
44 | + </td> | |
45 | + <% end %> | |
46 | + </tr> | |
47 | + <tr><td colspan='3'> </td></tr> | |
48 | + <% end %> | |
49 | +</table> | |
11 | 50 | |
12 | 51 | <% button_bar do %> |
52 | + <%= lightbox_button(:add, _('New theme ...'), :action => 'new') %> | |
13 | 53 | <%= button(:back, _('Back'), :controller => 'profile_editor', :action => 'index') %> |
14 | 54 | <% end %> | ... | ... |
... | ... | @@ -0,0 +1,10 @@ |
1 | +<h2><%= _('Create new theme') %></h2> | |
2 | + | |
3 | +<% form_tag(:action => 'new') do %> | |
4 | + | |
5 | + <%= labelled_form_field(_('Name of the new theme:'), text_field_tag(:name)) %> | |
6 | + | |
7 | + <% button_bar do %> | |
8 | + <%= submit_button(:save, _('Create')) %> | |
9 | + <% end %> | |
10 | +<% end %> | ... | ... |
... | ... | @@ -0,0 +1,16 @@ |
1 | +class AddEditAppearancePermission < ActiveRecord::Migration | |
2 | + | |
3 | + def self.up | |
4 | + [ Profile::Roles.admin, Environment::Roles.admin].each do |item| | |
5 | + item.permissions += [ 'edit_appearance' ] | |
6 | + item.save! | |
7 | + end | |
8 | + end | |
9 | + | |
10 | + def self.down | |
11 | + [ Profile::Roles.admin, Environment::Roles.admin].each do |item| | |
12 | + item.permissions -= [ 'edit_appearance' ] | |
13 | + item.save! | |
14 | + end | |
15 | + end | |
16 | +end | ... | ... |
db/schema.rb
... | ... | @@ -9,7 +9,7 @@ |
9 | 9 | # |
10 | 10 | # It's strongly recommended to check this file into your version control system. |
11 | 11 | |
12 | -ActiveRecord::Schema.define(:version => 50) do | |
12 | +ActiveRecord::Schema.define(:version => 51) do | |
13 | 13 | |
14 | 14 | create_table "article_versions", :force => true do |t| |
15 | 15 | t.integer "article_id" |
... | ... | @@ -73,8 +73,8 @@ ActiveRecord::Schema.define(:version => 50) do |
73 | 73 | t.boolean "virtual", :default => false |
74 | 74 | end |
75 | 75 | |
76 | - add_index "articles_categories", ["article_id"], :name => "index_articles_categories_on_article_id" | |
77 | 76 | add_index "articles_categories", ["category_id"], :name => "index_articles_categories_on_category_id" |
77 | + add_index "articles_categories", ["article_id"], :name => "index_articles_categories_on_article_id" | |
78 | 78 | |
79 | 79 | create_table "blocks", :force => true do |t| |
80 | 80 | t.string "title" |
... | ... | @@ -114,8 +114,8 @@ ActiveRecord::Schema.define(:version => 50) do |
114 | 114 | t.boolean "virtual", :default => false |
115 | 115 | end |
116 | 116 | |
117 | - add_index "categories_profiles", ["profile_id"], :name => "index_categories_profiles_on_profile_id" | |
118 | 117 | add_index "categories_profiles", ["category_id"], :name => "index_categories_profiles_on_category_id" |
118 | + add_index "categories_profiles", ["profile_id"], :name => "index_categories_profiles_on_profile_id" | |
119 | 119 | |
120 | 120 | create_table "comments", :force => true do |t| |
121 | 121 | t.string "title" |
... | ... | @@ -182,8 +182,8 @@ ActiveRecord::Schema.define(:version => 50) do |
182 | 182 | t.datetime "updated_at" |
183 | 183 | end |
184 | 184 | |
185 | - add_index "product_categorizations", ["product_id"], :name => "index_product_categorizations_on_product_id" | |
186 | 185 | add_index "product_categorizations", ["category_id"], :name => "index_product_categorizations_on_category_id" |
186 | + add_index "product_categorizations", ["product_id"], :name => "index_product_categorizations_on_product_id" | |
187 | 187 | |
188 | 188 | create_table "products", :force => true do |t| |
189 | 189 | t.integer "enterprise_id" |
... | ... | @@ -254,8 +254,8 @@ ActiveRecord::Schema.define(:version => 50) do |
254 | 254 | t.datetime "created_at" |
255 | 255 | end |
256 | 256 | |
257 | - add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id" | |
258 | 257 | add_index "taggings", ["taggable_id", "taggable_type"], :name => "index_taggings_on_taggable_id_and_taggable_type" |
258 | + add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id" | |
259 | 259 | |
260 | 260 | create_table "tags", :force => true do |t| |
261 | 261 | t.string "name" | ... | ... |
lib/tasks/populate.rake
... | ... | @@ -36,6 +36,7 @@ def create_roles |
36 | 36 | 'post_content', |
37 | 37 | 'edit_profile_design', |
38 | 38 | 'manage_products', |
39 | + 'edit_appearance', | |
39 | 40 | ]) |
40 | 41 | Role.create!(:key => 'profile_admin', :name => N_('Profile Administrator'), :permissions => [ |
41 | 42 | 'edit_profile', |
... | ... | @@ -44,6 +45,7 @@ def create_roles |
44 | 45 | 'post_content', |
45 | 46 | 'edit_profile_design', |
46 | 47 | 'manage_products', |
48 | + 'edit_appearance', | |
47 | 49 | ]) |
48 | 50 | # members for enterprises, communities etc |
49 | 51 | Role.create!(:key => "profile_member", :name => N_('Member'), :permissions => [ | ... | ... |
public/stylesheets/common.css
... | ... | @@ -387,3 +387,23 @@ div.pending-tasks { |
387 | 387 | max-width: 100%; |
388 | 388 | overflow: hidden; |
389 | 389 | } |
390 | + | |
391 | +/* theme test panel */ | |
392 | +#theme-test-panel { | |
393 | + z-index: 1000; | |
394 | + position: absolute; | |
395 | + width: 300px; | |
396 | + height: 180; | |
397 | + right: 50px; | |
398 | + top: 50px; | |
399 | + background: white; | |
400 | + border: 2px solid black; | |
401 | +} | |
402 | + | |
403 | +#theme-test-panel { | |
404 | + text-align: center; | |
405 | + cursor: move; | |
406 | +} | |
407 | +#theme-test-panel .button-bar { | |
408 | + margin: 1em; | |
409 | +} | ... | ... |
test/functional/application_controller_test.rb
... | ... | @@ -193,4 +193,25 @@ class ApplicationControllerTest < Test::Unit::TestCase |
193 | 193 | assert_tag :tag => 'base', :attributes => { :href => 'http://www.lala.net' } |
194 | 194 | end |
195 | 195 | |
196 | + should 'display theme test panel when testing theme' do | |
197 | + @request.session[:theme] = 'my-test-theme' | |
198 | + theme = mock | |
199 | + profile = mock | |
200 | + theme.expects(:owner).returns(profile).at_least_once | |
201 | + profile.expects(:identifier).returns('testinguser').at_least_once | |
202 | + Theme.expects(:find).with('my-test-theme').returns(theme).at_least_once | |
203 | + get :index | |
204 | + | |
205 | + assert_tag :tag => 'div', :attributes => { :id => 'theme-test-panel' }, :descendant => { | |
206 | + :tag => 'a', :attributes => { :href => '/myprofile/testinguser/themes/edit/my-test-theme'} | |
207 | + } | |
208 | + #{ :tag => 'a', :attributes => { :href => '/myprofile/testinguser/themes/stop_test/my-test-theme'} } | |
209 | + end | |
210 | + | |
211 | + should 'not display theme test panel in general' do | |
212 | + @controller.stubs(:session).returns({}) | |
213 | + get :index | |
214 | + assert_no_tag :tag => 'div', :attributes => { :id => 'theme-test-panel' } | |
215 | + end | |
216 | + | |
196 | 217 | end | ... | ... |
test/functional/themes_controller_test.rb
1 | 1 | require File.dirname(__FILE__) + '/../test_helper' |
2 | +require 'themes_controller' | |
2 | 3 | |
3 | -class ThemesControllerTest < ActionController::TestCase | |
4 | +class ThemesController; def rescue_action(e) raise e end; end | |
5 | + | |
6 | +class ThemesControllerTest < Test::Unit::TestCase | |
7 | + | |
8 | + def setup | |
9 | + @controller = ThemesController.new | |
10 | + @request = ActionController::TestRequest.new | |
11 | + @response = ActionController::TestResponse.new | |
12 | + | |
13 | + Theme.stubs(:user_themes_dir).returns(TMP_THEMES_DIR) | |
14 | + | |
15 | + @profile = create_user('testinguser').person | |
16 | + login_as('testinguser') | |
17 | + end | |
18 | + attr_reader :profile | |
19 | + | |
20 | + def teardown | |
21 | + FileUtils.rm_rf(TMP_THEMES_DIR) | |
22 | + end | |
23 | + | |
24 | + TMP_THEMES_DIR = RAILS_ROOT + '/test/tmp/themes_controller' | |
4 | 25 | |
5 | 26 | should 'display list of themes for selection' do |
6 | - profile = create_user('testinguser').person | |
7 | 27 | Theme.expects(:system_themes).returns([Theme.new('first'), Theme.new('second')]) |
8 | 28 | get :index, :profile => 'testinguser' |
9 | 29 | |
10 | 30 | %w[ first second ].each do |item| |
11 | - assert_tag :tag => 'a', :attributes => { :href => "/myprofile/testinguser/themes/set/#{item}" }, :descendant => { :tag => 'img' } | |
31 | + assert_tag :tag => 'a', :attributes => { :href => "/myprofile/testinguser/themes/set/#{item}" } | |
12 | 32 | end |
13 | 33 | end |
14 | 34 | |
15 | - should 'save selection of theme' do | |
16 | - profile = create_user('testinguser').person | |
35 | + should 'display list of my themes for edition' do | |
36 | + Theme.create('first', :owner => profile) | |
37 | + Theme.create('second', :owner => profile) | |
38 | + | |
39 | + get :index, :profile => 'testinguser' | |
40 | + | |
41 | + %w[ first second ].each do |item| | |
42 | + assert_tag :tag => 'a', :attributes => { :href => "/myprofile/testinguser/themes/edit/#{item}" } | |
43 | + end | |
44 | + end | |
17 | 45 | |
46 | + should 'save selection of theme' do | |
18 | 47 | get :set, :profile => 'testinguser', :id => 'onetheme' |
19 | 48 | profile.reload |
20 | 49 | assert_equal 'onetheme', profile.theme |
21 | 50 | end |
22 | 51 | |
23 | 52 | should 'point back to control panel' do |
24 | - create_user('testinguser').person | |
25 | 53 | get :index, :profile => 'testinguser' |
26 | 54 | assert_tag :tag => 'a', :attributes => { :href => '/myprofile/testinguser' }, :content => 'Back' |
27 | 55 | end |
28 | 56 | |
29 | - should 'check access control when choosing theme' | |
57 | + should 'display screen for creating new theme' do | |
58 | + @request.expects(:xhr?).returns(true).at_least_once | |
59 | + get :new, :profile => 'testinguser' | |
60 | + assert_tag :tag => 'form', :attributes => { :action => '/myprofile/testinguser/themes/new', :method => /post/i }, :descendant => { :tag => 'input', :attributes => { :type => 'text', :name => 'name' } } | |
61 | + end | |
62 | + | |
63 | + should 'create a new theme' do | |
64 | + post :new, :profile => 'testinguser', :name => 'My theme' | |
65 | + | |
66 | + ok('theme should be created') do | |
67 | + profile.themes.first.id == 'my-theme' | |
68 | + end | |
69 | + end | |
70 | + | |
71 | + should 'edit a theme' do | |
72 | + theme = Theme.create('mytheme', :owner => profile) | |
73 | + get :edit, :profile => 'testinguser', :id => 'mytheme' | |
74 | + | |
75 | + assert_equal theme, assigns(:theme) | |
76 | + end | |
77 | + | |
78 | + should 'list CSS files in theme' do | |
79 | + theme = Theme.create('mytheme', :owner => profile) | |
80 | + theme.add_css('one.css') | |
81 | + theme.add_css('two.css') | |
82 | + | |
83 | + get :edit, :profile => 'testinguser', :id => 'mytheme' | |
84 | + | |
85 | + %w[ one.css two.css ].each do |item| | |
86 | + assert_includes assigns(:css_files), item | |
87 | + assert_tag :tag => 'li', :descendant => { :tag => 'a', :content => item} | |
88 | + end | |
89 | + end | |
90 | + | |
91 | + should 'display dialog for creating new CSS' do | |
92 | + theme = Theme.create('mytheme', :owner => profile) | |
93 | + @request.expects(:xhr?).returns(true) | |
94 | + get :add_css, :profile => 'testinguser', :id => 'mytheme' | |
95 | + | |
96 | + assert_tag :tag => 'form', :attributes => { :action => '/myprofile/testinguser/themes/add_css/mytheme', :method => /post/i} | |
97 | + assert_tag :tag => 'input', :attributes => { :name => 'css', :type => 'text' } | |
98 | + assert_tag :tag => 'input', :attributes => { :type => 'submit' } | |
99 | + end | |
100 | + | |
101 | + should 'be able to add new CSS to theme' do | |
102 | + theme = Theme.create('mytheme', :owner => profile) | |
103 | + post :add_css, :profile => 'testinguser', :id => 'mytheme', :css => 'test.css' | |
104 | + | |
105 | + assert_response :redirect | |
106 | + | |
107 | + reloaded_theme = Theme.find('mytheme') | |
108 | + assert_includes reloaded_theme.css_files, 'test.css' | |
109 | + end | |
110 | + | |
111 | + should 'load code from a given CSS file' do | |
112 | + theme = Theme.create('mytheme', :owner => profile); theme.update_css('test.css', '/* sample code */') | |
113 | + get :css_editor, :profile => 'testinguser', :id => 'mytheme', :css => 'test.css' | |
114 | + | |
115 | + assert_tag :tag => 'form', :attributes => { :action => '/myprofile/testinguser/themes/update_css/mytheme' }, :descendant => { :tag => 'textarea', :content => '/* sample code */' } | |
116 | + end | |
117 | + | |
118 | + should 'be able to save CSS code' do | |
119 | + theme = Theme.create('mytheme', :owner => profile); theme.update_css('test.css', '/* sample code */') | |
120 | + get :css_editor, :profile => 'testinguser', :id => 'mytheme', :css => 'test.css' | |
121 | + | |
122 | + assert_tag :tag => 'form', :attributes => { :action => '/myprofile/testinguser/themes/update_css/mytheme' }, :descendant => { :tag => 'input', :attributes => { :type => 'submit' } } | |
123 | + assert_tag :tag => 'form', :attributes => { :action => '/myprofile/testinguser/themes/update_css/mytheme' }, :descendant => { :tag => 'input', :attributes => { :type => 'hidden', :name => 'css', :value => 'test.css' } } | |
124 | + end | |
125 | + | |
126 | + should 'update css code when saving' do | |
127 | + theme = Theme.create('mytheme', :owner => profile); theme.update_css('test.css', '/* sample code */') | |
128 | + post :update_css, :profile => 'testinguser', :id => 'mytheme', :css => 'test.css', :csscode => 'body { background: white; }' | |
129 | + assert_equal 'body { background: white; }', theme.read_css('test.css') | |
130 | + end | |
131 | + | |
132 | + should 'list image files in theme' do | |
133 | + theme = Theme.create('mytheme', :owner => profile) | |
134 | + theme.add_image('one.png', 'FAKE IMAGE DATA 1') | |
135 | + theme.add_image('two.png', 'FAKE IMAGE DATA 2') | |
136 | + | |
137 | + get :edit, :profile => 'testinguser', :id => 'mytheme' | |
138 | + | |
139 | + assert_tag :tag => 'img', :attributes => { :src => '/user_themes/mytheme/images/one.png' } | |
140 | + assert_tag :tag => 'img', :attributes => { :src => '/user_themes/mytheme/images/two.png' } | |
141 | + end | |
142 | + | |
143 | + should 'display "add image" button' do | |
144 | + theme = Theme.create('mytheme', :owner => profile) | |
145 | + get :edit, :profile => 'testinguser', :id => 'mytheme' | |
146 | + | |
147 | + assert_tag :tag => 'a', :attributes => { :href => '/myprofile/testinguser/themes/add_image/mytheme' } | |
148 | + end | |
30 | 149 | |
31 | - should 'check access control when editing themes' | |
150 | + should 'display the "add image" dialog' do | |
151 | + theme = Theme.create('mytheme', :owner => profile) | |
152 | + @request.expects(:xhr?).returns(true) | |
32 | 153 | |
33 | - should 'only allow environment-approved themes to be selected' | |
154 | + get :add_image, :profile => 'testinguser', :id => 'mytheme' | |
155 | + assert_tag :tag => 'form', :attributes => { :action => '/myprofile/testinguser/themes/add_image/mytheme', :method => /post/i, :enctype => 'multipart/form-data' }, :descendant => { :tag => 'input', :attributes => { :name => 'image', :type => 'file' } } | |
156 | + end | |
34 | 157 | |
35 | - should 'list user-created themes with link for editing' | |
158 | + should 'be able to add new image to theme' do | |
159 | + theme = Theme.create('mytheme', :owner => profile) | |
160 | + @request.expects(:xhr?).returns(false) | |
36 | 161 | |
37 | - should 'offer to create new theme' | |
162 | + post :add_image, :profile => 'testinguser', :id => 'mytheme', :image => fixture_file_upload('/files/rails.png', 'image/png', :binary) | |
163 | + assert_redirected_to :action => "edit", :id => 'mytheme' | |
164 | + assert theme.image_files.include?('rails.png') | |
165 | + assert(system('diff', RAILS_ROOT + '/test/fixtures/files/rails.png', TMP_THEMES_DIR + '/mytheme/images/rails.png'), 'should put the correct uploaded file in the right place') | |
166 | + end | |
38 | 167 | |
39 | - should 'be able to save new theme' | |
168 | + should 'link to "test theme"' do | |
169 | + Theme.create('one', :owner => profile) | |
170 | + Theme.create('two', :owner => profile) | |
171 | + get :index, :profile => 'testinguser' | |
40 | 172 | |
41 | - should 'be able to save existing theme' | |
173 | + %w[ one two ].each do |item| | |
174 | + assert_tag :tag => 'a', :attributes => { :href => '/myprofile/testinguser/themes/start_test/' + item } | |
175 | + end | |
176 | + end | |
177 | + | |
178 | + should 'start testing theme' do | |
179 | + theme = Theme.create('theme-under-test', :owner => profile) | |
180 | + post :start_test, :profile => 'testinguser', :id => 'theme-under-test' | |
181 | + | |
182 | + assert_equal 'theme-under-test', session[:theme] | |
183 | + assert_redirected_to :controller => 'content_viewer', :profile => 'testinguser' | |
184 | + end | |
185 | + | |
186 | + should 'stop testing theme' do | |
187 | + theme = Theme.create('theme-under-test', :owner => profile) | |
188 | + post :stop_test, :profile => 'testinguser', :id => 'theme-under-test' | |
189 | + | |
190 | + assert_nil session[:theme] | |
191 | + assert_redirected_to :action => 'index' | |
192 | + end | |
42 | 193 | |
43 | 194 | end | ... | ... |
test/unit/application_helper_test.rb
... | ... | @@ -4,6 +4,10 @@ class ApplicationHelperTest < Test::Unit::TestCase |
4 | 4 | |
5 | 5 | include ApplicationHelper |
6 | 6 | |
7 | + def setup | |
8 | + self.stubs(:session).returns({}) | |
9 | + end | |
10 | + | |
7 | 11 | should 'calculate correctly partial for object' do |
8 | 12 | self.stubs(:params).returns({:controller => 'test'}) |
9 | 13 | |
... | ... | @@ -150,6 +154,52 @@ class ApplicationHelperTest < Test::Unit::TestCase |
150 | 154 | assert_equal 'my-profile-theme', current_theme |
151 | 155 | end |
152 | 156 | |
157 | + should 'override theme with testing theme from session' do | |
158 | + stubs(:session).returns(:theme => 'theme-under-test') | |
159 | + assert_equal 'theme-under-test', current_theme | |
160 | + end | |
161 | + | |
162 | + should 'point to system theme path by default' do | |
163 | + expects(:current_theme).returns('my-system-theme') | |
164 | + assert_equal '/designs/themes/my-system-theme', theme_path | |
165 | + end | |
166 | + | |
167 | + should 'point to user theme path when testing theme' do | |
168 | + stubs(:session).returns({:theme => 'theme-under-test'}) | |
169 | + assert_equal '/user_themes/theme-under-test', theme_path | |
170 | + end | |
171 | + | |
172 | + should 'render theme footer' do | |
173 | + stubs(:theme_path).returns('/user_themes/mytheme') | |
174 | + footer = '../../public/user_themes/mytheme/footer.rhtml' | |
175 | + | |
176 | + File.expects(:exists?).with(RAILS_ROOT + '/app/views/../../public/user_themes/mytheme/footer.rhtml').returns(true) | |
177 | + expects(:render).with(:file => footer).returns("BLI") | |
178 | + | |
179 | + assert_equal "BLI", theme_footer | |
180 | + end | |
181 | + | |
182 | + should 'ignore unexisting theme footer' do | |
183 | + stubs(:theme_path).returns('/user_themes/mytheme') | |
184 | + footer = '../../public/user_themes/mytheme/footer.rhtml' | |
185 | + | |
186 | + File.expects(:exists?).with(RAILS_ROOT + '/app/views/../../public/user_themes/mytheme/footer.rhtml').returns(false) | |
187 | + expects(:render).with(:file => footer).never | |
188 | + | |
189 | + assert_nil theme_footer | |
190 | + end | |
191 | + | |
192 | + should 'expose theme owner' do | |
193 | + theme = mock | |
194 | + profile = mock | |
195 | + Theme.expects(:find).with('theme-under-test').returns(theme) | |
196 | + theme.expects(:owner).returns(profile) | |
197 | + profile.expects(:identifier).returns('sampleuser') | |
198 | + | |
199 | + stubs(:current_theme).returns('theme-under-test') | |
200 | + | |
201 | + assert_equal 'sampleuser', theme_owner | |
202 | + end | |
153 | 203 | |
154 | 204 | protected |
155 | 205 | ... | ... |
test/unit/profile_test.rb
... | ... | @@ -854,6 +854,33 @@ class ProfileTest < Test::Unit::TestCase |
854 | 854 | assert_equal 1, p.boxes[0].blocks.size |
855 | 855 | end |
856 | 856 | |
857 | + TMP_THEMES_DIR = RAILS_ROOT + '/test/tmp/profile_themes' | |
858 | + should 'have themes' do | |
859 | + Theme.stubs(:user_themes_dir).returns(TMP_THEMES_DIR) | |
860 | + | |
861 | + begin | |
862 | + p1 = Profile.create!(:name => 'test profile 1', :identifier => 'test_profile1') | |
863 | + t = Theme.new('test_theme'); t.owner = p1; t.save | |
864 | + | |
865 | + assert_equal [t], p1.themes | |
866 | + ensure | |
867 | + FileUtils.rm_rf(TMP_THEMES_DIR) | |
868 | + end | |
869 | + end | |
870 | + | |
871 | + should 'find theme by id' do | |
872 | + Theme.stubs(:user_themes_dir).returns(TMP_THEMES_DIR) | |
873 | + | |
874 | + begin | |
875 | + p1 = Profile.create!(:name => 'test profile 1', :identifier => 'test_profile1') | |
876 | + t = Theme.new('test_theme'); t.owner = p1; t.save | |
877 | + | |
878 | + assert_equal t, p1.find_theme('test_theme') | |
879 | + ensure | |
880 | + FileUtils.rm_rf(TMP_THEMES_DIR) | |
881 | + end | |
882 | + end | |
883 | + | |
857 | 884 | private |
858 | 885 | |
859 | 886 | def assert_invalid_identifier(id) | ... | ... |
test/unit/theme_test.rb
1 | 1 | require File.dirname(__FILE__) + '/../test_helper' |
2 | 2 | |
3 | 3 | class ThemeTest < ActiveSupport::TestCase |
4 | + | |
5 | + TMP_THEMES_DIR = RAILS_ROOT + '/test/tmp/themes' | |
6 | + | |
7 | + def setup | |
8 | + Theme.stubs(:user_themes_dir).returns(TMP_THEMES_DIR) | |
9 | + end | |
10 | + | |
11 | + def teardown | |
12 | + FileUtils.rm_rf(TMP_THEMES_DIR) | |
13 | + end | |
14 | + | |
4 | 15 | should 'list system themes' do |
5 | 16 | Dir.expects(:glob).with(RAILS_ROOT + '/public/designs/themes/*').returns( |
6 | 17 | [ |
... | ... | @@ -16,5 +27,127 @@ class ThemeTest < ActiveSupport::TestCase |
16 | 27 | assert_equal 'the-id', Theme.new('the-id').name |
17 | 28 | end |
18 | 29 | |
30 | + should 'create theme' do | |
31 | + t = Theme.create('mytheme') | |
32 | + assert_equal t, Theme.find('mytheme') | |
33 | + end | |
34 | + | |
35 | + should 'not be able to create two themes with the same identifier' do | |
36 | + Theme.create('themeid') | |
37 | + assert_raise Theme::DuplicatedIdentifier do | |
38 | + Theme.create('themeid') | |
39 | + end | |
40 | + end | |
41 | + | |
42 | + should 'not be able to create a theme named after a system theme' do | |
43 | + Theme.expects(:system_themes).returns([Theme.new('somesystemtheme')]) | |
44 | + assert_raise Theme::DuplicatedIdentifier do | |
45 | + Theme.create('somesystemtheme') | |
46 | + end | |
47 | + end | |
48 | + | |
49 | + should 'be able to add new CSS file to theme' do | |
50 | + t = Theme.create('mytheme') | |
51 | + t.add_css('common.css') | |
52 | + assert_equal '', File.read(TMP_THEMES_DIR + '/mytheme/stylesheets/common.css') | |
53 | + end | |
54 | + | |
55 | + should 'be able to update CSS file' do | |
56 | + t = Theme.create('mytheme') | |
57 | + t.add_css('common.css') | |
58 | + t.update_css('common.css', '/* only a comment */') | |
59 | + assert_equal '/* only a comment */', File.read(TMP_THEMES_DIR + '/mytheme/stylesheets/common.css') | |
60 | + end | |
61 | + | |
62 | + should 'be able to get content of CSS file' do | |
63 | + t = Theme.create('mytheme') | |
64 | + t.update_css('common.css', '/* only a comment */') | |
65 | + assert_equal '/* only a comment */', t.read_css('common.css') | |
66 | + end | |
67 | + | |
68 | + should 'force .css suffix for CSS files when adding' do | |
69 | + t = Theme.create('mytheme') | |
70 | + t.add_css('xyz') | |
71 | + assert_includes t.css_files, 'xyz.css' | |
72 | + end | |
73 | + | |
74 | + should 'list CSS files' do | |
75 | + t = Theme.create('mytheme') | |
76 | + t.add_css('one.css') | |
77 | + t.add_css('two.css') | |
78 | + assert_includes t.css_files, 'one.css' | |
79 | + assert_includes t.css_files, 'two.css' | |
80 | + end | |
81 | + | |
82 | + should 'add default stylesheets' do | |
83 | + theme = Theme.create('test') | |
84 | + %w[ common help menu article button search blocks forms login-box ].each do |item| | |
85 | + assert_includes theme.css_files, item + '.css' | |
86 | + end | |
87 | + end | |
88 | + | |
89 | + should 'be able to save twice' do | |
90 | + t = Theme.new('testtheme') | |
91 | + | |
92 | + assert_nothing_raised do | |
93 | + t.save | |
94 | + t.save | |
95 | + end | |
96 | + end | |
97 | + | |
98 | + should 'have an owner' do | |
99 | + profile = create_user('testinguser').person | |
100 | + t = Theme.new('mytheme') | |
101 | + t.owner = profile | |
102 | + t.save | |
103 | + | |
104 | + t = Theme.find('mytheme') | |
105 | + assert_equal profile, t.owner | |
106 | + end | |
107 | + | |
108 | + should 'have no owner by default' do | |
109 | + assert_nil Theme.new('test').owner | |
110 | + end | |
111 | + | |
112 | + should 'be able to find by owner' do | |
113 | + profile = create_user('testinguser').person | |
114 | + t = Theme.new('mytheme') | |
115 | + t.owner = profile | |
116 | + t.save | |
117 | + | |
118 | + assert_equal [t], Theme.find_by_owner(profile) | |
119 | + end | |
120 | + | |
121 | + should 'be able to set attributes in constructor' do | |
122 | + p = create_user('testuser').person | |
123 | + assert_equal p, Theme.new('test', :owner => p).owner | |
124 | + end | |
125 | + | |
126 | + should 'pass attributes to constructor' do | |
127 | + p = create_user('testuser').person | |
128 | + assert_equal p, Theme.create('test', :owner => p).owner | |
129 | + end | |
130 | + | |
131 | + should 'have a name' do | |
132 | + theme = Theme.new('mytheme', :name => 'My Theme') | |
133 | + assert_equal 'My Theme', theme.name | |
134 | + assert_equal 'My Theme', theme.config['name'] | |
135 | + end | |
136 | + | |
137 | + should 'insert image' do | |
138 | + theme = Theme.create('mytheme') | |
139 | + theme.add_image('test.png', 'FAKE IMAGE DATA') | |
140 | + | |
141 | + assert_equal 'FAKE IMAGE DATA', File.read(TMP_THEMES_DIR + '/mytheme/images/test.png') | |
142 | + end | |
143 | + | |
144 | + should 'list images' do | |
145 | + theme = Theme.create('mytheme') | |
146 | + theme.add_image('one.png', 'FAKE IMAGE DATA') | |
147 | + theme.add_image('two.png', 'FAKE IMAGE DATA') | |
148 | + | |
149 | + assert_equivalent [ 'one.png', 'two.png' ], theme.image_files | |
150 | + end | |
151 | + | |
19 | 152 | end |
20 | 153 | ... | ... |