Commit 84ca7256e30e2ac68264f248c8c15457cb134771

Authored by Joenio Costa
2 parents 75f13387 7be7e7c6

Merge branch 'customfields-rails4' into 'master'

Add custom field feature to core

Custom fields can be added to any profile through the admin panel
in the 'Fields' section. They have the same behavior as the current
Noosfero's fields (active, signup, required and privacy).

See merge request !730
Showing 52 changed files with 806 additions and 36 deletions   Show diff stats
app/controllers/admin/features_controller.rb
1 1 class FeaturesController < AdminController
2 2 protect 'edit_environment_features', :environment
  3 + helper CustomFieldsHelper
3 4  
4 5 def index
5 6 @features = Environment.available_features.sort_by{|k,v|v}
... ... @@ -51,6 +52,34 @@ class FeaturesController &lt; AdminController
51 52 redirect_to :action => 'manage_fields'
52 53 end
53 54  
  55 + def manage_custom_fields
  56 + custom_field_list = params[:custom_fields] || {}
  57 +
  58 + custom_fields_to_destroy =
  59 + params[:customized_type].constantize.custom_fields(environment).map(&:id) - custom_field_list.keys.map(&:to_i)
  60 + CustomField.destroy(custom_fields_to_destroy)
  61 +
  62 + custom_field_list.each_pair do |id, custom_field|
  63 + field = CustomField.find_by_id(id)
  64 + if not field.blank?
  65 + params_to_update = custom_field.except(:format, :extras, :customized_type,:environment)
  66 + field.update_attributes(params_to_update)
  67 + else
  68 + if !custom_field[:extras].nil?
  69 + tmp = []
  70 + custom_field[:extras].each_pair do |k, v|
  71 + tmp << v
  72 + end
  73 + custom_field[:extras] = tmp
  74 + end
  75 + field = CustomField.new custom_field.except(:environment)
  76 + field.environment=environment
  77 + field.save if field.valid?
  78 + end
  79 + end
  80 + redirect_to :action => 'manage_fields'
  81 + end
  82 +
54 83 def search_members
55 84 arg = params[:q].downcase
56 85 result = environment.people.where('LOWER(name) LIKE ? OR identifier LIKE ?', "%#{arg}%", "%#{arg}%")
... ...
app/controllers/my_profile/memberships_controller.rb
1 1 class MembershipsController < MyProfileController
2 2  
3 3 protect 'manage_memberships', :profile
  4 + helper CustomFieldsHelper
4 5  
5 6 def index
6 7 @roles = environment.roles.select do |role|
... ...
app/controllers/my_profile/profile_editor_controller.rb
... ... @@ -8,6 +8,7 @@ class ProfileEditorController &lt; MyProfileController
8 8 before_filter :forbid_destroy_profile, :only => [:destroy_profile]
9 9 before_filter :check_user_can_edit_header_footer, :only => [:header_footer]
10 10 helper_method :has_welcome_page
  11 + helper CustomFieldsHelper
11 12  
12 13 def index
13 14 @pending_tasks = Task.to(profile).pending.without_spam.select{|i| user.has_permission?(i.permission, profile)}
... ...
app/controllers/public/account_controller.rb
... ... @@ -6,6 +6,7 @@ class AccountController &lt; ApplicationController
6 6 before_filter :redirect_if_logged_in, :only => [:login, :signup]
7 7 before_filter :protect_from_bots, :only => :signup
8 8  
  9 + helper CustomFieldsHelper
9 10 # say something nice, you goof! something sweet.
10 11 def index
11 12 unless logged_in?
... ...
app/controllers/public/profile_controller.rb
... ... @@ -7,6 +7,7 @@ class ProfileController &lt; PublicController
7 7  
8 8 helper TagsHelper
9 9 helper ActionTrackerHelper
  10 + helper CustomFieldsHelper
10 11  
11 12 protect 'send_mail_to_members', :profile, :only => [:send_mail]
12 13  
... ...
app/helpers/custom_fields_helper.rb 0 → 100644
... ... @@ -0,0 +1,58 @@
  1 +module CustomFieldsHelper
  2 +
  3 + def format_name(format)
  4 + names = {}
  5 + names['string'] = _('String')
  6 + names['text'] = _('Text')
  7 + names['date'] = _('Date')
  8 + names['numeric'] = _('Numeric')
  9 + names['link'] = _('Link')
  10 + names['list'] = _('List')
  11 + names['checkbox'] = _('Checkbox')
  12 + names[format]
  13 + end
  14 +
  15 + def custom_field_forms(customized_type)
  16 + forms = []
  17 + forms << [_('String'), form_for_format(customized_type,'string')]
  18 + forms << [_('Text'), form_for_format(customized_type,'text')]
  19 + forms << [_('Date'), form_for_format(customized_type,'date')]
  20 + forms << [_('Numeric'), form_for_format(customized_type,'numeric')]
  21 + forms << [_('Link'), form_for_format(customized_type,'link')]
  22 + forms << [_('List'), form_for_format(customized_type,'list')]
  23 + forms << [_('Checkbox'), form_for_format(customized_type,'checkbox')]
  24 + forms
  25 + end
  26 +
  27 + def render_extras_field(id, extra=nil, field=nil)
  28 + if extra.nil?
  29 + CGI::escapeHTML((render(:partial => 'features/custom_fields/extras_field', :locals => {:id => id, :extra => nil, :field => field})))
  30 + else
  31 + render :partial => 'features/custom_fields/extras_field', :locals => {:id => id, :extra => extra, :field => field}
  32 + end
  33 + end
  34 +
  35 + def form_for_field(field, customized_type)
  36 + render :partial => 'features/custom_fields/form', :locals => {:field => field}
  37 + end
  38 +
  39 + def display_custom_field_value(custom_field_value)
  40 + value = profile.custom_value(custom_field_value.custom_field.name)
  41 + case custom_field_value.custom_field.format
  42 + when 'text', 'list', 'numeric', 'date', 'string'
  43 + value
  44 + when 'checkbox'
  45 + value == "1" ? _('Yes') : _('No')
  46 + when 'link'
  47 + url = value[/\Ahttps?:\/\//i] ? value : "http://#{value}"
  48 + link_to(value, url, :target => '_blank')
  49 + end
  50 + end
  51 +
  52 + private
  53 +
  54 + def form_for_format(customized_type, format)
  55 + field = CustomField.new(:format => format, :customized_type => customized_type, :environment => environment)
  56 + CGI::escapeHTML((render(:partial => 'features/custom_fields/form', :locals => {:field => field})))
  57 + end
  58 +end
... ...
app/helpers/forms_helper.rb
... ... @@ -186,6 +186,7 @@ module FormsHelper
186 186 element_id = html_options[:id] || 'datepicker-date'
187 187 value = value.strftime(format) if value.present?
188 188 method = datepicker_options[:time] ? 'datetimepicker' : 'datepicker'
  189 + current_date_or_nil = value.present? ? "new Date('#{value}')" : "null"
189 190 result = text_field_tag(name, value, html_options)
190 191 result +=
191 192 "
... ... @@ -236,7 +237,7 @@ module FormsHelper
236 237 weekHeader: #{datepicker_options[:week_header].to_json},
237 238 yearRange: #{datepicker_options[:year_range].to_json},
238 239 yearSuffix: #{datepicker_options[:year_suffix].to_json}
239   - }).datepicker('setDate', new Date('#{value}'))
  240 + }).datepicker('setDate', current_date_or_nil)
