diff --git a/app/controllers/my_profile/friends_controller.rb b/app/controllers/my_profile/friends_controller.rb index f452cfa..93a401f 100644 --- a/app/controllers/my_profile/friends_controller.rb +++ b/app/controllers/my_profile/friends_controller.rb @@ -1,5 +1,3 @@ -require "contacts" - class FriendsController < MyProfileController protect 'manage_friends', :profile @@ -30,83 +28,6 @@ class FriendsController < MyProfileController end end - def invite - @wizard = params[:wizard].blank? ? false : params[:wizard] - @step = 3 - if request.post? && params[:import] - begin - case params[:import_from] - when "gmail" - @friends = Contacts::Gmail.new(params[:login], params[:password]).contacts - when "yahoo" - @friends = Contacts::Yahoo.new(params[:login], params[:password]).contacts - when "hotmail" - @friends = Contacts::Hotmail.new(params[:login], params[:password]).contacts - else - @friends = [] - end - @friends.map! {|friend| friend + ["#{friend[0]} <#{friend[1]}>"]} - rescue - @login = params[:login] - flash.now[:notice] = __('There was an error while looking for your contact list. Did you enter correct login and password?') - end - - elsif request.post? && params[:confirmation] - friends_to_invite = [] - friends_to_invite += (params[:manual_import_addresses].is_a?(Array) ? params[:manual_import_addresses] : params[:manual_import_addresses].split("\r\n")) if params[:manual_import_addresses] - friends_to_invite += (params[:webmail_import_addresses].is_a?(Array) ? params[:webmail_import_addresses] : params[:webmail_import_addresses].split("\r\n")) if params[:webmail_import_addresses] - - if !params[:message].match(//) - flash.now[:notice] = __('<url> is needed in invitation message.') - elsif !friends_to_invite.empty? - friends_to_invite.each do |friend_to_invite| - next if friend_to_invite == __("Firstname Lastname ") - - friend_to_invite.strip! - if match = friend_to_invite.match(/(.*)<(.*)>/) and match[2].match(Noosfero::Constants::EMAIL_FORMAT) - friend_name = match[1].strip - friend_email = match[2] - elsif match = friend_to_invite.strip.match(Noosfero::Constants::EMAIL_FORMAT) - friend_name = "" - friend_email = match[0] - else - next - end - - friend = User.find_by_email(friend_email) - if friend.nil? - InviteFriend.create(:person => profile, :friend_name => friend_name, :friend_email => friend_email, :message => params[:message]) - elsif !friend.person.is_a_friend?(profile) - InviteFriend.create(:person => profile, :friend => friend.person) - end - end - - flash[:notice] = __('Your invitations have been sent.') - if @wizard - redirect_to :action => 'invite', :wizard => true - return - else - redirect_to :action => 'index' - end - else - flash.now[:notice] = __('Please enter a valid email address.') - end - - @friends = params[:webmail_friends] ? params[:webmail_friends].map {|e| YAML.load(e)} : [] - @manual_import_addresses = params[:manual_import_addresses] || "" - @webmail_import_addresses = params[:webmail_import_addresses] || [] - end - - @import_from = params[:import_from] || "manual" - @message = params[:message] || environment.message_for_friend_invitation - if @wizard - if !params[:import] - @friends = [] - end - render :layout => 'wizard' - end - end - protected class << self @@ -117,4 +38,5 @@ class FriendsController < MyProfileController def per_page self.class.per_page end + end diff --git a/app/controllers/public/invite_controller.rb b/app/controllers/public/invite_controller.rb new file mode 100644 index 0000000..8e94e8f --- /dev/null +++ b/app/controllers/public/invite_controller.rb @@ -0,0 +1,50 @@ +class InviteController < PublicController + + needs_profile + before_filter :login_required + before_filter :check_permissions_to_invite, :only => 'friends' + + def friends + step = params[:step] + if request.post? + if step == '1' + begin + @contacts = Invitation.get_contacts(params[:import_from], params[:login], params[:password]) + rescue + @login = params[:login] + flash.now[:notice] = _('There was an error while looking for your contact list. Did you enter correct login and password?') + end + elsif step == '2' + manual_import_addresses = params[:manual_import_addresses] + webmail_import_addresses = params[:webmail_import_addresses] + contacts_to_invite = Invitation.join_contacts(manual_import_addresses, webmail_import_addresses) + if !params[:mail_template].match(//) + flash.now[:notice] = _('<url> is needed in invitation mail.') + elsif !contacts_to_invite.empty? + Invitation.invite(current_user.person, contacts_to_invite, params[:mail_template], profile) + flash[:notice] = _('Your invitations have been sent.') + redirect_back_or_default :controller => 'profile' + else + flash.now[:notice] = _('Please enter a valid email address.') + end + @contacts = params[:webmail_friends] ? params[:webmail_friends].map {|e| YAML.load(e)} : [] + @manual_import_addresses = manual_import_addresses || "" + @webmail_import_addresses = webmail_import_addresses || [] + end + else + store_location(request.referer) + end + @import_from = params[:import_from] || "manual" + @mail_template = params[:mail_template] || environment.invitation_mail_template(profile) + end + + protected + + def check_permissions_to_invite + if profile.person? and !current_user.person.has_permission?(:manage_friends, profile) or + profile.community? and !current_user.person.has_permission?(:invite_members, profile) + render_access_denied + end + end + +end diff --git a/app/helpers/invite_helper.rb b/app/helpers/invite_helper.rb new file mode 100644 index 0000000..389f123 --- /dev/null +++ b/app/helpers/invite_helper.rb @@ -0,0 +1,2 @@ +module InviteHelper +end diff --git a/app/models/environment.rb b/app/models/environment.rb index aa98133..f7314eb 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -385,21 +385,30 @@ class Environment < ActiveRecord::Base signup_fields end - # Default message send to friend when user use invite a friend feature + def invitation_mail_template(profile) + if profile.person? + message_for_friend_invitation + else + message_for_member_invitation + end + end + def message_for_friend_invitation - self.settings['message_for_friend_invitation'] || [ - _('Hello ,'), - _(' is inviting you to participate on %{environment}.') % { :environment => self.name }, - _('To accept the invitation, please follow this link:') + "\n" + '', - "--\n#{self.name}", - '' - ].join("\n\n") + self.settings['message_for_friend_invitation'] || InviteFriend.mail_template end def message_for_friend_invitation=(value) self.settings['message_for_friend_invitation'] = value end + def message_for_member_invitation + self.settings['message_for_member_invitation'] || InviteMember.mail_template + end + + def message_for_member_invitation=(value) + self.settings['message_for_member_invitation'] = value + end + def custom_enterprise_fields self.settings[:custom_enterprise_fields].nil? ? {} : self.settings[:custom_enterprise_fields] end diff --git a/app/models/invitation.rb b/app/models/invitation.rb new file mode 100644 index 0000000..e48ff5c --- /dev/null +++ b/app/models/invitation.rb @@ -0,0 +1,113 @@ +class Invitation < Task + + acts_as_having_settings :field => :data + settings_items :message, :friend_name, :friend_email + + validates_presence_of :requestor_id + + validates_presence_of :target_id, :if => Proc.new{|invite| invite.friend_email.blank?} + + validates_presence_of :friend_email, :if => Proc.new{|invite| invite.target_id.blank?} + validates_format_of :friend_email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => Proc.new{|invite| invite.target_id.blank?} + + validates_presence_of :message, :if => Proc.new{|invite| invite.target_id.blank?} + validates_format_of :message, :with => //, :if => Proc.new{|invite| invite.target_id.blank?} + + alias :person :requestor + alias :person= :requestor= + + alias :friend :target + alias :friend= :target= + + after_create do |task| + TaskMailer.deliver_invitation_notification(task) unless task.friend + end + + def validate + super + email = friend ? friend.user.email : friend_email + if person && email && person.user.email == email + self.errors.add_to_base(_("You can't invite youself")) + end + end + + # Returns false. Adding friends by itself does not trigger e-mail + # sending. + def sends_email? + false + end + + def self.invite(person, contacts_to_invite, message, profile) + contacts_to_invite.each do |contact_to_invite| + next if contact_to_invite == _("Firstname Lastname ") + + contact_to_invite.strip! + if match = contact_to_invite.match(/(.*)<(.*)>/) and match[2].match(Noosfero::Constants::EMAIL_FORMAT) + friend_name = match[1].strip + friend_email = match[2] + elsif match = contact_to_invite.strip.match(Noosfero::Constants::EMAIL_FORMAT) + friend_name = "" + friend_email = match[0] + else + next + end + + user = User.find_by_email(friend_email) + + task_args = if user.nil? + {:person => person, :friend_name => friend_name, :friend_email => friend_email, :message => message} + elsif !user.person.is_a_friend?(person) + {:person => person, :target => user.person} + end + + if profile.person? + InviteFriend.create(task_args) + elsif profile.community? + InviteMember.create(task_args.merge(:community_id => profile.id)) + else + raise NotImplementedError, 'Don\'t know how to invite people to a %s' % profile.class.to_s + end + end + end + + def self.get_contacts(source, login, password) + contacts = [] + case source + when "gmail" + contacts = Contacts::Gmail.new(login, password).contacts + when "yahoo" + contacts = Contacts::Yahoo.new(login, password).contacts + when "hotmail" + contacts = Contacts::Hotmail.new(login, password).contacts + when "manual" + #do nothing + else + raise NotImplementedError, 'Unknown source to get contacts' + end + contacts.map { |contact| contact + ["#{contact[0]} <#{contact[1]}>"] } + end + + def self.join_contacts(manual_import_addresses, webmail_import_addresses) + contacts = [] + if manual_import_addresses + contacts += manual_import_addresses.is_a?(Array) ? manual_import_addresses : manual_import_addresses.split + end + if webmail_import_addresses + contacts += webmail_import_addresses.is_a?(Array) ? webmail_import_addresses : webmail_import_addresses.split + end + contacts + end + + def expanded_message + msg = message + msg = msg.gsub //, person.name + msg = msg.gsub //, friend_name.blank? ? friend_email : friend_name + msg = msg.gsub //, person.environment.name + msg + end + + def mail_template + raise 'You should implement mail_template in a subclass' + end + +end diff --git a/app/models/invite_friend.rb b/app/models/invite_friend.rb index 1042fcb..fa2f028 100644 --- a/app/models/invite_friend.rb +++ b/app/models/invite_friend.rb @@ -1,51 +1,28 @@ -class InviteFriend < Task +class InviteFriend < Invitation - acts_as_having_settings :group_for_person, :group_for_friend, :message, :friend_name, :friend_email, :field => :data - - validates_presence_of :requestor_id - - validates_presence_of :target_id, :if => Proc.new{|invite| invite.friend_email.blank? } - - validates_presence_of :friend_email, :if => Proc.new{|invite| invite.target_id.blank? } - validates_format_of :friend_email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => Proc.new{|invite| invite.target_id.blank? } - - validates_presence_of :message, :if => Proc.new{|invite| invite.target_id.blank? } - validates_format_of :message, :with => //, :if => Proc.new{|invite| invite.target_id.blank? } - - alias :person :requestor - alias :person= :requestor= - - alias :friend :target - alias :friend= :target= - - after_create do |task| - TaskMailer.deliver_invitation_notification(task) unless task.friend - end - - def validate - super - friendemail = friend ? friend.user.email : friend_email - if person && friendemail && person.user.email == friendemail - self.errors.add_to_base(_("You can't invite youself")) - end - end + settings_items :group_for_person, :group_for_friend def perform - requestor.add_friend(target, group_for_person) - target.add_friend(requestor, group_for_friend) - end - - # Returns false. Adding friends by itself does not trigger e-mail - # sending. - def sends_email? - false + person.add_friend(friend, group_for_person) + friend.add_friend(person, group_for_friend) end def description - _('%s wants to be your friend.') % [requestor.name] + _('%s wants to be your friend.') % [person.name] end def permission :manage_friends end + + # Default message send to friend when user use invite a friend feature + def self.mail_template + [ _('Hello ,'), + _(' is inviting you to participate on .'), + _('To accept the invitation, please follow this link:'), + '', + "--\n", + ].join("\n\n") + end + end diff --git a/app/models/invite_member.rb b/app/models/invite_member.rb new file mode 100644 index 0000000..5040963 --- /dev/null +++ b/app/models/invite_member.rb @@ -0,0 +1,36 @@ +class InviteMember < Invitation + + settings_items :community_id, :type => :integer + validates_presence_of :community_id + + def community + Community.find(community_id) + end + + def community=(newcommunity) + community_id = newcommunity.id + end + + def perform + community.add_member(friend) + end + + def description + _('%s invites you to join the community %s.') % [person.name, community.name] + end + + def expanded_message + super.gsub //, community.name + end + + # Default message send to friend when user use invite a friend feature + def self.mail_template + [ _('Hello ,'), + _(' is inviting you to participate of on .'), + _('To accept the invitation, please follow this link:'), + '', + "--\n", + ].join("\n\n") + end + +end diff --git a/app/models/profile.rb b/app/models/profile.rb index cc2b1e8..3e08680 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -43,6 +43,7 @@ class Profile < ActiveRecord::Base 'edit_appearance' => N_('Edit appearance'), 'view_private_content' => N_('View private content'), 'publish_content' => N_('Publish content'), + 'invite_members' => N_('Invite members'), } acts_as_accessible diff --git a/app/models/task_mailer.rb b/app/models/task_mailer.rb index ba8160b..f279bf2 100644 --- a/app/models/task_mailer.rb +++ b/app/models/task_mailer.rb @@ -27,10 +27,8 @@ class TaskMailer < ActionMailer::Base end def invitation_notification(task) - msg = task.message - msg = msg.gsub(//, task.requestor.name) - msg = msg.gsub(//, task.friend_name.blank? ? task.friend_email : task.friend_name) - msg = msg.gsub(//, generate_environment_url(task, :controller => 'account', :action => 'signup', :invitation_code => task.code)) + msg = task.expanded_message + msg = msg.gsub //, generate_environment_url(task, :controller => 'account', :action => 'signup', :invitation_code => task.code) recipients task.friend_email diff --git a/app/views/friends/index.rhtml b/app/views/friends/index.rhtml index 01169ba..86ce47b 100644 --- a/app/views/friends/index.rhtml +++ b/app/views/friends/index.rhtml @@ -14,7 +14,7 @@ <% button_bar do %> <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %> <%= button(:search, _('Find people'), :controller => 'search', :action => 'assets', :asset => 'people') %> - <%= button(:search, _('Invite people from my e-mail contacts'), :action => 'invite') %> + <%= button(:search, _('Invite people from my e-mail contacts'), :controller => 'invite', :action => 'friends') %> <% end %> <% end %> @@ -45,7 +45,7 @@ <% button_bar do %> <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %> <%= button(:search, _('Find people'), :controller => 'search', :action => 'assets', :asset => 'people') %> - <%= button(:search, _('Invite people from my e-mail contacts'), :action => 'invite') %> + <%= button(:search, _('Invite people from my e-mail contacts'), :controller => 'invite', :action => 'friends') %> <% end %> <% end %> diff --git a/app/views/friends/invite.rhtml b/app/views/friends/invite.rhtml deleted file mode 100644 index 9ff94c5..0000000 --- a/app/views/friends/invite.rhtml +++ /dev/null @@ -1,107 +0,0 @@ -<% if @wizard %> - <%= render :partial => 'account/wizard_steps'%> -<% end %> - -

<%= __('Invite your friends') %>

- -<% unless @friends %> - - <% if !@wizard %> -

<%= __('Step 1 of 1: Select address book') %>

- <% end %> - - <% form_tag do %> - <%= hidden_field_tag(:import, 1) %> - - <%= hidden_field_tag(:wizard, @wizard) %> - - <%= [ - radio_button_tag(:import_from, "manual", @import_from == "manual", :onclick => 'hide_invite_friend_login_password()') + content_tag('label', __('Manually (empty field)'), :for => "import_from_manual"), - radio_button_tag(:import_from, "gmail", @import_from == "gmail", :onclick => 'show_invite_friend_login_password()') + content_tag('label', 'Gmail', :for => 'import_from_gmail'), - radio_button_tag(:import_from, "yahoo", @import_from == "yahoo", :onclick => 'show_invite_friend_login_password()') + content_tag('label', 'Yahoo', :for => "import_from_yahoo"), - radio_button_tag(:import_from, "hotmail", @import_from == "hotmail", :onclick => 'show_invite_friend_login_password()') + content_tag('label', 'Hotmail', :for => "import_from_hotmail") - ].join("\n
\n") %> - - -
> - <%= labelled_form_field(__("Username") + ":", text_field_tag(:login, @login)) %> - <%= labelled_form_field(__("Password") + ":", password_field_tag(:password)) %> -
- - <% button_bar do %> - <%= submit_button(:forward, __("Next")) %> - <% end %> -

<%= __("We won't store your password or contact anyone without your permission.")%>

- - <% end %> - -<% else %> - - <% if !@wizard %> -

<%= __('Step 2 of 2: Selecting Friends') %>

- <% end %> -

- <%= __('Indicate which friends you want to invite.') %> -

- - <% if @wizard && @import_from == 'manual' %> -
- <%= __('Import now your contacts from %s, %s or %s') % [link_to_import('GMail', :import_from => 'gmail'), link_to_import('Yahoo', :import_from => 'yahoo'), link_to_import('Hotmail', :import_from => 'hotmail')] %> -
-
- <%= _('or') %> -
- <% end %> - - <% form_tag do %> - <%= hidden_field_tag(:confirmation, 1) %> - <%= hidden_field_tag(:import_from, @import_from) %> - <%= hidden_field_tag(:wizard, @wizard) %> - -
- <%= __('Enter one e-mail address per line:') %> - <%= text_area_tag(:manual_import_addresses, (@manual_import_addresses || ''), :cols => 72, :rows => 5) %> -
- <% if @import_from != 'manual' %> -
- <%= link_to_function __('Check all'), "$$('input.friend_to_invite').each(function(checkbox) { checkbox.checked = true; });" %> - <%= link_to_function __('Uncheck all'), "$$('input.friend_to_invite').each(function(checkbox) { checkbox.checked = false; });" %> - <% friend_pos = 0 %> -
- <% @friends.each do |friend| %> - <% friend_pos += 1 %> -

- <%= hidden_field_tag("webmail_friends[]", friend.to_yaml) %> - <%= check_box_tag("webmail_import_addresses[]", friend[2], (!@webmail_import_addresses || @webmail_import_addresses.include?(friend[2])), :id => "friends_to_invite_#{friend_pos}", :class => "friend_to_invite" ) %> -

- <% end %> -
-
- <% end -%> - -
- - <%= link_to_function(_('Personalize invitation message'), nil) do |page| - page['invitation-message'].show - end %> - - - - <% button_bar do %> - <%= submit_button(:ok, __("Invite my friends!")) %> - <% end %> - <% end %> - -<% end %> - diff --git a/app/views/invite/friends.rhtml b/app/views/invite/friends.rhtml new file mode 100644 index 0000000..0080bdb --- /dev/null +++ b/app/views/invite/friends.rhtml @@ -0,0 +1,93 @@ +<% if profile.person? %> +

<%= _('Invite your friends') %>

+<% else %> +

<%= _('Invite your friends to join %s') % profile.name %>

+<% end %> + +<% unless @contacts %> + +

<%= _('Step 1 of 2: Select address book') %>

+ + <% form_tag do %> + <%= hidden_field_tag(:step, 1) %> + + <%= [ + radio_button_tag(:import_from, "manual", @import_from == "manual", :onclick => 'hide_invite_friend_login_password()') + content_tag('label', _('Manually (empty field)'), :for => "import_from_manual"), + radio_button_tag(:import_from, "gmail", @import_from == "gmail", :onclick => 'show_invite_friend_login_password()') + content_tag('label', 'Gmail', :for => 'import_from_gmail'), + radio_button_tag(:import_from, "yahoo", @import_from == "yahoo", :onclick => 'show_invite_friend_login_password()') + content_tag('label', 'Yahoo', :for => "import_from_yahoo"), + radio_button_tag(:import_from, "hotmail", @import_from == "hotmail", :onclick => 'show_invite_friend_login_password()') + content_tag('label', 'Hotmail', :for => "import_from_hotmail") + ].join("\n
\n") %> + + +
> + <%= labelled_form_field(_("Username") + ":", text_field_tag(:login, @login)) %> + <%= labelled_form_field(_("Password") + ":", password_field_tag(:password)) %> +
+ + <% button_bar do %> + <%= submit_button(:forward, _("Next")) %> + <% end %> +

<%= _("We won't store your password or contact anyone without your permission.") %>

+ <% end %> + +<% else %> + +

<%= _('Step 2 of 2: Selecting Friends') %>

+

+ <%= _('Indicate which friends you want to invite.') %> +

+ + <% form_tag do %> + <%= hidden_field_tag(:step, 2) %> + <%= hidden_field_tag(:import_from, @import_from) %> + +
+ <%= labelled_form_field(_('Enter one e-mail address per line:'), text_area_tag(:manual_import_addresses, (@manual_import_addresses || ''), :cols => 72, :rows => 5)) %> +
+ <% if @import_from != 'manual' %> +
+ <%= link_to_function _('Check all'), "$$('input.contact_to_invite').each(function(checkbox) { checkbox.checked = true; });" %> + <%= link_to_function _('Uncheck all'), "$$('input.contact_to_invite').each(function(checkbox) { checkbox.checked = false; });" %> + <% friend_pos = 0 %> +
+ <% @contacts.each do |contact| %> + <% friend_pos += 1 %> +

+ <%= hidden_field_tag("webmail_friends[]", contact.to_yaml) %> + <%= check_box_tag("webmail_import_addresses[]", contact[2], + (!@webmail_import_addresses || @webmail_import_addresses.include?(contact[2])), + :id => "contacts_to_invite_#{friend_pos}", + :class => "contact_to_invite" ) + %> + +

+ <% end %> +
+
+ <% end -%> + +
+ + <%= link_to_function(_('Personalize invitation mail'), nil) do |page| + page['invitation-mail_template'].show + end %> + + + + <% button_bar do %> + <%= submit_button(:ok, _("Invite my friends!")) %> + <% end %> + <% end %> + +<% end %> diff --git a/app/views/profile/friends.rhtml b/app/views/profile/friends.rhtml index b0dbc66..b042e9a 100644 --- a/app/views/profile/friends.rhtml +++ b/app/views/profile/friends.rhtml @@ -16,11 +16,11 @@ <% end %> <% button_bar do %> + <%= button :back, _('Go back'), { :controller => 'profile' }, :help => _('Back to the page where you come from.') %> <% if user == profile %> <%= button :edit, _('Manage my friends'), :controller => 'friends', :action => 'index', :profile => profile.identifier %> + <%= button(:search, _('Invite people from my e-mail contacts'), :controller => 'invite', :action => 'friends') %> <% end %> - <%= button :back, _('Go back'), { :controller => 'profile' }, - :help => _('Back to the page where you come from.') %> <% end %> diff --git a/app/views/profile/members.rhtml b/app/views/profile/members.rhtml index c884c04..d6650b4 100644 --- a/app/views/profile/members.rhtml +++ b/app/views/profile/members.rhtml @@ -10,9 +10,10 @@ <% button_bar do %> - <%= button :back, _('Go back'), { :controller => 'profile' }, - :help => _('Back to the page where you come from.') %> + <%= button :back, _('Go back'), { :controller => 'profile' }, :help => _('Back to the page where you come from.') %> + <% if profile.community? and user and user.has_permission?(:invite_members, profile) %> + <%= button :search, _('Invite your friends to join %s') % profile.name, :controller => 'invite', :action => 'friends' %> + <% end %> <% end %> - diff --git a/app/views/profile_members/index.rhtml b/app/views/profile_members/index.rhtml index e01b3bd..a8e87fc 100644 --- a/app/views/profile_members/index.rhtml +++ b/app/views/profile_members/index.rhtml @@ -5,4 +5,7 @@ <% button_bar do %> <%= button :back, _('Back'), :controller => 'profile_editor' %> <%= button :add, _('Add members'), :action => 'add_members' if profile.enterprise? %> + <% if profile.community? and user.has_permission?(:invite_members, profile) %> + <%= button :search, _('Invite your friends to join %s') % profile.name, :controller => 'invite', :action => 'friends' %> + <% end %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index 24fa2e4..0f172e9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -58,13 +58,16 @@ ActionController::Routing::Routes.draw do |map| map.events 'profile/:profile/events/:year/:month', :controller => 'events', :action => 'events', :year => /\d*/, :month => /\d*/, :profile => /#{Noosfero.identifier_format}/ map.events 'profile/:profile/events', :controller => 'events', :action => 'events', :profile => /#{Noosfero.identifier_format}/ - # public profile information - map.profile 'profile/:profile/:action/:id', :controller => 'profile', :action => 'index', :id => /.*/, :profile => /#{Noosfero.identifier_format}/ - # catalog map.catalog 'catalog/:profile', :controller => 'catalog', :action => 'index', :profile => /#{Noosfero.identifier_format}/ map.product 'catalog/:profile/:id', :controller => 'catalog', :action => 'show', :profile => /#{Noosfero.identifier_format}/ + # invite + map.invite 'profile/:profile/invite/:action', :controller => 'invite', :profile => /#{Noosfero.identifier_format}/ + + # public profile information + map.profile 'profile/:profile/:action/:id', :controller => 'profile', :action => 'index', :id => /.*/, :profile => /#{Noosfero.identifier_format}/ + # contact map.contact 'contact/:profile/:action/:id', :controller => 'contact', :action => 'index', :id => /.*/, :profile => /#{Noosfero.identifier_format}/ diff --git a/db/migrate/082_add_invite_members_permission_to_admins.rb b/db/migrate/082_add_invite_members_permission_to_admins.rb new file mode 100644 index 0000000..070855a --- /dev/null +++ b/db/migrate/082_add_invite_members_permission_to_admins.rb @@ -0,0 +1,17 @@ +class AddInviteMembersPermissionToAdmins < ActiveRecord::Migration + def self.up + Environment.all.each{ |env| + admin = Profile::Roles.admin(env.id) + admin.permissions += ['invite_members'] + admin.save! + } + end + + def self.down + Environment.all.each{ |env| + admin = Profile::Roles.admin(env.id) + admin.permissions -= ['invite_members'] + admin.save! + } + end +end diff --git a/db/schema.rb b/db/schema.rb index 0e4c968..c208202 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -9,7 +9,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 81) do +ActiveRecord::Schema.define(:version => 82) do create_table "article_versions", :force => true do |t| t.integer "article_id" diff --git a/features/invitation.feature b/features/invitation.feature new file mode 100644 index 0000000..e1afedc --- /dev/null +++ b/features/invitation.feature @@ -0,0 +1,85 @@ +Feature: invitation + As a noosfero visitor + I want to invite my friends to Noosfero + + Background: + Given the following users + | login | + | josesilva | + | josesantos | + And the following communities + | owner | identifier | name | + | josesilva | 26-bsslines | 26 Bsslines | + And the following enterprises + | owner | identifier | name | + | josesilva | beatles-for-sale | Beatles For Sale | + And I am logged in as "josesilva" + + Scenario: see link to invite friends + When I am on /profile/josesilva/friends + Then I should see "Invite people from my e-mail contacts" link + + Scenario: see link to invite friends in myprofile + When I am on /myprofile/josesilva/friends + Then I should see "Invite people from my e-mail contacts" link + + Scenario: go to invitation screen when follow link to invite friends + Given I am on /myprofile/josesilva/friends + When I follow "Invite people from my e-mail contacts" + Then I am on /profile/josesilva/invite/friends + + Scenario: see title when invite friends + When I am on /profile/josesilva/invite/friends + Then I should see "Invite your friends" + + # why not work? + Scenario: back to manage friends after invite friends + Given I am on /myprofile/josesilva/friends + And I follow "Invite people from my e-mail contacts" + And I press "Next" + And I fill in "manual_import_addresses" with "misfits@devil.doll" + And I fill in "mail_template" with "Follow this link " + When I press "Invite my friends!" + Then I should be on /myprofile/josesilva/friends + + Scenario: see link to invite members to community + When I am on /profile/26-bsslines/members + Then I should see "Invite your friends to join 26 Bsslines" link + + Scenario: not see link to invite members to community if has no rights + Given I am not logged in + And I am logged in as "josesantos" + When I am on /profile/26-bsslines/members + Then I should not see "Invite your friends to join 26 Bsslines" link + + Scenario: go to invitation screen when follow link to invite members + Given I am on /profile/26-bsslines/members + When I follow "Invite your friends to join 26 Bsslines" + Then I am on /profile/26-bsslines/invite/friends + + Scenario: see title when invite members + When I am on /profile/26-bsslines/invite/friends + Then I should see "Invite your friends to join 26 Bsslines" + + Scenario: not see link to invite members to enterprise + When I am on /profile/beatles-for-sale/members + Then I should not see "Invite your friends to join Beatles For Sale" link + + Scenario: deny access if user has no right to invite members + Given I am not logged in + And I am logged in as "josesantos" + When I am on /profile/26-bsslines/invite/friends + Then I should see "Access denied" + + Scenario: not see link to invite members to enterprise in manage members + Given I am on /myprofile/beatles-for-sale/profile_members + Then I should not see "Invite your friends to join Beatles For Sale" link + + Scenario: back to manage members after invite friends + Given I am on /myprofile/26-bsslines/profile_members + And I follow "Invite your friends to join 26 Bsslines" + And I press "Next" + And I fill in "manual_import_addresses" with "misfits@devil.doll" + And I fill in "mail_template" with "Follow this link " + When I press "Invite my friends!" + Then I should be on /myprofile/26-bsslines/profile_members diff --git a/public/stylesheets/controller_friends.css b/public/stylesheets/controller_friends.css index 2e940e4..c2e411d 100644 --- a/public/stylesheets/controller_friends.css +++ b/public/stylesheets/controller_friends.css @@ -12,13 +12,6 @@ padding-top: 20px; } -#friends-list { - overflow: auto; - height: 300px; - border: 1px solid #999; - margin: 5px 0px; -} - #pagination-friends .pagination span { display: inline; } diff --git a/public/stylesheets/controller_invite.css b/public/stylesheets/controller_invite.css new file mode 100644 index 0000000..960064f --- /dev/null +++ b/public/stylesheets/controller_invite.css @@ -0,0 +1,6 @@ +#contacts-list { + overflow: auto; + height: 300px; + border: 1px solid #999; + margin: 5px 0px; +} diff --git a/test/fixtures/roles.yml b/test/fixtures/roles.yml index 9dc95c4..6da2688 100644 --- a/test/fixtures/roles.yml +++ b/test/fixtures/roles.yml @@ -40,6 +40,8 @@ profile_admin: name: 'Profile Administrator' system: true permissions: + - invite_members + - manage_memberships - edit_profile_design - edit_profile - moderate_comments diff --git a/test/functional/friends_controller_test.rb b/test/functional/friends_controller_test.rb index 20988c4..c940893 100644 --- a/test/functional/friends_controller_test.rb +++ b/test/functional/friends_controller_test.rb @@ -75,93 +75,4 @@ class FriendsControllerTest < Test::Unit::TestCase assert_tag :tag => 'a', :content => 'Find people', :attributes => { :href => '/assets/people' } end - should 'display invitation page' do - get :invite - assert_response :success - assert_template 'invite' - end - - should 'actualy invite manually added addresses with name and e-mail' do - assert_difference InviteFriend, :count, 1 do - post :invite, :manual_import_addresses => "Test Name ", :import_from => "manual", :message => "click: ", :confirmation => 1 - assert_redirected_to :action => 'index' - end - end - - should 'actualy invite manually added addresses with name and e-mail on wizard' do - assert_difference InviteFriend, :count, 1 do - post :invite, :manual_import_addresses => "Test Name ", :import_from => "manual", :message => "click: ", :confirmation => 1, :wizard => true - assert_redirected_to :action => 'invite', :wizard => true - end - end - - should 'actually invite manually added address with only e-mail' do - assert_difference InviteFriend, :count, 1 do - post :invite, :manual_import_addresses => "test@test.com", :import_from => "manual", :message => "click: ", :confirmation => 1 - assert_redirected_to :action => 'index' - end - end - - should 'actually invite manually added address with only e-mail on wizard' do - assert_difference InviteFriend, :count, 1 do - post :invite, :manual_import_addresses => "test@test.com", :import_from => "manual", :message => "click: ", :confirmation => 1, :wizard => true - assert_redirected_to :action => 'invite', :wizard => true - end - end - - should 'actually invite manually added addresses with e-mail and other format' do - assert_difference InviteFriend, :count, 1 do - post :invite, :manual_import_addresses => "test@test.cz.com", :import_from => "manual", :message => "click: ", :confirmation => 1 - assert_redirected_to :action => 'index' - end - end - - should 'actually invite manually added addresses with e-mail and other format on wizard' do - assert_difference InviteFriend, :count, 1 do - post :invite, :manual_import_addresses => "test@test.cz.com", :import_from => "manual", :message => "click: ", :confirmation => 1, :wizard => true - assert_redirected_to :action => 'invite', :wizard => true - end - end - - should 'actually invite manually added address with friend object' do - assert_difference InviteFriend, :count, 1 do - post :invite, :manual_import_addresses => "#{friend.name} <#{friend.email}>", :import_from => "manual", :message => "click: ", :confirmation => 1 - assert_redirected_to :action => 'index' - end - end - - should 'actually invite manually added address with friend object on wizard' do - assert_difference InviteFriend, :count, 1 do - post :invite, :manual_import_addresses => "#{friend.name} <#{friend.email}>", :import_from => "manual", :message => "click: ", :confirmation => 1, :wizard => true - assert_redirected_to :action => 'invite', :wizard => true - end - end - - should 'actually invite more than one manually added addres' do - assert_difference InviteFriend, :count, 2 do - post :invite, :manual_import_addresses => "Some Friend \r\notherperson@bleble.net\r\n", :import_from => "manual", :message => "click: ", :confirmation => 1 - assert_redirected_to :action => 'index' - end - end - - should 'actually invite more than one manually added addres on wizard' do - assert_difference InviteFriend, :count, 2 do - post :invite, :manual_import_addresses => "Some Friend \r\notherperson@bleble.net\r\n", :import_from => "manual", :message => "click: ", :confirmation => 1, :wizard => true - assert_redirected_to :action => 'invite', :wizard => true - end - end - - should 'not invite yourself' do - assert_no_difference InviteFriend, :count do - post :invite, :manual_import_addresses => "#{profile.name} <#{profile.user.email}>", :import_from => "manual", :message => "click: ", :confirmation => 1, :wizard => true - end - end - - should 'not create InviteFriend if is a friend' do - friend = create_user('testfriend', :email => 'friend@noosfero.org') - friend.person.add_friend(profile) - assert_no_difference InviteFriend, :count do - post :invite, :manual_import_addresses => "#{friend.name} <#{friend.email}>", :import_from => "manual", :message => "click: ", :confirmation => 1, :wizard => true - end - end end diff --git a/test/functional/invite_controller_test.rb b/test/functional/invite_controller_test.rb new file mode 100644 index 0000000..dc99e7f --- /dev/null +++ b/test/functional/invite_controller_test.rb @@ -0,0 +1,86 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class InviteControllerTest < ActionController::TestCase + + def setup + @profile = create_user('testuser').person + @friend = create_user('thefriend').person + @community = fast_create(Community) + login_as ('testuser') + end + attr_accessor :profile, :friend, :community + + should 'actually invite manually added address with friend object' do + assert_difference InviteFriend, :count, 1 do + post :friends, :profile => profile.identifier, :manual_import_addresses => "#{friend.name} <#{friend.email}>", :import_from => "manual", :mail_template => "click: ", :step => 2 + assert_redirected_to :controller => 'profile' + end + end + + should 'actually invite manually added address with only e-mail' do + assert_difference InviteFriend, :count, 1 do + post :friends, :profile => profile.identifier, :manual_import_addresses => "test@test.com", :import_from => "manual", :mail_template => "click: ", :step => 2 + end + end + + should 'actually invite manually added addresses with e-mail and other format' do + assert_difference InviteFriend, :count, 1 do + post :friends, :profile => profile.identifier, :manual_import_addresses => "test@test.cz.com", :import_from => "manual", :mail_template => "click: ", :step => 2 + end + end + + should 'actually invite more than one manually added address' do + assert_difference InviteFriend, :count, 2 do + post :friends, :profile => profile.identifier, :manual_import_addresses => "Some Friend \r\notherperson@bleble.net\r\n", :import_from => "manual", :mail_template => "click: ", :step => 2 + end + end + + should 'actualy invite manually added addresses with name and e-mail' do + assert_difference InviteFriend, :count, 1 do + post :friends, :profile => profile.identifier, :manual_import_addresses => "Test Name ", :import_from => "manual", :mail_template => "click: ", :step => 2 + end + end + + should 'not invite yourself' do + assert_no_difference InviteFriend, :count do + post :friends, :profile => profile.identifier, :manual_import_addresses => "#{profile.name} <#{profile.user.email}>", :import_from => "manual", :mail_template => "click: ", :step => 2 + end + end + + should 'not invite if already a friend' do + friend = create_user('testfriend', :email => 'friend@noosfero.org') + friend.person.add_friend(profile) + assert_no_difference InviteFriend, :count do + post :friends, :profile => profile.identifier, :manual_import_addresses => "#{friend.name} <#{friend.email}>", :import_from => "manual", :mail_template => "click: ", :step => 2 + end + end + + should 'display invitation page' do + get :friends, :profile => profile.identifier + assert_response :success + assert_tag :tag => 'h1', :content => 'Invite your friends' + end + + should 'get mail template to invite members' do + community.add_admin(profile) + get :friends, :profile => community.identifier + assert_equal InviteMember.mail_template, assigns(:mail_template) + end + + should 'get mail template to invite friends' do + community.add_admin(profile) + get :friends, :profile => profile.identifier + assert_equal InviteFriend.mail_template, assigns(:mail_template) + end + + should 'deny if user has no rights to invite members' do + get :friends, :profile => community.identifier + assert_response 403 # forbidden + end + + should 'deny access when trying to invite friends to another user' do + get :friends, :profile => friend.identifier + assert_response 403 # forbidden + end + +end diff --git a/test/unit/environment_test.rb b/test/unit/environment_test.rb index 05bdbfa..a6f39fd 100644 --- a/test/unit/environment_test.rb +++ b/test/unit/environment_test.rb @@ -587,19 +587,32 @@ class EnvironmentTest < Test::Unit::TestCase assert_equal ['birth_date'], env.required_person_fields end - should 'provide a default invitation message' do - env = Environment.new + should 'provide a default invitation message for friend' do + env = Environment.default message = [ 'Hello ,', - " is inviting you to participate on #{env.name}.", - 'To accept the invitation, please follow this link:' + "\n" + '', - "--\n#{env.name}", - '' + " is inviting you to participate on .", + 'To accept the invitation, please follow this link:', + '', + "--\n", ].join("\n\n") assert_equal message, env.message_for_friend_invitation end + should 'provide a default invitation message for member' do + env = Environment.default + message = [ + 'Hello ,', + " is inviting you to participate of on .", + 'To accept the invitation, please follow this link:', + '', + "--\n", + ].join("\n\n") + + assert_equal message, env.message_for_member_invitation + end + should 'set custom_enterprises_fields' do env = Environment.new env.custom_enterprise_fields = {'contact_person' => {'required' => 'true', 'active' => 'true'},'contact_email'=> {'required' => 'true', 'active' => 'true'}} @@ -841,4 +854,18 @@ class EnvironmentTest < Test::Unit::TestCase assert_equal [], Environment.new.local_docs end + should 'provide right invitation mail template for friends' do + env = Environment.default + person = Person.new + + assert_equal env.message_for_friend_invitation, env.invitation_mail_template(person) + end + + should 'provide right invitation mail template for members' do + env = Environment.default + community = Community.new + + assert_equal env.message_for_member_invitation, env.invitation_mail_template(community) + end + end diff --git a/test/unit/invitation_test.rb b/test/unit/invitation_test.rb new file mode 100644 index 0000000..0f35ebd --- /dev/null +++ b/test/unit/invitation_test.rb @@ -0,0 +1,62 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class InvitationTest < ActiveSupport::TestCase + + should 'expand message' do + invitation = Invitation.new( + :person => Person.new(:name => 'Sadam', :environment => Environment.new(:name => 'AnarquiaOi')), + :friend_name => 'Fernandinho', + :message => 'Hi , is inviting you to !' + ) + assert_equal 'Hi Fernandinho, Sadam is inviting you to AnarquiaOi!', invitation.expanded_message + end + + should 'require subclasses implement mail_template method' do + assert_raise RuntimeError do + Invitation.new.mail_template + end + end + + should 'join string contacts with array contacts' do + string_contacts = "sadam@garotos\nfernandinho@garotos\roi@garotos" + array_contacts = ['casiotone@gengivas.negras'] + + assert_equal ['sadam@garotos', 'fernandinho@garotos', 'oi@garotos', 'casiotone@gengivas.negras'], + Invitation.join_contacts(string_contacts, array_contacts) + end + + should 'raises when try get contacts from unknown source' do + assert_raise NotImplementedError do + Invitation.get_contacts('ze', 'ze12', 'bli-mail') + end + end + + should 'not know how to invite members to non-community' do + person = fast_create(Person) + enterprise = fast_create(Enterprise) + + assert_raise NotImplementedError do + Invitation.invite(person, ['sadam@garotos.podres'], 'hello friend', enterprise) + end + end + + should 'create right task when invite friends' do + person = fast_create(Person) + person.user = User.new(:email => 'current_user@email.invalid') + + assert_difference InviteFriend, :count do + Invitation.invite(person, ['sadam@garotos.podres'], 'hello friend ', person) + end + end + + should 'create right task when invite members to community' do + person = fast_create(Person) + person.user = User.new(:email => 'current_user@email.invalid') + community = fast_create(Community) + + assert_difference InviteMember, :count do + Invitation.invite(person, ['sadam@garotos.podres'], 'hello friend ', community) + end + end + +end diff --git a/test/unit/invite_member_test.rb b/test/unit/invite_member_test.rb new file mode 100644 index 0000000..284fb87 --- /dev/null +++ b/test/unit/invite_member_test.rb @@ -0,0 +1,112 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class InviteMemberTest < ActiveSupport::TestCase + + should 'be a task' do + ok { InviteMember.new.kind_of?(Task) } + end + + should 'actually add as member when confirmed' do + person = fast_create(Person) + friend = fast_create(Person) + friend.stubs(:user).returns(User.new(:email => 'garotos@podres.punk.oi')) + person.stubs(:user).returns(User.new(:email => 'suburbio-operario@podres.podres')) + community = fast_create(Community) + + assert_equal [], community.members + + task = InviteMember.create!(:person => person, :friend => friend, :community_id => community.id) + task.finish + community.reload + + ok('friend is member of community') { community.members.include?(friend) } + end + + should 'require community (person inviting other to be a member)' do + task = InviteMember.new + task.valid? + + ok('community is required') { task.errors.invalid?(:community_id) } + end + + should 'require friend email if no target given (person being invited)' do + task = InviteMember.new + task.valid? + + ok('friend_email is required') { task.errors.invalid?(:friend_email) } + end + + should 'dont require friend email if target given (person being invited)' do + task = InviteMember.new(:target => create_user('testuser2').person) + task.valid? + + ok('friend_email isnt required') { !task.errors.invalid?(:friend_email) } + end + + should 'require target (person being invited) if no friend email given' do + task = InviteMember.new + task.valid? + + ok('target is required') { task.errors.invalid?(:target_id) } + end + + should 'dont require target (person being invited) if friend email given' do + task = InviteMember.new(:friend_email => "test@test.com") + task.valid? + + ok('target isn required') { !task.errors.invalid?(:target_id) } + end + + should 'not send e-mails to requestor' do + p1 = create_user('testuser1').person + p2 = create_user('testuser2').person + + TaskMailer.expects(:deliver_task_finished).never + TaskMailer.expects(:deliver_task_created).never + + task = InviteMember.create!(:person => p1, :friend => p2, :community_id => fast_create(Community).id) + task.finish + end + + should 'send e-mails to friend if friend_email given' do + p1 = create_user('testuser1').person + + TaskMailer.expects(:deliver_invitation_notification).once + + task = InviteMember.create!(:person => p1, :friend_email => 'test@test.com', :message => '', :community_id => fast_create(Community).id) + end + + should 'not send e-mails to friend if target given (person being invited)' do + p1 = create_user('testuser1').person + p2 = create_user('testuser2').person + + TaskMailer.expects(:deliver_invitation_notification).never + + task = InviteMember.create!(:person => p1, :friend => p2, :community_id => fast_create(Community).id) + end + + should 'provide proper description' do + p1 = create_user('testuser1').person + p2 = create_user('testuser2').person + + TaskMailer.expects(:deliver_task_finished).never + TaskMailer.expects(:deliver_task_created).never + + community = fast_create(Community) + task = InviteMember.create!(:person => p1, :friend => p2, :community_id => community.id) + + assert_equal "#{p1.name} invites you to join the community #{community.name}.", task.description + end + + should 'not invite yourself' do + p = create_user('testuser1').person + + task1 = InviteMember.new(:person => p, :friend => p, :message => 'click here: ') + assert !task1.save + + task2 = InviteMember.new(:person => p, :friend_name => 'Myself', :friend_email => p.user.email, :message => 'click here: ') + assert !task2.save + end + +end + diff --git a/test/unit/task_mailer_test.rb b/test/unit/task_mailer_test.rb index 5254f0a..568f9f7 100644 --- a/test/unit/task_mailer_test.rb +++ b/test/unit/task_mailer_test.rb @@ -124,6 +124,7 @@ class TaskMailerTest < Test::Unit::TestCase environment.expects(:name).returns('example').at_least_once task.expects(:requestor).returns(requestor).at_least_once + task.expects(:person).returns(requestor).at_least_once requestor.expects(:environment).returns(environment).at_least_once mail = TaskMailer.create_invitation_notification(task) -- libgit2 0.21.2