Commit fd4ced10bf7d7367e72c77d209e66a5ba3b5f707

Authored by Larissa Reis
1 parent 66e89085

Makes it possible to order fields by dragdrop

plugins/custom_forms/controllers/custom_forms_plugin_myprofile_controller.rb
@@ -19,6 +19,8 @@ class CustomFormsPluginMyprofileController < MyProfileController @@ -19,6 +19,8 @@ class CustomFormsPluginMyprofileController < MyProfileController
19 params[:form][:profile_id] = profile.id 19 params[:form][:profile_id] = profile.id
20 @form = CustomFormsPlugin::Form.new(params[:form]) 20 @form = CustomFormsPlugin::Form.new(params[:form])
21 21
  22 + normalize_positions(@form)
  23 +
22 respond_to do |format| 24 respond_to do |format|
23 if @form.save 25 if @form.save
24 flash[:notice] = _("Custom form #{@form.name} was successfully created.") 26 flash[:notice] = _("Custom form #{@form.name} was successfully created.")
@@ -35,9 +37,12 @@ class CustomFormsPluginMyprofileController < MyProfileController @@ -35,9 +37,12 @@ class CustomFormsPluginMyprofileController < MyProfileController
35 37
36 def update 38 def update
37 @form = CustomFormsPlugin::Form.find(params[:id]) 39 @form = CustomFormsPlugin::Form.find(params[:id])
  40 + @form.attributes = params[:form]
  41 +
  42 + normalize_positions(@form)
38 43
39 respond_to do |format| 44 respond_to do |format|
40 - if @form.update_attributes(params[:form]) 45 + if @form.save
41 flash[:notice] = _("Custom form #{@form.name} was successfully updated.") 46 flash[:notice] = _("Custom form #{@form.name} was successfully updated.")
42 format.html { redirect_to(:action=>'index') } 47 format.html { redirect_to(:action=>'index') }
43 else 48 else
@@ -84,4 +89,21 @@ class CustomFormsPluginMyprofileController < MyProfileController @@ -84,4 +89,21 @@ class CustomFormsPluginMyprofileController < MyProfileController
84 @form = @submission.form 89 @form = @submission.form
85 end 90 end
86 91
  92 + private
  93 +
  94 + def normalize_positions(form)
  95 + counter = 0
  96 + form.fields.sort_by{ |f| f.position.to_i }.each do |field|
  97 + field.position = counter
  98 + counter += 1
  99 + end
  100 + form.fields.each do |field|
  101 + counter = 0
  102 + field.alternatives.sort_by{ |alt| alt.position.to_i }.each do |alt|
  103 + alt.position = counter
  104 + counter += 1
  105 + end
  106 + end
  107 + end
  108 +
87 end 109 end
plugins/custom_forms/db/migrate/20131107050913_add_position_to_field_and_alternatives.rb 0 → 100644
@@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
  1 +class AddPositionToFieldAndAlternatives < ActiveRecord::Migration
  2 + def self.up
  3 + change_table :custom_forms_plugin_fields do |t|
  4 + t.integer :position, :default => 0
  5 + end
  6 +
  7 + change_table :custom_forms_plugin_alternatives do |t|
  8 + t.integer :position, :default => 0
  9 + end
  10 +
  11 + CustomFormsPlugin::Field.find_each do |f|
  12 + f.position = f.id
  13 + f.save!
  14 + end
  15 +
  16 + CustomFormsPlugin::Alternative.find_each do |f|
  17 + f.position = f.id
  18 + f.save!
  19 + end
  20 + end
  21 +
  22 + def self.down
  23 + change_table :custom_forms_plugin_fields do |t|
  24 + t.remove :position
  25 + end
  26 +
  27 + change_table :custom_forms_plugin_alternatives do |t|
  28 + t.remove :position
  29 + end
  30 + end
  31 +end