240 241 </script>
241 242 ".html_safe
242 243 result
... ...
app/models/community.rb
... ... @@ -29,7 +29,7 @@ class Community &lt; Organization
29 29 # places that call this method are safe from mass-assignment by setting the
30 30 # environment key themselves.
31 31 def self.create_after_moderation(requestor, attributes = {})
32   - environment = attributes.delete(:environment)
  32 + environment = attributes[:environment]
33 33 community = Community.new(attributes)
34 34 community.environment = environment
35 35 if community.environment.enabled?('admin_must_approve_new_communities')
... ...
app/models/custom_field.rb 0 → 100644
... ... @@ -0,0 +1,34 @@
  1 +class CustomField < ActiveRecord::Base
  2 + attr_accessible :name, :default_value, :format, :extras, :customized_type, :active, :required, :signup, :environment
  3 + serialize :customized_type
  4 + serialize :extras
  5 + has_many :custom_field_values, :dependent => :delete_all
  6 + belongs_to :environment
  7 +
  8 + validates_presence_of :name, :format, :customized_type, :environment
  9 + validate :related_to_other?
  10 + validate :unique?
  11 +
  12 + def unique?
  13 + if environment.custom_fields.any?{|cf| cf.name==name && cf.environment == environment && cf.customized_type==customized_type && new_record?}
  14 + errors.add(:body, N_("There is a field with the same name for this type in this environment"))
  15 + return false
  16 + end
  17 + true
  18 + end
  19 +
  20 + def related_to_other?
  21 + environment.custom_fields.any? do |cf|
  22 + if cf.name == name && cf.customized_type != customized_type
  23 + ancestor = cf.customized_type.constantize < customized_type.constantize
  24 + descendant = cf.customized_type.constantize > customized_type.constantize
  25 + if ancestor || descendant
  26 + errors.add(:body, N_("New field related to existent one with same name"))
  27 + return false
  28 + end
  29 + end
  30 + end
  31 + true
  32 + end
  33 +end
  34 +
... ...
app/models/custom_field_value.rb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +class CustomFieldValue < ActiveRecord::Base
  2 + belongs_to :custom_field
  3 + belongs_to :customized, :polymorphic => true
  4 + attr_accessible :value, :public, :customized, :custom_field, :customized_type
  5 + validate :can_save?
  6 +
  7 + def can_save?
  8 + if value.blank? && custom_field.required
  9 + errors.add(custom_field.name, _("can't be blank"))
  10 + return false
  11 + end
  12 + return true
  13 + end
  14 +end
... ...
app/models/environment.rb
... ... @@ -24,6 +24,7 @@ class Environment &lt; ActiveRecord::Base
24 24  
25 25 has_many :tasks, :dependent => :destroy, :as => 'target'
26 26 has_many :search_terms, :as => :context
  27 + has_many :custom_fields, :dependent => :destroy
27 28  
28 29 IDENTIFY_SCRIPTS = /(php[0-9s]?|[sp]htm[l]?|pl|py|cgi|rb)/
29 30  
... ...
app/models/profile.rb
... ... @@ -84,6 +84,7 @@ class Profile &lt; ActiveRecord::Base
84 84 }
85 85  
86 86 acts_as_accessible
  87 + acts_as_customizable
87 88  
88 89 include Noosfero::Plugin::HotSpot
89 90  
... ...
app/views/account/_signup_form.html.erb
... ... @@ -126,6 +126,7 @@
126 126 </div>
127 127  
128 128 <%= recaptcha_tags :ajax => true, :display => {:theme => 'clean'} if @block_bot %>
  129 +<%= render :partial => 'shared/custom_fields', :locals => {:f => f, :profile => @person, :signup => true} %>
129 130  
130 131 <p style="text-align: center">
131 132 <%= submit_button('save', _('Create my account')) %>
... ...
app/views/custom_fields/_checkbox.html.erb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= labelled_check_box(field.name, name, 1, profile.custom_value(field.name) == '1' )%>
... ...
app/views/custom_fields/_date.html.erb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= labelled_form_field(field.name, date_field(name, profile.custom_value(field.name).to_date, '%Y-%m-%d', {:change_month => true, :change_year => true, :year_range => '-100:-5', :date_format => 'yy-mm-dd'}, {:id => field.name.parameterize.underscore}))%>
... ...
app/views/custom_fields/_link.html.erb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= render :partial => "custom_fields/string", :locals => {:field => field, :profile => profile, :name => name} %>
... ...
app/views/custom_fields/_list.html.erb 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +<%= label_tag field.name, nil, class: 'formlabel'%>
  2 +<%= select_tag name, options_for_select(field.extras.map{|v| [v,v]}, profile.custom_value(field.name)), {:prompt => _('[Select ...]') } %>
... ...
app/views/custom_fields/_numeric.html.erb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= labelled_form_field field.name, number_field_tag(name,profile.custom_value(field.name))%>
... ...
app/views/custom_fields/_string.html.erb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= labelled_form_field field.name, text_field_tag(name, profile.custom_value(field.name), :size => 30) %>
... ...
app/views/custom_fields/_text.html.erb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= labelled_form_field field.name, text_area_tag(name,profile.custom_value(field.name), :rows => 5, :cols => 40)%>
... ...
app/views/features/_manage_community_fields.html.erb
... ... @@ -63,6 +63,4 @@
63 63  
64 64 <% end %>
65 65  
66   -
67   -
68   -
  66 +<%= render :partial => "manage_custom_fields", :locals => {:customized_type => Community} %>
... ...
app/views/features/_manage_custom_fields.html.erb 0 → 100644
... ... @@ -0,0 +1,29 @@
  1 +<h1><%= _("Custom Fields") %></h1><hr>
  2 +
  3 +<% form_id = "#{customized_type.to_s.downcase}-custom-fields-form" %>
  4 +<% fields_id = "#{customized_type.to_s.downcase}-custom-fields" %>
  5 +<% format_values_id = "#{customized_type.to_s.downcase}-formats" %>
  6 +
  7 +<%= form_tag({:action => 'manage_custom_fields'}, :id => "#{form_id}") do %>
  8 +
  9 +<%= hidden_field_tag 'customized_type', customized_type.to_s %>
  10 +
  11 +<div id="<%= fields_id %>">
  12 + <% customized_type.custom_fields(environment).each do |field|%>
  13 + <%= form_for_field(field, customized_type.to_s) %>
  14 + <% end %>
  15 +</div>
  16 +
  17 +<div class="addition-buttons">
  18 + <fieldset>
  19 + <legend><%= _('New field') %></legend>
  20 + <%= select_tag _('Type: '), options_for_select(custom_field_forms(customized_type.to_s)), {:id => "#{format_values_id}"} %>
  21 + <%= button(:add, _('Add'), 'javascript: void()', :onClick => "add_content('##{fields_id}',$('##{format_values_id} option:selected').val(), 'NEW_FIELD_ID');") %>
  22 + </fieldset>
  23 +</div>
  24 +
  25 +<% button_bar do %>
  26 + <%= button(:save, _('Save'), 'javascript: void()', :onClick => "submit_custom_field_form('##{format_values_id}', '##{form_id}');") %>
  27 +<% end %>
  28 +
  29 +<% end %>
