Commit 6c70738c86eee708f050eec365df833a47090abb

Authored by JoenioCosta
1 parent 81fab8d7

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
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, " &rarr; "),  
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') %>
app/views/shared/_select_categories.rhtml 0 → 100644
@@ -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(' &rarr; ')
  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 &lt; Test::Unit::TestCase @@ -286,6 +286,19 @@ class CmsControllerTest &lt; 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 &lt; Test::Unit::TestCase @@ -480,4 +493,15 @@ class CmsControllerTest &lt; 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 &lt; Test::Unit::TestCase @@ -12,7 +12,9 @@ class ProfileEditorControllerTest &lt; 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 &lt; Test::Unit::TestCase @@ -432,5 +434,16 @@ class ProfileEditorControllerTest &lt; 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
test/selenium/edit_article_categories.rsel 0 → 100644
@@ -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 &lt; Test::Unit::TestCase @@ -349,7 +349,16 @@ class ArticleTest &lt; 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