Commit 6c70738c86eee708f050eec365df833a47090abb
1 parent
81fab8d7
Exists in
master
and in
29 other branches
ActionItem498: add ajax power to category selector (not finished yet)
git-svn-id: https://svn.colivre.coop.br/svn/noosfero/trunk@2221 3f533792-8f58-4932-b0fe-aaf55b0a4547
Showing
9 changed files
with
168 additions
and
35 deletions
Show diff stats
app/controllers/my_profile/cms_controller.rb
@@ -115,6 +115,12 @@ class CmsController < MyProfileController | @@ -115,6 +115,12 @@ class CmsController < MyProfileController | ||
115 | render :action => params[:action], :layout => false | 115 | render :action => params[:action], :layout => false |
116 | end | 116 | end |
117 | 117 | ||
118 | + def update_categories | ||
119 | + @current_category = Category.find(params[:category_id]) | ||
120 | + @categories = @current_category.children | ||
121 | + render :partial => 'shared/select_categories', :locals => {:object_name => 'article'}, :layout => false | ||
122 | + end | ||
123 | + | ||
118 | protected | 124 | protected |
119 | 125 | ||
120 | def redirect_back | 126 | def redirect_back |
app/controllers/my_profile/profile_editor_controller.rb
@@ -38,4 +38,10 @@ class ProfileEditorController < MyProfileController | @@ -38,4 +38,10 @@ class ProfileEditorController < MyProfileController | ||
38 | end | 38 | end |
39 | end | 39 | end |
40 | 40 | ||
41 | + def update_categories | ||
42 | + @current_category = Category.find(params[:category_id]) | ||
43 | + @categories = @current_category.children | ||
44 | + render :partial => 'shared/select_categories', :locals => {:object_name => 'profile_data'}, :layout => false | ||
45 | + end | ||
46 | + | ||
41 | end | 47 | end |
app/helpers/application_helper.rb
@@ -216,11 +216,29 @@ module ApplicationHelper | @@ -216,11 +216,29 @@ module ApplicationHelper | ||
216 | end | 216 | end |
217 | 217 | ||
218 | def button_to_function(type, label, js_code, html_options = {}) | 218 | def button_to_function(type, label, js_code, html_options = {}) |
219 | - html_options[:class] = "" unless html_options[:class] | ||
220 | - html_options[:class] << " button #{type}" | 219 | + html_options[:class] = "button with-text" unless html_options[:class] |
220 | + html_options[:class] << " icon-#{type}" | ||
221 | link_to_function(label, js_code, html_options) | 221 | link_to_function(label, js_code, html_options) |
222 | end | 222 | end |
223 | 223 | ||
224 | + def button_to_function(type, label, js_code, html_options = {}, &block) | ||
225 | + html_options[:class] = "button with-text" unless html_options[:class] | ||
226 | + html_options[:class] << " icon-#{type}" | ||
227 | + link_to_function(label, js_code, html_options, &block) | ||
228 | + end | ||
229 | + | ||
230 | + def button_to_function_without_text(type, label, js_code, html_options = {}) | ||
231 | + html_options[:class] = "" unless html_options[:class] | ||
232 | + html_options[:class] << " button icon-#{type}" | ||
233 | + link_to_function(content_tag('span', label), js_code, html_options) | ||
234 | + end | ||
235 | + | ||
236 | + def button_to_function_without_text(type, label, js_code, html_options = {}, &block) | ||
237 | + html_options[:class] = "" unless html_options[:class] | ||
238 | + html_options[:class] << " button icon-#{type}" | ||
239 | + link_to_function(content_tag('span', label), js_code, html_options, &block) | ||
240 | + end | ||
241 | + | ||
224 | def icon(icon_name, html_options = {}) | 242 | def icon(icon_name, html_options = {}) |
225 | the_class = "button #{icon_name}" | 243 | the_class = "button #{icon_name}" |
226 | if html_options.has_key?(:class) | 244 | if html_options.has_key?(:class) |
@@ -420,40 +438,29 @@ module ApplicationHelper | @@ -420,40 +438,29 @@ module ApplicationHelper | ||
420 | 438 | ||
421 | object = instance_variable_get("@#{object_name}") | 439 | object = instance_variable_get("@#{object_name}") |
422 | 440 | ||
423 | - result = content_tag 'h'+title_size.to_s(), title | ||
424 | - result << javascript_tag( 'function open_close_cat( link ) { | ||
425 | - var div = link.parentNode.getElementsByTagName("div")[0]; | ||
426 | - var end = function(){ | ||
427 | - if ( div.style.display == "none" ) { | ||
428 | - this.link.className="button icon-button icon-down" | ||
429 | - } else { | ||
430 | - this.link.className="button icon-button icon-up-red" | ||
431 | - } | ||
432 | - } | ||
433 | - Effect.toggle( div, "slide", { link:link, div:div, afterFinish:end } ) | ||
434 | - }') | ||
435 | - environment.top_level_categories.select{|i| !i.children.empty?}.each do |toplevel| | ||
436 | - next unless object.accept_category?(toplevel) | ||
437 | - # FIXME | ||
438 | - ([toplevel] + toplevel.children_for_menu).each do |cat| | ||
439 | - if cat.top_level? | ||
440 | - result << '<div class="categorie_box">' | ||
441 | - result << icon_button( :down, _('open'), '#', :onclick => 'open_close_cat(this); return false' ) | ||
442 | - result << content_tag('h5', toplevel.name) | ||
443 | - result << '<div style="display:none"><ul class="categories">' | ||
444 | - else | ||
445 | - checkbox_id = "#{object_name}_#{cat.full_name.downcase.gsub(/\s+|\//, '_')}" | ||
446 | - result << content_tag('li', labelled_check_box( | ||
447 | - cat.full_name_without_leading(1, " → "), | ||
448 | - "#{object_name}[category_ids][]", cat.id, | ||
449 | - object.category_ids.include?(cat.id), :id => checkbox_id, | ||
450 | - :onchange => 'this.parentNode.className=(this.checked?"cat_checked":"")' ), | ||
451 | - :class => ( object.category_ids.include?(cat.id) ? 'cat_checked' : '' ) ) + "\n" | ||
452 | - end | ||
453 | - end | ||
454 | - result << '</ul></div></div>' | 441 | + result = content_tag("h#{title_size}", title) + |
442 | + content_tag('ul', object.categories.map{|i| content_tag('li', i.full_name + hidden_field_tag("#{object_name}[category_ids][]", i.id) + button_to_function_without_text(:cancel, _('Remove'), nil, :id => "remove-selected-category-#{i.id}-button"){|page| page["selected-category-#{i.id}"].remove}, :id => "selected-category-#{i.id}")}, :id => 'selected-categories') + | ||
443 | + content_tag('div', nil, :id => 'select-categories') + | ||
444 | + button_to_function(:add, _('Add category'), nil, :id => 'add-category-button') do |page| | ||
445 | + page['add-category-button'].hide | ||
446 | + page['select-categories'].replace_html :partial => 'shared/select_categories', :locals => {:object_name => object_name} | ||
455 | end | 447 | end |
456 | 448 | ||
449 | + #environment.top_level_categories.select{|i| !i.children.empty?}.each do |toplevel| | ||
450 | + # next unless object.accept_category?(toplevel) | ||
451 | + # ([toplevel] + toplevel.children_for_menu).each do |cat| | ||
452 | + # if cat.top_level? | ||
453 | + # result << '<div class="categorie_box">' | ||
454 | + # result << icon_button( :down, _('open'), '#', :onclick => remote_function(:update => "categories_#{cat.id}", :url => { :action => :update_categories, :id => cat, :object_name => object_name, :object_id => object.id})) | ||
455 | + # result << content_tag('h5', toplevel.name) | ||
456 | + # result << "<div id='categories_#{cat.id}'>" | ||
457 | + # else | ||
458 | + # ... | ||
459 | + # end | ||
460 | + # end | ||
461 | + # result << '</div></div>' | ||
462 | + #end | ||
463 | + | ||
457 | content_tag('div', result) | 464 | content_tag('div', result) |
458 | end | 465 | end |
459 | 466 |
app/views/profile_editor/edit.rhtml
@@ -38,7 +38,6 @@ | @@ -38,7 +38,6 @@ | ||
38 | 38 | ||
39 | <%= select_categories(:profile_data, _('Select the categories of your interest'), 1) %> | 39 | <%= select_categories(:profile_data, _('Select the categories of your interest'), 1) %> |
40 | 40 | ||
41 | - | ||
42 | <% button_bar do %> | 41 | <% button_bar do %> |
43 | <%= submit_button('save', _('Save'), :cancel => {:action => 'index'}) %> | 42 | <%= submit_button('save', _('Save'), :cancel => {:action => 'index'}) %> |
44 | <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %> | 43 | <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %> |
@@ -0,0 +1,40 @@ | @@ -0,0 +1,40 @@ | ||
1 | +<% if !@current_category.nil? %> | ||
2 | + <% | ||
3 | + categories = [@current_category] | ||
4 | + categories.push(@current_category) while @current_category = @current_category.parent | ||
5 | + %> | ||
6 | + <% if categories.size > 0 %> | ||
7 | + <%= categories.compact.reverse.map{|i| | ||
8 | + link_to_remote(i.name, | ||
9 | + :update => "select-categories", | ||
10 | + :url => { :action => 'update_categories', :category_id => i.id, :loaded => visual_effect(:highlight, "select-categories") }, | ||
11 | + :class => 'select-current-category-link')}.join(' → ') | ||
12 | + %> | ||
13 | + <strong> | ||
14 | + <%= button_to_function_without_text(:save, _('Save'), nil, :id => 'save-category-button') do |page| | ||
15 | + page.replace_html 'select-categories', '' | ||
16 | + page['add-category-button'].show | ||
17 | + page.insert_html :bottom, 'selected-categories', content_tag('li', categories.first.full_name + | ||
18 | + hidden_field_tag("#{object_name}[category_ids][]", categories.first.id) + | ||
19 | + button_to_function_without_text(:cancel, _('Remove'), nil, :id => "remove-selected-category-#{categories.first.id}-button") {|page| page["selected-category-#{categories.first.id}"].remove}, :id => "selected-category-#{categories.first.id}") | ||
20 | + end %> | ||
21 | + <%= button_to_function_without_text(:cancel, _('Cancel'), nil, :id => 'cancel-category-button') do |page| | ||
22 | + page.replace_html 'select-categories', '' | ||
23 | + page['add-category-button'].show | ||
24 | + end %> | ||
25 | + </strong> | ||
26 | + <% end %> | ||
27 | +<br/> | ||
28 | +<% end %> | ||
29 | +<% if @categories.nil? | ||
30 | + @categories = environment.top_level_categories.select{|i| !i.children.empty?} | ||
31 | +end %> | ||
32 | +<% for category in @categories.each do %> | ||
33 | + <%= link_to_remote category.name, { | ||
34 | + :update => "select-categories", | ||
35 | + :url => { :action => "update_categories", :category_id => category.id }, | ||
36 | + :loaded => visual_effect(:highlight, "select-categories") }, | ||
37 | + :class => 'select-subcategory-link' | ||
38 | + %> | ||
39 | +<% end %> | ||
40 | + |
test/functional/cms_controller_test.rb
@@ -286,6 +286,19 @@ class CmsControllerTest < Test::Unit::TestCase | @@ -286,6 +286,19 @@ class CmsControllerTest < Test::Unit::TestCase | ||
286 | assert_not_includes saved.categories, c2 | 286 | assert_not_includes saved.categories, c2 |
287 | assert_includes saved.categories, c3 | 287 | assert_includes saved.categories, c3 |
288 | end | 288 | end |
289 | + | ||
290 | + should 'not associate articles with category twice' do | ||
291 | + env = Environment.default | ||
292 | + c1 = env.categories.build(:name => "Test category 1"); c1.save! | ||
293 | + c2 = env.categories.build(:name => "Test category 2"); c2.save! | ||
294 | + c3 = env.categories.build(:name => "Test Category 3"); c3.save! | ||
295 | + | ||
296 | + # post is in c1, c3 and c3 | ||
297 | + post :new, :type => TextileArticle.name, :profile => profile.identifier, :article => { :name => 'adding-categories-test', :category_ids => [ c1.id, c3.id, c3.id ] } | ||
298 | + | ||
299 | + saved = profile.articles.find_by_name('adding-categories-test') | ||
300 | + assert_equal [c1, c3, c3], saved.categories | ||
301 | + end | ||
289 | 302 | ||
290 | should 'filter html from textile article name' do | 303 | should 'filter html from textile article name' do |
291 | post :new, :type => 'TextileArticle', :profile => profile.identifier, :article => { :name => 'a <strong>test</strong> article', :body => 'the text of the article ...' } | 304 | post :new, :type => 'TextileArticle', :profile => profile.identifier, :article => { :name => 'a <strong>test</strong> article', :body => 'the text of the article ...' } |
@@ -480,4 +493,15 @@ class CmsControllerTest < Test::Unit::TestCase | @@ -480,4 +493,15 @@ class CmsControllerTest < Test::Unit::TestCase | ||
480 | assert_includes @controller.available_article_types, EnterpriseHomepage | 493 | assert_includes @controller.available_article_types, EnterpriseHomepage |
481 | end | 494 | end |
482 | 495 | ||
496 | + should 'update categories' do | ||
497 | + env = Environment.default | ||
498 | + top = env.categories.create!(:display_in_menu => true, :name => 'Top-Level category') | ||
499 | + c1 = env.categories.create!(:display_in_menu => true, :name => "Test category 1", :parent_id => top.id) | ||
500 | + c2 = env.categories.create!(:display_in_menu => true, :name => "Test category 2", :parent_id => top.id) | ||
501 | + get :update_categories, :profile => profile.identifier, :category_id => top.id | ||
502 | + assert_template 'shared/_select_categories' | ||
503 | + assert_equal top, assigns(:current_category) | ||
504 | + assert_equal [c1, c2], assigns(:categories) | ||
505 | + end | ||
506 | + | ||
483 | end | 507 | end |
test/functional/profile_editor_controller_test.rb
@@ -12,7 +12,9 @@ class ProfileEditorControllerTest < Test::Unit::TestCase | @@ -12,7 +12,9 @@ class ProfileEditorControllerTest < Test::Unit::TestCase | ||
12 | @request = ActionController::TestRequest.new | 12 | @request = ActionController::TestRequest.new |
13 | @response = ActionController::TestResponse.new | 13 | @response = ActionController::TestResponse.new |
14 | login_as('ze') | 14 | login_as('ze') |
15 | + @profile = Person['ze'] | ||
15 | end | 16 | end |
17 | + attr_reader :profile | ||
16 | 18 | ||
17 | def test_local_files_reference | 19 | def test_local_files_reference |
18 | assert_local_files_reference :get, :index, :profile => 'ze' | 20 | assert_local_files_reference :get, :index, :profile => 'ze' |
@@ -432,5 +434,16 @@ class ProfileEditorControllerTest < Test::Unit::TestCase | @@ -432,5 +434,16 @@ class ProfileEditorControllerTest < Test::Unit::TestCase | ||
432 | get :index, :profile => 'testuser' | 434 | get :index, :profile => 'testuser' |
433 | assert_tag :tag => 'a', :content => 'Register a new Enterprise' | 435 | assert_tag :tag => 'a', :content => 'Register a new Enterprise' |
434 | end | 436 | end |
437 | + | ||
438 | + should 'update categories' do | ||
439 | + env = Environment.default | ||
440 | + top = env.categories.create!(:display_in_menu => true, :name => 'Top-Level category') | ||
441 | + c1 = env.categories.create!(:display_in_menu => true, :name => "Test category 1", :parent_id => top.id) | ||
442 | + c2 = env.categories.create!(:display_in_menu => true, :name => "Test category 2", :parent_id => top.id) | ||
443 | + get :update_categories, :profile => profile.identifier, :category_id => top.id | ||
444 | + assert_template 'shared/_select_categories' | ||
445 | + assert_equal top, assigns(:current_category) | ||
446 | + assert_equal [c1, c2], assigns(:categories) | ||
447 | + end | ||
435 | 448 | ||
436 | end | 449 | end |
@@ -0,0 +1,29 @@ | @@ -0,0 +1,29 @@ | ||
1 | +setup :fixtures => :all | ||
2 | +include_partial 'login' | ||
3 | + | ||
4 | +Category.destroy_all | ||
5 | +env = Environment.default | ||
6 | +top = env.categories.create!(:display_in_menu => true, :name => 'Top-Level category') | ||
7 | +c1 = env.categories.create!(:display_in_menu => true, :name => "Test category 1", :parent_id => top.id) | ||
8 | +c2 = env.categories.create!(:display_in_menu => true, :name => "Test category 2", :parent_id => top.id) | ||
9 | +c3 = env.categories.create!(:display_in_menu => true, :name => "Test Category 3", :parent_id => top.id) | ||
10 | + | ||
11 | +click_and_wait 'link=Manage Content' | ||
12 | +click 'link=New article' | ||
13 | +click_and_wait 'link=Text article with Textile markup language' | ||
14 | +click 'link=Add category' | ||
15 | + | ||
16 | +assert_visible 'css=div[id=select-categories]' | ||
17 | +assert_visible 'css=a[class=select-subcategory-link]' | ||
18 | +assert_text 'css=a[class=select-subcategory-link]', 'Top-Level category' | ||
19 | + | ||
20 | +click 'link=Top-Level category' | ||
21 | + | ||
22 | +assert_visible 'css=a[class=select-subcategory-link]' | ||
23 | +assert_text 'css=a[class=select-subcategory-link]', 'Test category 1' | ||
24 | +click 'link=Test category 1' | ||
25 | +assert_not_visible 'css=div[id=select-categories]' | ||
26 | + | ||
27 | +#assert_visible 'css=a[class=select-subcategory-link]' | ||
28 | + | ||
29 | +# vim: ft=ruby |
test/unit/article_test.rb
@@ -349,7 +349,16 @@ class ArticleTest < Test::Unit::TestCase | @@ -349,7 +349,16 @@ class ArticleTest < Test::Unit::TestCase | ||
349 | a = p.articles.create!(:name => 'test', :category_ids => [c1.id, c2.id]) | 349 | a = p.articles.create!(:name => 'test', :category_ids => [c1.id, c2.id]) |
350 | 350 | ||
351 | assert_equivalent [c1, c2], a.categories(true) | 351 | assert_equivalent [c1, c2], a.categories(true) |
352 | + end | ||
352 | 353 | ||
354 | + should 'not add a category twice to article' do | ||
355 | + c1 = Category.create!(:environment => Environment.default, :name => 'c1') | ||
356 | + c2 = c1.children.create!(:environment => Environment.default, :name => 'c2') | ||
357 | + c3 = c2.children.create!(:environment => Environment.default, :name => 'c3') | ||
358 | + owner = create_user('testuser').person | ||
359 | + art = owner.articles.create!(:name => 'ytest') | ||
360 | + art.category_ids = [c2,c3,c3].map(&:id) | ||
361 | + assert_equal [c2, c3], art.categories(true) | ||
353 | end | 362 | end |
354 | 363 | ||
355 | should 'not accept Product category as category' do | 364 | should 'not accept Product category as category' do |