... ...
app/views/features/_manage_enterprise_fields.html.erb
... ... @@ -63,6 +63,4 @@
63 63  
64 64 <% end %>
65 65  
66   -
67   -
68   -
  66 +<%= render :partial => "manage_custom_fields", :locals => {:customized_type => Enterprise} %>
... ...
app/views/features/_manage_person_fields.html.erb
... ... @@ -60,9 +60,7 @@
60 60 <%= button :back, _('Back to admin panel'), :controller => 'admin_panel', :action => 'index' %>
61 61 <% end %>
62 62 </div>
63   -
  63 +<br>
64 64 <% end %>
65 65  
66   -
67   -
68   -
  66 +<%= render :partial => "manage_custom_fields", :locals => {:customized_type => Person} %>
... ...
app/views/features/custom_fields/_extras_field.html.erb 0 → 100644
... ... @@ -0,0 +1,15 @@
  1 +<% field_id = extra.nil? ? 'EXTRAS_ID' : "#{Time.now.usec}" %>
  2 +<% def_value = field.nil? ? '' : field.default_value%>
  3 +<tr id='<%= "extras_#{field_id}" %>' >
  4 + <td>
  5 + <%= text_field_tag "custom_fields[#{id}][extras][#{field_id}]", (extra.nil? ? '' : extra), :onkeyup => "update_default_value($(this).val(), '#custom_fields_#{id}_extras_#{field_id}_default')" %>
  6 + </td>
  7 + <td>
  8 + <%= check_box_tag "custom_fields[#{id}][default_value]", extra.blank? ? '' : extra, extra.blank? ? false : def_value == extra, :id => "custom_fields_#{id}_extras_#{field_id}_default", :onclick => "manage_default_option(this);" %>
  9 + </td>
  10 + <td>
  11 + <% if extra.nil? %>
  12 + <%= button_to_function_without_text :remove, _('Remove'), "remove_content('#extras_#{field_id}'); ", :class => 'remove-field', :title => 'Remove alternative' %>
  13 + <% end %>
  14 + </td>
  15 +</tr>
... ...
app/views/features/custom_fields/_form.html.erb 0 → 100644
... ... @@ -0,0 +1,43 @@
  1 +<% id = field.new_record? ? "NEW_FIELD_ID" : field.id %>
  2 +
  3 +<div id="<%= id %>" class="custom-field-item">
  4 +<fieldset class="fieldbox">
  5 + <legend><%= format_name(field.format) %></legend>
  6 + <%= required labelled_form_field _('Name'), text_field_tag("custom_fields[#{id}][name]", field.name, :size => 30) %>
  7 + <%= button_to_function :delete, _('Remove field'), "remove_content('##{id}');" %>
  8 +
  9 + <% if field.format != "list" %>
  10 + <%= labelled_form_field _('Default value'), text_field_tag("custom_fields[#{id}][default_value]", field.default_value, :size => 30) %>
  11 + <% end %>
  12 + <%= hidden_field_tag "custom_fields[#{id}][customized_type]", field.customized_type.to_s %>
  13 + <%= hidden_field_tag "custom_fields[#{id}][format]", field.format %>
  14 +
  15 + <div>
  16 + <%= labelled_check_box _('Active'), "custom_fields[#{id}][active]", 1, field.active, :id => "active_checkbox", :onclick => "active_action('custom_fields[#{id}][active]','custom_fields[#{id}][required]', 'custom_fields[#{id}][signup]')" %>
  17 + <%= labelled_check_box _('Required'), "custom_fields[#{id}][required]", 1, field.required, :id => "required_checkbox", :onclick => "required_action('custom_fields[#{id}][active]','custom_fields[#{id}][required]', 'custom_fields[#{id}][signup]')" %>
  18 + <%= labelled_check_box _('Display on creation?'), "custom_fields[#{id}][signup]", 1, field.signup, :id => "signup_checkbox",:onclick => "signup_action('custom_fields[#{id}][active]','custom_fields[#{id}][required]', 'custom_fields[#{id}][signup]')" %>
  19 + </div>
  20 +
  21 + <% if field.format == "list" %>
  22 + <table>
  23 + <thead>
  24 + <tr>
  25 + <th><%= _("Alternative") %></th>
  26 + <th><%= _("Default") %></th>
  27 + <th><%= _("Delete") %></th>
  28 + </tr>
  29 + </thead>
  30 + <tfoot>
  31 + <tr><td colspan=3><%= button(:add, _('Add option'), 'javascript: void()', :id => "btn_opt_#{id}", :onclick => "add_content('##{id} .custom-field-extras', $('#btn_opt_#{id}').attr('value'), 'EXTRAS_ID');", :value => "#{render_extras_field(id)}") %></td></tr>
  32 + </tfoot>
  33 + <tbody class="custom-field-extras">
  34 + <% if !field.extras.blank?%>
  35 + <% field.extras.each do |extra|%>
  36 + <%= render_extras_field id, extra, field %>
  37 + <% end %>
  38 + <% end %>
  39 + </tbody>
  40 + </table>
  41 + <% end %>
  42 +</fieldset>
  43 +</div>
... ...
app/views/features/custom_fields/_view.html.erb 0 → 100644
app/views/features/custom_fields/edit.html.erb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= render :partial => 'features/custom_fields/form' %>
... ...
app/views/features/custom_fields/new.html.erb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= render :partial => 'features/custom_fields/form' %>
... ...
app/views/memberships/new_community.html.erb
... ... @@ -13,7 +13,6 @@
13 13 <%= labelled_form_for :community, :html => { :multipart => true } do |f| %>
14 14  
15 15 <%= required_fields_message %>
16   -
17 16 <%= required f.text_field(:name) %>
18 17  
19 18 <% @plugins.dispatch(:new_community_hidden_fields).each do |field| %>
... ... @@ -23,6 +22,7 @@
23 22 <% end %>
24 23  
25 24 <%= render :partial => 'shared/organization_custom_fields', :locals => { :f => f, :object_name => 'community', :profile => @community } %>
  25 + <%= render :partial => 'shared/custom_fields', :locals => { :f => f, :profile => @community, :signup => true } %>