plugins/custom_forms/lib/custom_forms_plugin/field.rb
@@ -6,7 +6,7 @@ class CustomFormsPlugin::Field &lt; ActiveRecord::Base @@ -6,7 +6,7 @@ class CustomFormsPlugin::Field &lt; ActiveRecord::Base
6 belongs_to :form, :class_name => 'CustomFormsPlugin::Form' 6 belongs_to :form, :class_name => 'CustomFormsPlugin::Form'
7 has_many :answers, :class_name => 'CustomFormsPlugin::Answer' 7 has_many :answers, :class_name => 'CustomFormsPlugin::Answer'
8 8
9 - has_many :alternatives, :class_name => 'CustomFormsPlugin::Alternative' 9 + has_many :alternatives, :order => 'position', :class_name => 'CustomFormsPlugin::Alternative'
10 accepts_nested_attributes_for :alternatives, :allow_destroy => true 10 accepts_nested_attributes_for :alternatives, :allow_destroy => true
11 11
12 before_validation do |field| 12 before_validation do |field|
plugins/custom_forms/lib/custom_forms_plugin/form.rb
1 class CustomFormsPlugin::Form < Noosfero::Plugin::ActiveRecord 1 class CustomFormsPlugin::Form < Noosfero::Plugin::ActiveRecord
2 belongs_to :profile 2 belongs_to :profile
3 3
4 - has_many :fields, :class_name => 'CustomFormsPlugin::Field', :dependent => :destroy 4 + has_many :fields, :order => 'position', :class_name => 'CustomFormsPlugin::Field', :dependent => :destroy
5 accepts_nested_attributes_for :fields, :allow_destroy => true 5 accepts_nested_attributes_for :fields, :allow_destroy => true
6 6
7 has_many :submissions, :class_name => 'CustomFormsPlugin::Submission' 7 has_many :submissions, :class_name => 'CustomFormsPlugin::Submission'
plugins/custom_forms/public/field.js
  1 +var fixHelperSortable = function(e, tr) {
  2 + tr.children().each(function() {
  3 + jQuery(this).width(jQuery(this).width());
  4 + });
  5 + return tr;
  6 +};
  7 +
  8 +var updatePosition = function(e, ui) {
  9 + var tag = ui.item[0].tagName.toLowerCase();
  10 + var count = ui.item.prevAll(tag).eq(0).find('input').filter(function() {return /_position/.test(this.id); }).val();
  11 + count = count ? ++count : 0;
  12 +
  13 + ui.item.find('input').filter(function() {return /_position/.test(this.id); }).eq(0).val(count);
  14 +
  15 + for (i = 0; i < ui.item.nextAll(tag).length; i++) {
  16 + count++;
  17 + ui.item.nextAll(tag).eq(i).find('input').filter(function() {return /_position/.test(this.id); }).val(count);
  18 + }
  19 +}
  20 +
  21 +jQuery('tbody.field-list').sortable({
  22 + helper: fixHelperSortable,
  23 + update: updatePosition
  24 +});
  25 +
  26 +jQuery("ul.field-list").sortable({
  27 + placeholder: 'ui-state-highlight',
  28 + axis: 'y',
  29 + opacity: 0.8,
  30 + cursor: 'move',
  31 + tolerance: 'pointer',
  32 + forcePlaceholderSize: true,
  33 + update: updatePosition
  34 +});
  35 +
  36 +jQuery("ul.field-list li").disableSelection();
  37 +