26 26  
27 27 <%= f.fields_for :image_builder, @community.image do |i| %>
28 28 <%= file_field_or_thumbnail(_('Image:'), @community.image, i) %>
... ... @@ -56,7 +56,3 @@
56 56 <% end %>
57 57  
58 58 </div>
59   -
60   -
61   -
62   -
... ...
app/views/profile/_custom_fields.html.erb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +<% public_values = profile.public_values %>
  2 +<% if !public_values.blank?%>
  3 + <tr> <th colspan="2"><%= _('Others') %> </th></tr>
  4 + <% profile.public_values.each do |cv|%>
  5 + <tr>
  6 + <td class="field-name"><%= cv.custom_field.name %></td>
  7 + <td><%= display_custom_field_value(cv) %></td>
  8 + </tr>
  9 + <% end%>
  10 +<% end %>
  11 +
... ...
app/views/profile/_organization_profile.html.erb
... ... @@ -3,4 +3,5 @@
3 3 <%= display_contact %>
4 4 <%= display_economic %>
5 5 <%= render :partial => 'common' %>
  6 + <%= render :partial => 'custom_fields' %>
6 7 </table>
... ...
app/views/profile/_person_profile.html.erb
... ... @@ -10,5 +10,6 @@
10 10  
11 11 <%= render :partial => 'common' %>
12 12 <% end %>
13   -</table>
14 13  
  14 + <%= render :partial => 'custom_fields'%>
  15 +</table>
... ...
app/views/profile_editor/_organization.html.erb
... ... @@ -61,6 +61,7 @@
61 61 <% end %>
62 62  
63 63 <%= render :partial => 'shared/organization_custom_fields', :locals => { :f => f, :object_name => 'profile_data', :profile => @profile } %>
  64 +<%= render :partial => 'shared/custom_fields', :locals => {:f => f, :profile => @profile, :editing_profile => true} %>
64 65  
65 66 <%= labelled_check_box(_('Enable "contact us"'), 'profile_data[enable_contact_us]', "1", @profile.enable_contact_us) if @profile.enterprise? %>
66 67  
... ...
app/views/profile_editor/_person.html.erb
... ... @@ -27,6 +27,7 @@
27 27 <%= link_to("Reset token", {:controller => :profile_editor, :action => :reset_private_token, :id => @profile.id}, :class => "button with-text") %>
28 28  
29 29 <%= render :partial => 'person_form', :locals => {:f => f} %>
  30 + <%= render :partial => 'shared/custom_fields', :locals => {:f => f, :profile => @profile, :editing_profile => true} %>
30 31  
31 32 <h2><%= _('Notification options') %></h2>
32 33 <div>
... ...
app/views/shared/_custom_fields.html.erb 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +<% if signup ||= false %>
  2 + <% fields = profile.class.signup_custom_fields environment%>
  3 +<% else %>
  4 + <% fields = profile.class.active_custom_fields environment%>
  5 +<% end %>
  6 +<% editing_profile ||= false %>
  7 +<% fields.each do |field| %>
  8 + <% rendered = render(:partial => "custom_fields/#{field.format}", :locals => {:field => field, :profile => profile, :name => "profile_data[custom_values[#{field.name}[value]]]"})%>
  9 + <div class="<%= 'field-with-privacy-selector' if editing_profile %>">
  10 +
  11 + <% if field.required%>
  12 + <%= required rendered%>
  13 + <% else %>
  14 + <%= rendered %>
  15 + <% end %>
  16 +
  17 + <% if editing_profile %>
  18 + <div class="field-privacy-selector">
  19 + <%= labelled_check_box(_('Public'),"profile_data[custom_values[#{field.name}[public]]]", "true", profile.is_public(field.name))%>
  20 + </div>
  21 + <% end %>
  22 +
  23 + </div>
  24 +<% end %>
  25 +
... ...
app/views/shared/_organization_custom_fields.html.erb
... ... @@ -29,4 +29,7 @@
29 29 <%= optional_field(profile, 'acronym', f.text_field(:acronym)) %>
30 30 <%= optional_field(profile, 'foundation_year', f.text_field(:foundation_year)) %>
31 31 <% end %>
  32 +
  33 +<%= render :partial => 'shared/custom_fields', :locals => {:f => f, :profile => profile, :signup => true} %>
  34 +
32 35 <%= javascript_include_tag('city_state_validation') %>
... ...
config/initializers/dependencies.rb
... ... @@ -18,6 +18,7 @@ require &#39;acts_as_having_settings&#39;
18 18 require 'acts_as_having_boxes'
19 19 require 'acts_as_having_image'
20 20 require 'acts_as_having_posts'
  21 +require 'acts_as_customizable'
21 22 require 'route_if'
22 23 require 'maybe_add_http'
23 24 require 'set_profile_region_from_city_state'
... ...
db/migrate/20150921140802_create_custom_fields.rb 0 → 100644
... ... @@ -0,0 +1,31 @@
  1 +class CreateCustomFields < ActiveRecord::Migration
  2 + def change
  3 + create_table :custom_fields do |t|
  4 + t.string :name
  5 + t.string :format, :default => ""
  6 + t.text :default_value, :default => ""
  7 + t.string :customized_type
  8 + t.text :extras, :default => ""
  9 + t.boolean :active, :default => false
  10 + t.boolean :required, :default => false
  11 + t.boolean :signup, :default => false
  12 + t.integer :environment_id
  13 + t.timestamps
  14 + end
  15 +
  16 + create_table :custom_field_values do |t|
  17 + t.column "customized_type", :string, :default => "", :null => false
  18 + t.column "customized_id", :integer, :default => 0, :null => false
  19 + t.column "public", :boolean, :default => false, :null => false
  20 + t.column "custom_field_id", :integer, :default => 0, :null => false
  21 + t.column "value", :text, :default => ""
  22 + t.timestamps
  23 + end
  24 +
  25 +
  26 + add_index :custom_field_values, ["customized_type", "customized_id","custom_field_id"], :unique => true, :name => 'index_custom_field_values'
  27 + add_index :custom_fields, ["customized_type","name","environment_id"], :unique => true, :name => 'index_custom_field'
  28 +
  29 + end
  30 +end
  31 +
... ...
lib/acts_as_customizable.rb 0 → 100644
... ... @@ -0,0 +1,125 @@
  1 +module Customizable
  2 +
  3 + def self.included(base)
  4 + base.attr_accessible :custom_values
  5 + base.extend ClassMethods
  6 + end
  7 +
  8 + module ClassMethods
  9 + def acts_as_customizable(options = {})
  10 + attr_accessor :custom_values
  11 + has_many :custom_field_values, :dependent => :delete_all, :as => :customized
  12 + send :include, Customizable::InstanceMethods
  13 + after_save :save_custom_values
  14 + validate :valid_custom_values?
  15 + end
  16 +
  17 + def active_custom_fields environment
  18 + environment.custom_fields.select{|cf| customized_ancestors_list.include?(cf.customized_type) && cf.active}
  19 + end
  20 +
  21 + def required_custom_fields environment
  22 + environment.custom_fields.select{|cf| customized_ancestors_list.include?(cf.customized_type) && cf.required}
  23 + end
  24 +
  25 + def signup_custom_fields environment
  26 + environment.custom_fields.select{|cf| customized_ancestors_list.include?(cf.customized_type) && cf.signup}
  27 + end
  28 +
  29 + def custom_fields environment
  30 + environment.custom_fields.select{|cf| customized_ancestors_list.include?(cf.customized_type)}
  31 + end
  32 +
  33 + def customized_ancestors_list
  34 + current=self
  35 + result=[]
  36 + while current.instance_methods.include? :custom_value do
  37 + result << current.name
  38 + current=current.superclass
  39 + end
  40 + result
  41 + end
  42 +
  43 + end
  44 +
  45 + module InstanceMethods
  46 +
  47 + def valid_custom_values?
  48 + is_valid = true
  49 + parse_custom_values.each do |cv|
  50 + unless cv.valid?
  51 + name = cv.custom_field.name
  52 + errors.add(name, cv.errors.messages[name.to_sym].first)
  53 + is_valid = false
  54 + end
  55 + end
  56 + is_valid
  57 + end
  58 +
  59 + def customized_class
  60 + current=self.class
  61 + while current.instance_methods.include? :custom_fields do
  62 + result=current
  63 + current=current.superclass
  64 + end
  65 + result.name
  66 + end
  67 +
  68 + def is_public(field_name)
  69 + cv = self.custom_field_values.detect{|cv| cv.custom_field.name==field_name}
  70 + cv.nil? ? false : cv.public
  71 + end
  72 +
  73 + def public_values
  74 + self.custom_field_values.select{|cv| cv.public}
  75 + end
  76 +
  77 + def custom_value(field_name)
  78 + cv = self.custom_field_values.detect{|cv| cv.custom_field.name==field_name}
  79 + cv.nil? ? default_value_for(field_name) : cv.value
  80 + end
  81 +
  82 + def default_value_for(field_name)
  83 + field=self.class.custom_fields(environment).detect {|c| c.name == field_name}
  84 + field.nil? ? nil : field.default_value
  85 + end
  86 +
  87 + def parse_custom_values
  88 + return_list = []
  89 + return return_list if custom_values.blank?
  90 + custom_values.each_pair do |key, value|
  91 + custom_field = environment.custom_fields.detect{|cf|cf.name==key}
  92 + next if custom_field.blank?
  93 + custom_field_value = self.custom_field_values.detect{|cv| cv.custom_field.name==key}
  94 +
  95 + if custom_field_value.nil?
  96 + custom_field_value = CustomFieldValue.new
  97 + custom_field_value.custom_field = custom_field
  98 + custom_field_value.customized = self
  99 + end
  100 +
  101 + if value.is_a?(Hash)
  102 + custom_field_value.value = value['value'].to_s
  103 + if value.has_key?('public')
  104 + is_public = value['public']=="true" || value['public']==true
  105 + custom_field_value.public = is_public
  106 + else
  107 + custom_field_value.public = false
  108 + end
  109 + else
  110 + custom_field_value.value = value.to_s
  111 + custom_field_value.public = false
  112 + end
  113 + return_list << custom_field_value
  114 + end
  115 + return_list
  116 + end
  117 +
  118 + def save_custom_values
  119 + parse_custom_values.each(&:save)
  120 + end
  121 +
  122 + end
  123 +end
  124 +
  125 +ActiveRecord::Base.send(:include, Customizable)
... ...
lib/noosfero/api/entities.rb
... ... @@ -15,7 +15,7 @@ module Noosfero
15 15 }
16 16  
17 17 def self.can_display? profile, options, field, permission = :friend
18   - return true if profile.public_fields.include?(field)
  18 + return true if profile.public_fields.map{|f| f.to_sym}.include?(field.to_sym)
19 19 current_person = options[:current_person]
20 20  
21 21 current_permission = if current_person.present?
... ... @@ -31,7 +31,6 @@ module Noosfero
31 31 else
32 32 :anonymous
33 33 end
34   -
35 34 PERMISSIONS[current_permission] <= PERMISSIONS[permission]
36 35 end
37 36  
... ... @@ -84,6 +83,20 @@ module Noosfero
84 83 expose :identifier, :name, :id
85 84 expose :created_at, :format_with => :timestamp
86 85 expose :updated_at, :format_with => :timestamp
  86 + expose :additional_data do |profile, options|
  87 + hash ={}
  88 + profile.public_values.each do |value|
  89 + hash[value.custom_field.name]=value.value
  90 + end
  91 +
  92 + private_values = profile.custom_field_values - profile.public_values
  93 + private_values.each do |value|
  94 + if Entities.can_display?(profile,options,:custom_field)
  95 + hash[value.custom_field.name]=value.value
  96 + end
  97 + end
  98 + hash
  99 + end
87 100 expose :image, :using => Image
88 101 expose :region, :using => Region
89 102 end
... ...
lib/noosfero/api/v1/comments.rb
... ... @@ -18,12 +18,12 @@ module Noosfero
18 18 article = find_article(environment.articles, params[:id])
19 19 comments = select_filtered_collection_of(article, :comments, params)
20 20  
21   - present comments, :with => Entities::Comment
  21 + present comments, :with => Entities::Comment, :current_person => current_person
22 22 end
23 23  
24 24 get ":id/comments/:comment_id" do
25 25 article = find_article(environment.articles, params[:id])
26   - present article.comments.find(params[:comment_id]), :with => Entities::Comment
  26 + present article.comments.find(params[:comment_id]), :with => Entities::Comment, :current_person => current_person
27 27 end
28 28  
29 29 # Example Request:
... ... @@ -31,7 +31,7 @@ module Noosfero
31 31 post ":id/comments" do
32 32 article = find_article(environment.articles, params[:id])
33 33 options = params.select { |key,v| !['id','private_token'].include?(key) }.merge(:author => current_person, :source => article)
34   - present Comment.create(options), :with => Entities::Comment
  34 + present Comment.create(options), :with => Entities::Comment, :current_person => current_person
35 35 end
36 36 end
37 37  
... ...
lib/noosfero/api/v1/communities.rb
... ... @@ -20,14 +20,21 @@ module Noosfero
20 20 communities = select_filtered_collection_of(environment, 'communities', params)
21 21 communities = communities.visible_for_person(current_person)
22 22 communities = communities.by_location(params) # Must be the last. May return Exception obj.
23   - present communities, :with => Entities::Community
  23 + present communities, :with => Entities::Community, :current_person => current_person
24 24 end
25 25  
26 26  
27 27 # Example Request:
28 28 # POST api/v1/communties?private_token=234298743290432&community[name]=some_name
  29 + # for each custom field for community, add &community[field_name]=field_value to the request
29 30 post do
30 31 params[:community] ||= {}
  32 +
  33 + params[:community][:custom_values]={}
  34 + params[:community].keys.each do |key|
  35 + params[:community][:custom_values][key]=params[:community].delete(key) if Community.custom_fields(environment).any?{|cf| cf.name==key}
  36 + end
  37 +
31 38 begin
32 39 community = Community.create_after_moderation(current_person, params[:community].merge({:environment => environment}))
33 40 rescue
... ... @@ -38,12 +45,12 @@ module Noosfero
38 45 render_api_errors!(community.errors.full_messages)
39 46 end
40 47  
41   - present community, :with => Entities::Community
  48 + present community, :with => Entities::Community, :current_person => current_person