1 var customFormsPlugin = { 38 var customFormsPlugin = {
2 removeFieldBox: function (button, confirmMsg) { 39 removeFieldBox: function (button, confirmMsg) {
3 if (confirm(confirmMsg)) { 40 if (confirm(confirmMsg)) {
@@ -20,11 +57,17 @@ var customFormsPlugin = { @@ -20,11 +57,17 @@ var customFormsPlugin = {
20 addFields: function (button, association, content) { 57 addFields: function (button, association, content) {
21 var new_id = new Date().getTime(); 58 var new_id = new Date().getTime();
22 var regexp = new RegExp("new_" + association, "g"); 59 var regexp = new RegExp("new_" + association, "g");
23 - jQuery(content.replace(regexp, new_id)).insertBefore(jQuery(button).closest('.addition-buttons')).hide().slideDown(); 60 + content = content.replace(regexp, new_id);
24 61
25 if(association == 'alternatives') { 62 if(association == 'alternatives') {
  63 + jQuery(content).appendTo(jQuery(button).closest('tfoot').next('tbody.field-list')).hide().slideDown();
26 jQuery(button).closest('table').find('tr:first').show(); 64 jQuery(button).closest('table').find('tr:first').show();
  65 + jQuery(button).closest('tfoot').next('tbody.field-list').sortable({ helper: fixHelperSortable, update: updatePosition});
  66 + } else {
  67 + jQuery('<li>').append(jQuery(content)).appendTo(jQuery(button).parent().prev('ul.field-list')).hide().slideDown();
27 } 68 }
  69 +
  70 + jQuery('input').filter(function () { return new RegExp(new_id + "_position", "g").test(this.id); }).val(new_id);
28 }, 71 },
29 72
30 checkHeaderDisplay: function(table) { 73 checkHeaderDisplay: function(table) {
plugins/custom_forms/public/style.css
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 } 4 }
5 5
6 .action-table { 6 .action-table {
7 - width: 100%; 7 + width: 100%;
8 overflow: hidden; 8 overflow: hidden;
9 } 9 }
10 10
@@ -13,10 +13,6 @@ @@ -13,10 +13,6 @@
13 text-align: center; 13 text-align: center;
14 } 14 }
15 15
16 -.action-table td{  
17 - cursor: move;  
18 -}  
19 -  
20 .action-table .actions{ 16 .action-table .actions{
21 white-space: nowrap; 17 white-space: nowrap;
22 } 18 }
@@ -55,3 +51,22 @@ @@ -55,3 +51,22 @@
55 .field-text-default { 51 .field-text-default {
56 margin-top: 10px; 52 margin-top: 10px;
57 } 53 }
  54 +
  55 +.field-list {
  56 + list-style-type: none;
  57 + margin: 0px;
  58 + padding: 0px;
  59 + cursor: move;
  60 +}
  61 +
  62 +.field-list label, .field-list legend {
  63 + cursor: move;
  64 +}
  65 +
  66 +ul.field-list > li > fieldset:hover {
  67 + border: 2px dotted #BBB;
  68 +}
  69 +
  70 +tr.addition-buttons {
  71 + cursor: auto;
  72 +}
plugins/custom_forms/test/functional/custom_forms_plugin_myprofile_controller_test.rb
@@ -76,8 +76,8 @@ class CustomFormsPluginMyprofileControllerTest &lt; ActionController::TestCase @@ -76,8 +76,8 @@ class CustomFormsPluginMyprofileControllerTest &lt; ActionController::TestCase
76 assert_equal 'Cool form', form.description 76 assert_equal 'Cool form', form.description
77 assert_equal 2, form.fields.count 77 assert_equal 2, form.fields.count
78 78
79 - f1 = form.fields.first  
80 - f2 = form.fields.last 79 + f1 = form.fields[0]
  80 + f2 = form.fields[1]
81 81
82 assert_equal 'Name', f1.name 82 assert_equal 'Name', f1.name
83 assert_equal 'Jack', f1.default_value 83 assert_equal 'Jack', f1.default_value
@@ -89,15 +89,15 @@ class CustomFormsPluginMyprofileControllerTest &lt; ActionController::TestCase @@ -89,15 +89,15 @@ class CustomFormsPluginMyprofileControllerTest &lt; ActionController::TestCase
89 assert f2.kind_of?(CustomFormsPlugin::SelectField) 89 assert f2.kind_of?(CustomFormsPlugin::SelectField)
90 end 90 end
91 91
92 - should 'create fields in the order they are sent' do 92 + should 'create fields in the order they are sent when no position defined' do
93 format = '%Y-%m-%d %H:%M' 93 format = '%Y-%m-%d %H:%M'
94 num_fields = 10 94 num_fields = 10
95 begining = Time.now.strftime(format) 95 begining = Time.now.strftime(format)
96 ending = (Time.now + 1.day).strftime(format) 96 ending = (Time.now + 1.day).strftime(format)
97 fields = {} 97 fields = {}
98 num_fields.times do |i| 98 num_fields.times do |i|
99 - fields[i.to_s] = {  
100 - :name => i.to_s, 99 + fields[i] = {
  100 + :name => (10-i).to_s,
101 :default_value => '', 101 :default_value => '',
102 :type => 'CustomFormsPlugin::TextField' 102 :type => 'CustomFormsPlugin::TextField'
103 } 103 }
@@ -115,13 +115,47 @@ class CustomFormsPluginMyprofileControllerTest &lt; ActionController::TestCase @@ -115,13 +115,47 @@ class CustomFormsPluginMyprofileControllerTest &lt; ActionController::TestCase
115 end 115 end
116 form = CustomFormsPlugin::Form.find_by_name('My Form') 116 form = CustomFormsPlugin::Form.find_by_name('My Form')
117 assert_equal num_fields, form.fields.count 117 assert_equal num_fields, form.fields.count
118 - lst = -1  
119 - form.fields.find_each do |f|  
120 - assert f.name.to_i > lst  
121 - lst = f.name.to_i 118 + lst = 10
  119 + form.fields.each do |f|
  120 + assert f.name.to_i == lst
  121 + lst = lst - 1
122 end 122 end
123 end 123 end
124 124
  125 + should 'create fields in any position size' do
  126 + format = '%Y-%m-%d %H:%M'
  127 + begining = Time.now.strftime(format)
  128 + ending = (Time.now + 1.day).strftime(format)
  129 + fields = {}
  130 + fields['0'] = {
  131 + :name => '0',
  132 + :default_value => '',
  133 + :type => 'CustomFormsPlugin::TextField',
  134 + :position => '999999999999'
  135 + }
  136 + fields['1'] = {
  137 + :name => '1',
  138 + :default_value => '',
  139 + :type => 'CustomFormsPlugin::TextField',
  140 + :position => '1'
  141 + }
  142 + assert_difference CustomFormsPlugin::Form, :count, 1 do
  143 + post :create, :profile => profile.identifier,
  144 + :form => {
  145 + :name => 'My Form',
  146 + :access => 'logged',
  147 + :begining => begining,
  148 + :ending => ending,
  149 + :description => 'Cool form',
  150 + :fields_attributes => fields
  151 + }
  152 + end
  153 + form = CustomFormsPlugin::Form.find_by_name('My Form')
  154 + assert_equal 2, form.fields.count
  155 + assert form.fields.first.name == "1"
  156 + assert form.fields.last.name == "0"
  157 + end
  158 +
125 should 'edit a form' do 159 should 'edit a form' do
126 form = CustomFormsPlugin::Form.create!(:profile => profile, :name => 'Free Software') 160 form = CustomFormsPlugin::Form.create!(:profile => profile, :name => 'Free Software')
127 format = '%Y-%m-%d %H:%M' 161 format = '%Y-%m-%d %H:%M'
plugins/custom_forms/test/unit/custom_forms_plugin/field_test.rb
@@ -32,5 +32,13 @@ class CustomFormsPlugin::FieldTest &lt; ActiveSupport::TestCase @@ -32,5 +32,13 @@ class CustomFormsPlugin::FieldTest &lt; ActiveSupport::TestCase
32 end 32 end
33 assert_equal form.fields, [license_field] 33 assert_equal form.fields, [license_field]
34 end 34 end
  35 +
  36 + should 'sort alternatives by position' do
  37 + field = CustomFormsPlugin::Field.create!(:name => 'field001')
  38 + second = CustomFormsPlugin::Alternative.create!(:label => 'second', :field => field, :position => 2)
  39 + first = CustomFormsPlugin::Alternative.create!(:label => 'first', :field => field, :position => 1)
  40 +
  41 + assert_equal field.alternatives, [first, second]
  42 + end
35 end 43 end
36 44
plugins/custom_forms/test/unit/custom_forms_plugin/form_test.rb
@@ -180,4 +180,12 @@ class CustomFormsPlugin::FormTest &lt; ActiveSupport::TestCase @@ -180,4 +180,12 @@ class CustomFormsPlugin::FormTest &lt; ActiveSupport::TestCase
180 end 180 end
181 end 181 end
182 182
  183 + should 'sort fields by position' do
  184 + form = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => fast_create(Profile))
  185 + license_field = CustomFormsPlugin::Field.create!(:name => 'License', :form => form, :position => 2)
  186 + url_field = CustomFormsPlugin::Field.create!(:name => 'URL', :form => form, :position => 0)
  187 +
  188 + assert_equal form.fields, [url_field, license_field]
  189 + end
  190 +
183 end 191 end
plugins/custom_forms/views/custom_forms_plugin_myprofile/_field.html.erb
@@ -9,6 +9,8 @@ @@ -9,6 +9,8 @@
9 <%= f.check_box :mandatory %> 9 <%= f.check_box :mandatory %>
10 <%= f.label :mandatory, _('Mandatory') %> 10 <%= f.label :mandatory, _('Mandatory') %>
11 11
  12 + <%= f.hidden_field(:position) %>
  13 +
12 <%= f.hidden_field :_destroy, :class => 'destroy-field' %> 14 <%= f.hidden_field :_destroy, :class => 'destroy-field' %>
13 <%= button_to_function :delete, _('Remove field'), "customFormsPlugin.removeFieldBox(this, #{_('Are you sure you want to remove this field?').to_json})" %> 15 <%= button_to_function :delete, _('Remove field'), "customFormsPlugin.removeFieldBox(this, #{_('Are you sure you want to remove this field?').to_json})" %>
14 <%= yield %> 16 <%= yield %>
plugins/custom_forms/views/custom_forms_plugin_myprofile/_form.html.erb
@@ -17,9 +17,13 @@ @@ -17,9 +17,13 @@
17 17
18 <h2><%= _('Fields') %></h2> 18 <h2><%= _('Fields') %></h2>
19 19
20 -<% f.fields_for :fields do |builder| %>  
21 - <%= render partial_for_class(builder.object.class), :f => builder %>  
22 -<% end %> 20 +<ul class='field-list'>
  21 + <% f.fields_for :fields do |builder| %>
  22 + <li>
  23 + <%= render partial_for_class(builder.object.class), :f => builder %>
  24 + </li>
  25 + <% end %>
  26 +</ul>
23 27
24 <div class="addition-buttons"> 28 <div class="addition-buttons">
25 <%= button(:add, _('Add a new text field'), '#', :onclick => "customFormsPlugin.addFields(this, 'fields', #{html_for_field(f, :fields, CustomFormsPlugin::TextField).to_json}); return false")%> 29 <%= button(:add, _('Add a new text field'), '#', :onclick => "customFormsPlugin.addFields(this, 'fields', #{html_for_field(f, :fields, CustomFormsPlugin::TextField).to_json}); return false")%>
plugins/custom_forms/views/custom_forms_plugin_myprofile/custom_forms_plugin/_alternative.html.erb
@@ -3,8 +3,10 @@ @@ -3,8 +3,10 @@
3 3
4 <td> <%= f.check_box(:selected_by_default) %> </td> 4 <td> <%= f.check_box(:selected_by_default) %> </td>
5 5
  6 + <%= f.hidden_field(:position) %>
  7 +
6 <td> 8 <td>
7 <%= f.hidden_field :_destroy, :class => 'destroy-field' %> 9 <%= f.hidden_field :_destroy, :class => 'destroy-field' %>
8 <%= button_to_function_without_text :remove, _('Remove alternative'), "customFormsPlugin.removeAlternative(this, #{_('Are you sure you want to remove this alternative?').to_json})", :class => 'remove-field', :title => _('Remove alternative') %> 10 <%= button_to_function_without_text :remove, _('Remove alternative'), "customFormsPlugin.removeAlternative(this, #{_('Are you sure you want to remove this alternative?').to_json})", :class => 'remove-field', :title => _('Remove alternative') %>
9 </td> 11 </td>
10 -</div> 12 +</tr>
plugins/custom_forms/views/custom_forms_plugin_myprofile/custom_forms_plugin/_select_field.html.erb
@@ -12,20 +12,25 @@ @@ -12,20 +12,25 @@
12 </div> 12 </div>
13 13
14 <table> 14 <table>
  15 + <thead>
15 <tr <%='style="display:none"' if f.object.alternatives.empty? %>> 16 <tr <%='style="display:none"' if f.object.alternatives.empty? %>>
16 <th><%= _('Alternative') %></th> 17 <th><%= _('Alternative') %></th>
17 <th><%= _('Preselected') %></th> 18 <th><%= _('Preselected') %></th>
18 <th><%= _('Remove') %></th> 19 <th><%= _('Remove') %></th>
19 </tr> 20 </tr>
20 - <% f.fields_for :alternatives do |builder| %>  
21 - <%= render partial_for_class(builder.object.class), :f => builder %>  
22 - <% end %> 21 + </thead>
  22 + <tfoot>
23 <tr class="addition-buttons"> 23 <tr class="addition-buttons">
24 <td colspan="3"> 24 <td colspan="3">
25 <%= button(:add, _('Add a new alternative'), '#', :onclick => "customFormsPlugin.addFields(this, 'alternatives', #{html_for_field(f, :alternatives, CustomFormsPlugin::Alternative).to_json}); return false") %> 25 <%= button(:add, _('Add a new alternative'), '#', :onclick => "customFormsPlugin.addFields(this, 'alternatives', #{html_for_field(f, :alternatives, CustomFormsPlugin::Alternative).to_json}); return false") %>
26 </td> 26 </td>
27 </tr> 27 </tr>
  28 + </tfoot>
  29 + <tbody class='field-list'>
  30 + <% f.fields_for :alternatives do |builder| %>
  31 + <%= render partial_for_class(builder.object.class), :f => builder %>
  32 + <% end %>
  33 + </tbody>
28 </table> 34 </table>
29 35
30 -  
31 <% end %> 36 <% end %>