42 49 end
43 50  
44 51 get ':id' do
45 52 community = environment.communities.visible_for_person(current_person).find_by_id(params[:id])
46   - present community, :with => Entities::Community
  53 + present community, :with => Entities::Community, :current_person => current_person
47 54 end
48 55  
49 56 end
... ... @@ -58,7 +65,7 @@ module Noosfero
58 65 person = environment.people.find(params[:person_id])
59 66 communities = select_filtered_collection_of(person, 'communities', params)
60 67 communities = communities.visible
61   - present communities, :with => Entities::Community
  68 + present communities, :with => Entities::Community, :current_person => current_person
62 69 end
63 70  
64 71 end
... ...
lib/noosfero/api/v1/enterprises.rb
... ... @@ -21,13 +21,13 @@ module Noosfero
21 21 enterprises = select_filtered_collection_of(environment, 'enterprises', params)
22 22 enterprises = enterprises.visible_for_person(current_person)
23 23 enterprises = enterprises.by_location(params) # Must be the last. May return Exception obj.
24   - present enterprises, :with => Entities::Enterprise
  24 + present enterprises, :with => Entities::Enterprise, :current_person => current_person
25 25 end
26 26  
27 27 desc "Return one enterprise by id"
28 28 get ':id' do
29 29 enterprise = environment.enterprises.visible_for_person(current_person).find_by_id(params[:id])
30   - present enterprise, :with => Entities::Enterprise
  30 + present enterprise, :with => Entities::Enterprise, :current_person => current_person
31 31 end
32 32  
33 33 end
... ... @@ -42,7 +42,7 @@ module Noosfero
42 42 person = environment.people.find(params[:person_id])
43 43 enterprises = select_filtered_collection_of(person, 'enterprises', params)
44 44 enterprises = enterprises.visible.by_location(params)
45   - present enterprises, :with => Entities::Enterprise
  45 + present enterprises, :with => Entities::Enterprise, :current_person => current_person
46 46 end
47 47  
48 48 end
... ...
lib/noosfero/api/v1/people.rb
... ... @@ -33,30 +33,31 @@ module Noosfero
33 33 get do
34 34 people = select_filtered_collection_of(environment, 'people', params)
35 35 people = people.visible_for_person(current_person)
36   - present people, :with => Entities::Person
  36 + present people, :with => Entities::Person, :current_person => current_person
37 37 end
38 38  
39 39 desc "Return the logged user information"
40 40 get "/me" do
41   - present current_person, :with => Entities::Person
  41 + present current_person, :with => Entities::Person, :current_person => current_person
42 42 end
43 43  
44 44 desc "Return the person information"
45 45 get ':id' do
46 46 person = environment.people.visible_for_person(current_person).find_by_id(params[:id])
47 47 return not_found! if person.blank?
48   - present person, :with => Entities::Person
  48 + present person, :with => Entities::Person, :current_person => current_person
49 49 end
50 50  
51 51 desc "Update person information"
52 52 post ':id' do
53 53 return forbidden! if current_person.id.to_s != params[:id]
54 54 current_person.update_attributes!(params[:person])
55   - present current_person, :with => Entities::Person
  55 + present current_person, :with => Entities::Person, :current_person => current_person
56 56 end
57 57  
58 58 # Example Request:
59 59 # POST api/v1/people?person[login]=some_login&person[password]=some_password&person[name]=Jack
  60 + # for each custom field for person, add &person[field_name]=field_value to the request
60 61 desc "Create person"
61 62 post do
62 63 user_data = {}
... ... @@ -64,14 +65,21 @@ module Noosfero
64 65 user_data[:email] = params[:person].delete(:email)
65 66 user_data[:password] = params[:person].delete(:password)
66 67 user_data[:password_confirmation] = params[:person].delete(:password_confirmation)
  68 +
  69 + params[:person][:custom_values]={}
  70 + params[:person].keys.each do |key|
  71 + params[:person][:custom_values][key]=params[:person].delete(key) if Person.custom_fields(environment).any?{|cf| cf.name==key}
  72 + end
  73 +
67 74 user = User.build(user_data, params[:person], environment)
  75 +
68 76 begin
69 77 user.signup!
70 78 rescue ActiveRecord::RecordInvalid
71 79 render_api_errors!(user.errors.full_messages)
72 80 end
73 81  
74   - present user.person, :with => Entities::Person
  82 + present user.person, :with => Entities::Person, :current_person => user.person
75 83 end
76 84  
77 85 desc "Return the person friends"
... ... @@ -79,7 +87,7 @@ module Noosfero
79 87 person = environment.people.visible_for_person(current_person).find_by_id(params[:id])
80 88 return not_found! if person.blank?
81 89 friends = person.friends.visible
82   - present friends, :with => Entities::Person
  90 + present friends, :with => Entities::Person, :current_person => current_person
83 91 end
84 92  
85 93 desc "Return the person permissions on other profiles"
... ...
public/javascripts/manage-fields.js
... ... @@ -36,6 +36,34 @@ function signup_action(name_active, name_required, name_signup) {
36 36 update_active(name_active, name_required, name_signup)
37 37 }
38 38  
  39 +function add_content(target_id, content, mask) {
  40 + var id = new Date().getTime();
  41 + var regexp = new RegExp(mask, "g");
  42 + content = content.replace(regexp, id);
  43 + $(target_id).append(content);
  44 + $('#' + id).hide().slideDown();
  45 +}
  46 +
  47 +function remove_content(target) {
  48 + $(target).remove();
  49 +}
  50 +
  51 +function submit_custom_field_form(selector_id, form_id, customized_type) {
  52 + $(selector_id).attr('disabled', true);
  53 + $(form_id).submit();
  54 +}
  55 +
  56 +function manage_default_option(source) {
  57 + var th = $(source);
  58 + var name = th.prop('name');
  59 + if(th.is(':checked')){
  60 + $(':checkbox[name="' + name + '"]').not($(source)).prop('checked',false);
  61 + }
  62 +}
  63 +
  64 +function update_default_value(source, target) {
  65 + $(target).val(source);
  66 +}
39 67  
40 68 jQuery(document).ready(function(){
41 69 function check_fields(check, table_id, start) {
... ...
public/stylesheets/manage-fields.scss
... ... @@ -9,3 +9,11 @@
9 9 font-style: italic;
10 10 }
11 11  
  12 +.custom-field-item {
  13 + position: relative;
  14 + a.icon-delete {
  15 + position: absolute;
  16 + top: 20px;
  17 + right: 20px;
  18 + }
  19 +}
... ...
test/functional/features_controller_test.rb
... ... @@ -8,6 +8,7 @@ class FeaturesControllerTest &lt; ActionController::TestCase
8 8 @controller = FeaturesController.new
9 9 @request = ActionController::TestRequest.new
10 10 @response = ActionController::TestResponse.new
  11 +
11 12 login_as(create_admin_user(Environment.find(2)))
12 13 end
13 14  
... ... @@ -159,4 +160,51 @@ class FeaturesControllerTest &lt; ActionController::TestCase
159 160 assert_includes json_response, {"id"=>person.id, "name"=>person.name}
160 161 end
161 162  
  163 + should 'create custom field' do
  164 + uses_host 'anhetegua.net'
  165 + assert_nil Environment.find(2).custom_fields.find_by_name('foo')
  166 + post :manage_custom_fields, :customized_type => 'Person', :custom_fields => {
  167 + Time.now.to_i => {
  168 + :name => 'foo',
  169 + :default_value => 'foobar',
  170 + :format => 'string',
  171 + :customized_type => 'Person',
  172 + :active => true,
  173 + :required => true,
  174 + :signup => true
  175 + }
  176 + }
  177 + assert_redirected_to :action => 'manage_fields'
  178 + assert_not_nil Environment.find(2).custom_fields.find_by_name('foo')
  179 + end
  180 +
  181 + should 'update custom field' do
  182 + uses_host 'anhetegua.net'
  183 +
  184 + field = CustomField.create! :name => 'foo', :default_value => 'foobar', :format => 'string', :extras => '', :customized_type => 'Enterprise', :active => true, :required => true, :signup => true, :environment => Environment.find(2)
  185 + post :manage_custom_fields, :customized_type => 'Enterprise', :custom_fields => {
  186 + field.id => {
  187 + :name => 'foo bar',
  188 + :default_value => 'foobar',
  189 + :active => true,
  190 + :required => true,
  191 + :signup => true
  192 + }
  193 + }
  194 + field.reload
  195 + assert_redirected_to :action => 'manage_fields'
  196 + assert_equal 'foo bar', field.name
  197 + end
  198 +
  199 + should 'destroy custom field' do
  200 + uses_host 'anhetegua.net'
  201 +
  202 + field = CustomField.create! :name => 'foo', :default_value => 'foobar', :format => 'string', :extras => '', :customized_type => 'Enterprise', :active => true, :required => true, :signup => true, :environment => Environment.find(2)
  203 +
  204 + post :manage_custom_fields, :customized_type => 'Enterprise'
  205 +
  206 + assert_redirected_to :action => 'manage_fields'
  207 + assert_nil Environment.find(2).custom_fields.find_by_name('foo')
  208 + end
  209 +
162 210 end
... ...
test/unit/acts_as_customizable_test.rb 0 → 100644
... ... @@ -0,0 +1,23 @@
  1 +require_relative "../test_helper"
  2 +
  3 +class ActsAsCustomizableTest < ActiveSupport::TestCase
  4 +
  5 + should 'save custom field values for person' do
  6 + CustomField.create!(:name => "Blog", :format => "string", :customized_type => "Person", :active => true, :environment => Environment.default)
  7 + person = create_user('testinguser').person
  8 + assert_difference 'CustomFieldValue.count' do
  9 + person.custom_values = { "Blog" => { "value" => "www.blog.org", "public" => "0"} }
  10 + person.save!
  11 + assert_equal 'www.blog.org', CustomFieldValue.find(:last, :conditions => {:customized_id => person.id}).value
  12 + end
  13 + end
  14 +
  15 + should 'not be valid when required custom field not filled' do
  16 + CustomField.create!(:name => "Blog", :format => "string", :customized_type => "Person", :active => true, :environment => Environment.default, :required => true)
  17 + person = create_user('testinguser').person
  18 +
  19 + person.custom_values = { "Blog" => { "value" => "", "public" => "0"} }
  20 + refute person.valid?
  21 + end
  22 +
  23 +end
... ...
test/unit/api/people_test.rb
... ... @@ -165,4 +165,44 @@ class PeopleTest &lt; ActiveSupport::TestCase
165 165 assert_equal another_name, person.name
166 166 end
167 167  
  168 + should 'display public custom fields' do
  169 + CustomField.create!(:name => "Custom Blog", :format => "string", :customized_type => "Person", :active => true, :environment => Environment.default)
  170 + some_person = create_user('some-person').person
  171 + some_person.custom_values = { "Custom Blog" => { "value" => "www.blog.org", "public" => "true"} }
  172 + some_person.save!
  173 +
  174 + get "/api/v1/people/#{some_person.id}?#{params.to_query}"
  175 + json = JSON.parse(last_response.body)
  176 + assert json['person']['additional_data'].has_key?('Custom Blog')
  177 + assert_equal "www.blog.org", json['person']['additional_data']['Custom Blog']
  178 + end
  179 +
  180 + should 'not display non-public custom fields' do
  181 + CustomField.create!(:name => "Custom Blog", :format => "string", :customized_type => "Person", :active => true, :environment => Environment.default)
  182 + some_person = create_user('some-person').person
  183 + some_person.custom_values = { "Custom Blog" => { "value" => "www.blog.org", "public" => "0"} }
  184 + some_person.save!
  185 +
  186 + get "/api/v1/people/#{some_person.id}?#{params.to_query}"
  187 + json = JSON.parse(last_response.body)
  188 + assert_equal json['person']['additional_data'], {}
  189 + end
  190 +
  191 + should 'display non-public custom fields to friend' do
  192 + CustomField.create!(:name => "Custom Blog", :format => "string", :customized_type => "Person", :active => true, :environment => Environment.default)
  193 + some_person = create_user('some-person').person
  194 + some_person.custom_values = { "Custom Blog" => { "value" => "www.blog.org", "public" => "0"} }
  195 + some_person.save!
  196 +
  197 + f = Friendship.new
  198 + f.friend = some_person
  199 + f.person = person
  200 + f.save!
  201 +
  202 + get "/api/v1/people/#{some_person.id}?#{params.to_query}"
  203 + json = JSON.parse(last_response.body)
  204 + assert json['person']['additional_data'].has_key?("Custom Blog")
  205 + assert_equal "www.blog.org", json['person']['additional_data']['Custom Blog']
  206 + end
  207 +
168 208 end
... ...
test/unit/custom_field_test.rb 0 → 100644
... ... @@ -0,0 +1,153 @@
  1 +require_relative "../test_helper"
  2 +
  3 +class CustomFieldTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @person = create_user('test_user').person
  7 +
  8 + @e1 = Environment.default
  9 + @e2 = fast_create(Environment)
  10 +
  11 + @community = create(Community, :environment => @e1, :name => 'my new community')
  12 +
  13 + @community_custom_field = CustomField.create(:name => "community_field", :format=>"myFormat", :default_value => "value for community", :customized_type=>"Community", :active => true, :environment => @e1)
  14 + @person_custom_field = CustomField.create(:name => "person_field", :format=>"myFormat", :default_value => "value for person", :customized_type=>"Person", :active => true, :environment => @e1)
  15 + @profile_custom_field = CustomField.create(:name => "profile_field", :format=>"myFormat", :default_value => "value for any profile", :customized_type=>"Profile", :active => true, :environment => @e1)
  16 +
  17 + @e1.reload
  18 + end
  19 +
  20 + should 'not access another environments custom fields' do
  21 + @e2_custom_field = CustomField.create(:name => "another_field", :format=>"anoherFormat", :default_value => "default value for e2", :customized_type=>"Profile", :active => true, :environment => @e2)
  22 + @e2.reload
  23 +
  24 + assert_equal 1, Profile.custom_fields(@e1).size
  25 + assert_equal @profile_custom_field, Profile.custom_fields(@e1).first
  26 +
  27 + assert_equal 1, Profile.custom_fields(@e2).size
  28 + assert_equal @e2_custom_field, Profile.custom_fields(@e2).first
  29 +
  30 + end
  31 +
  32 + should 'no access to custom field on sibling' do
  33 + refute (Person.custom_fields(@e1).any?{|cf| cf.name == @community_custom_field.name})
  34 + refute (Community.custom_fields(@e1).any?{|cf| cf.name == @person_custom_field.name})
  35 + end
  36 +
  37 + should 'inheritance of custom_field' do
  38 + assert Community.custom_fields(@e1).any?{|cf| cf.name == @profile_custom_field.name}
  39 + assert Person.custom_fields(@e1).any?{|cf| cf.name == @profile_custom_field.name}
  40 + end
  41 +
  42 + should 'save custom_field_values' do
  43 + @community.custom_values = {"community_field" => "new_value!", "profile_field"=> "another_value!"}
  44 + @community.save
  45 +
  46 + assert CustomFieldValue.all.any?{|cv| cv.custom_field_id == @community_custom_field.id && cv.customized_id == @community.id && cv.value == "new_value!"}
  47 + assert CustomFieldValue.all.any?{|cv| cv.custom_field_id == @profile_custom_field.id && cv.customized_id == @community.id && cv.value = "another_value!"}
  48 + end
  49 +
  50 + should 'delete custom field and its values' do
  51 + @community.custom_values = {"community_field" => "new_value!", "profile_field"=> "another_value!"}
  52 + @community.save
  53 +
  54 + old_id = @community_custom_field.id
  55 + @community_custom_field.destroy
  56 +
  57 + @e1.reload
  58 + refute (@e1.custom_fields.any?{|cf| cf.id == old_id})
  59 + refute (Community.custom_fields(@e1).any?{|cf| cf.name == "community_field"})
  60 + refute (CustomFieldValue.all.any?{|cv| cv.custom_field_id == old_id})
  61 + end
  62 +
  63 + should 'not save related custom field' do
  64 + another_field = CustomField.create(:name => "profile_field", :format=>"myFormat", :default_value => "value for any profile", :customized_type=>"Community", :environment => @e1)
  65 + assert another_field.id.nil?
  66 + end
  67 +
  68 + should 'not save same custom field twice in the same environment' do
  69 + field = CustomField.create(:name => "the new field", :format=>"myFormat", :customized_type=>"Community", :environment => @e1)
  70 + refute field.id.nil?
  71 + @e1.reload
  72 + another = CustomField.new(:name => "the new field", :format=>"myFormat", :customized_type=>"Community", :environment => @e1)
  73 + refute another.valid?
  74 + end
  75 +
  76 + should 'save same custom field in another environment' do
  77 + field = CustomField.create(:name => "the new field", :format=>"myFormat", :customized_type=>"Community", :environment => @e1)
  78 + refute field.id.nil?
  79 + another_field = CustomField.create(:name => "the new field", :format=>"myFormat", :customized_type=>"Community", :environment => @e2)
  80 + refute another_field.id.nil?
  81 + end
  82 +
  83 + should 'not return inactive fields' do
  84 + @community_custom_field.update_attributes(:active=>false)
  85 + @e1.reload
  86 + refute Community.active_custom_fields(@e1).any?{|cf| cf.name == @community_custom_field.name}
  87 + end
  88 +
  89 + should 'delete a model and its custom field values' do
  90 + @community.custom_values = {"community_field" => "new_value!", "profile_field"=> "another_value!"}
  91 + @community.save
  92 +
  93 + old_id = @community.id
  94 + @community.destroy
  95 + refute (Community.all.any?{|c| c.id == old_id})
  96 + refute (CustomFieldValue.all.any?{|cv| cv.customized_id == old_id && cv.customized_type == "Community"})
  97 + end
  98 +
  99 + should 'keep field value if the field is reactivated' do
  100 +
  101 + @community.custom_values = {"community_field" => "new_value!"}
  102 + @community.save
  103 +
  104 + @community_custom_field.update_attributes(:active=>false)
  105 + @e1.reload
  106 + refute Community.active_custom_fields(@e1).any?{|cf| cf.name == @community_custom_field.name}
  107 +
  108 + @community_custom_field.update_attributes(:active=>true)
  109 +
  110 + @e1.reload
  111 + @community.reload
  112 + assert Community.active_custom_fields(@e1).any?{|cf| cf.name == @community_custom_field.name}
  113 + assert_equal @community.custom_value("community_field"), "new_value!"
  114 + end
  115 +
  116 + should 'list of required fields' do
  117 + refute Community.required_custom_fields(@e1).any?{|cf| cf.name == @community_custom_field.name}
  118 +
  119 + @community_custom_field.update_attributes(:required=>true)
  120 + @community.reload
  121 + @e1.reload
  122 + assert Community.required_custom_fields(@e1).any?{|cf| cf.name == @community_custom_field.name}
  123 + end
  124 +
  125 + should 'list of signup fields' do
  126 + refute Community.signup_custom_fields(@e1).any?{|cf| cf.name == @community_custom_field.name}
  127 +
  128 + @community_custom_field.update_attributes(:signup=>true)
  129 + @community.reload
  130 + @e1.reload
  131 + assert Community.signup_custom_fields(@e1).any?{|cf| cf.name == @community_custom_field.name}
  132 + end
  133 +
  134 + should 'public values handling' do
  135 + refute @community.is_public("community_field")
  136 + @community.custom_values = {"community_field" => {"value" => "new_value!", "public"=>"true"}, "profile_field"=> "another_value!"}
  137 + @community.save
  138 + @community.reload
  139 +
  140 + assert @community.is_public("community_field")
  141 + refute @community.is_public("profile_field")
  142 + end
  143 +
  144 + should 'complete list of fields' do
  145 + assert Person.custom_fields(@e1).include? @profile_custom_field
  146 + assert Person.custom_fields(@e1).include? @person_custom_field
  147 + end
  148 +
  149 + should 'get correct customized ancestors list' do
  150 + assert (Person.customized_ancestors_list-["Person","Profile"]).blank?
  151 + end
  152 +end
  153 +
... ...
test/unit/custom_field_values_test.rb 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +require_relative "../test_helper"
  2 +
  3 +class CustomFieldValuesTest < ActiveSupport::TestCase
  4 +
  5 + should 'custom field value not be valid' do
  6 + c = CustomField.create!(:name => "Blog", :format => "string", :customized_type => "Person", :active => true, :required => true, :environment => Environment.default)
  7 + person = create_user('testinguser').person
  8 +
  9 + cv=CustomFieldValue.new(:customized => person, :custom_field => c, :value => "")
  10 + refute cv.valid?
  11 + end
  12 +end
... ...