Commit f3f04a53c45d39468b43272d6da7b2666e12935f

Authored by Joenio Costa
Committed by Antonio Terceiro
1 parent 3d351025

ActionItem791: re-implement contact-us without tasks

  - add fields city and state to contact form
  - aceess_control plugin: filter members by role
  - stay on contact form after send message
  - validates format of email only if filled
  - remove phone and add name and fill fields if user logged in
  - mark contact fields to translation
app/controllers/public/contact_controller.rb
... ... @@ -3,14 +3,24 @@ class ContactController < PublicController
3 3 needs_profile
4 4  
5 5 def new
6   - @contact = Contact.new(params[:contact])
  6 + @contact
7 7 if request.post?
8   - if @contact.save
  8 + @contact = Contact.new(params[:contact])
  9 + @contact.dest = profile
  10 + @contact.city = City.exists?(params[:city]) ? City.find(params[:city]).name : _('Missing')
  11 + @contact.state = State.exists?(params[:state]) ? State.find(params[:state]).name : _('Missing')
  12 + if @contact.deliver
9 13 flash[:notice] = _('Contact successfully sent')
10   - redirect_to :controller => 'profile', :profile => profile.identifier
  14 + redirect_to :action => 'new'
11 15 else
12 16 flash[:notice] = _('Contact not sent')
13 17 end
  18 + else
  19 + if logged_in?
  20 + @contact = Contact.new(:name => user.name, :email => user.email)
  21 + else
  22 + @contact = Contact.new
  23 + end
14 24 end
15 25 end
16 26  
... ...
app/models/contact.rb
1   -class Contact < Task
  1 +class Contact < ActiveRecord::Base #WithoutTable
  2 + tableless :columns => [
  3 + [:name, :string],
  4 + [:subject, :string],
  5 + [:message, :string],
  6 + [:email, :string],
  7 + [:state, :string],
  8 + [:city, :string]
  9 + ]
  10 + attr_accessor :dest
2 11  
3   - validates_presence_of :target_id, :subject, :email, :message
4   - validates_format_of :email, :with => Noosfero::Constants::EMAIL_FORMAT
  12 + N_('Subject'); N_('Message'); N_('City and state'); N_('e-Mail'); N_('Name')
5 13  
6   - acts_as_having_settings :field => :data
7   - settings_items :subject, :message, :city_and_state, :email, :phone
  14 + validates_presence_of :subject, :email, :message, :name
  15 + validates_format_of :email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda {|o| !o.email.blank?})
8 16  
9   - def description
10   - _('%s sent a new message') % (requestor ? requestor.name : _('Someone'))
  17 + def deliver
  18 + Contact::Sender.deliver_mail(self)
  19 + end
  20 +
  21 + class Sender < ActionMailer::Base
  22 + def mail(contact)
  23 + emails = [contact.dest.contact_email] + contact.dest.admins.map{|i| i.email}
  24 + recipients emails
  25 + from "#{contact.name} <#{contact.email}>"
  26 + subject contact.subject
  27 + body :name => contact.name,
  28 + :email => contact.email,
  29 + :city => contact.city,
  30 + :state => contact.state,
  31 + :message => contact.message,
  32 + :environment => contact.dest.environment.name,
  33 + :url => url_for(:host => contact.dest.environment.default_hostname, :controller => 'home')
  34 + end
11 35 end
12 36  
13 37 end
... ...
app/models/profile.rb
... ... @@ -514,4 +514,8 @@ class Profile &lt; ActiveRecord::Base
514 514 !self.articles.count(:conditions => {:type => 'Blog'}).zero?
515 515 end
516 516  
  517 + def admins
  518 + self.members_by_role(Profile::Roles.admin)
  519 + end
  520 +
517 521 end
... ...
app/views/contact/new.rhtml
... ... @@ -4,13 +4,11 @@
4 4  
5 5 <% labelled_form_for :contact, @contact do |f| %>
6 6  
7   - <%= hidden_field_tag 'contact[target_id]', profile.id %>
8   - <%= hidden_field_tag 'contact[requestor_id]', (logged_in? ? current_user.person.id : nil) %>
9   - <%= required f.text_field(:subject) %>
10   - <%= required f.text_field(:email) %>
11   - <%= f.text_field :city_and_state %>
12   - <%= f.text_field :phone %>
13   - <%= required f.text_area(:message, :rows => 5) %>
  7 + <%= f.text_field :name %>
  8 + <%= f.text_field :email %>
  9 + <%= labelled_form_field _('City and state'), select_city(true) %>
  10 + <%= f.text_field :subject %>
  11 + <%= f.text_area :message, :rows => 10, :cols => 60 %>
14 12  
15 13 <%= submit_button(:send, _('Send')) %>
16 14  
... ...
app/views/contact/sender/mail.rhtml 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +<%= _('Name: %s') % @name %>
  2 +<%= _('e-Mail: %s') % @email %>
  3 +<%= _('City and state: %s-%s') % [@city, @state] %>
  4 +
  5 +<%= _('Message:') %>
  6 +--
  7 +
  8 +<%= @message %>
  9 +
  10 +--
  11 +<%= _('%s environment system') % @environment %>
  12 +<%= @url %>
... ...
app/views/tasks/_contact.rhtml
... ... @@ -1,18 +0,0 @@
1   -<h2><%= task.description %></h2>
2   -
3   -<% form_for('task', task, :url => { :action => 'close', :id => task.id}) do |f| %>
4   -
5   - <%= hidden_field_tag(:decision, :finish) %>
6   - <table>
7   - <% %w[ subject email phone city_and_state message ].each do |field| %>
8   - <% if task.respond_to?(field) and !task.send(field).nil? and !task.send(field).empty? %>
9   - <tr> <td><strong><%= _(field.humanize) %></strong></td> <td><%= task.send(field) %></td> </tr>
10   - <% end %>
11   - <% end %>
12   - </table>
13   -
14   - <% button_bar do %>
15   - <%= submit_button(:ok, _('Ok!')) %>
16   - <% end %>
17   -
18   -<% end %>
public/stylesheets/controller_contact.css 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +/*** SELECT CITY ***/
  2 +
  3 +#content .select_state_for_origin,
  4 +#content .select_city_for_origin {
  5 + display: inline;
  6 +}
... ...
test/functional/contact_controller_test.rb
... ... @@ -43,33 +43,30 @@ class ContactControllerTest &lt; Test::Unit::TestCase
43 43 assert_tag :tag => 'textarea', :attributes => { :name => 'contact[message]' }
44 44 end
45 45  
46   - should 'add hidden field with target_id' do
47   - get :new, :profile => enterprise.identifier
48   - assert_tag :tag => 'input', :attributes => { :name => 'contact[target_id]', :value => enterprise.id, :type => 'hidden' }
  46 + should 'redirect back to contact page after send contact' do
  47 + post :new, :profile => enterprise.identifier, :contact => {:name => 'john', :subject => 'Hi', :email => 'visitor@mail.invalid', :message => 'Hi, all'}
  48 + assert_response :redirect
  49 + assert_redirected_to :action => 'new'
49 50 end
50 51  
51   - should 'add requestor id if logged in' do
  52 + should 'fill email if user logged in' do
52 53 login_as(profile.identifier)
53   - @controller.stubs(:current_user).returns(profile.user)
54 54 get :new, :profile => enterprise.identifier
55   - assert_tag :tag => 'input', :attributes => { :name => 'contact[requestor_id]', :value => profile.id }
  55 + assert_tag :tag => 'input', :attributes => {:name => 'contact[email]', :value => profile.email}
56 56 end
57 57  
58   - should 'nil requestor id if not logged in' do
  58 + should 'fill name if user logged in' do
  59 + login_as(profile.identifier)
59 60 get :new, :profile => enterprise.identifier
60   - assert_tag :tag => 'input', :attributes => { :name => 'contact[requestor_id]', :value => nil }
61   - end
62   -
63   - should 'redirect to profile page after contact' do
64   - post :new, :profile => enterprise.identifier, :contact => {:subject => 'Hi', :email => 'visitor@mail.invalid', :message => 'Hi, all', :target_id => enterprise.id}
65   - assert_response :redirect
66   - assert_redirected_to :controller => 'profile', :profile => enterprise.identifier
  61 + assert_tag :tag => 'input', :attributes => {:name => 'contact[name]', :value => profile.name}
67 62 end
68 63  
69   - should 'be able to send contact' do
70   - assert_difference Contact, :count do
71   - post :new, :profile => enterprise.identifier, :contact => {:subject => 'Hi', :email => 'visitor@mail.invalid', :message => 'Hi, all', :target_id => enterprise.id}
72   - end
  64 + should 'define city and state' do
  65 + City.stubs(:find).returns(City.new(:name => 'Camaçari'))
  66 + State.stubs(:find).returns(State.new(:name => 'Bahia'))
  67 + post :new, :profile => enterprise.identifier, :contact => {:name => 'john', :subject => 'Hi', :email => 'visitor@mail.invalid', :message => 'Hi, all', :state => 1, :city => 1}
  68 + assert_equal 'Camaçari', assigns(:contact).city
  69 + assert_equal 'Bahia', assigns(:contact).state
73 70 end
74 71  
75 72 end
... ...
test/functional/tasks_controller_test.rb
... ... @@ -152,15 +152,4 @@ class TasksControllerTest &lt; Test::Unit::TestCase
152 152 assert_equal f, assigns(:ticket).target
153 153 end
154 154  
155   - should 'list enterprise contacts' do
156   - ent = Enterprise.create!(:identifier => 'contact_test_enterprise', :name => 'Test contact enteprise')
157   - task = Contact.create!(:subject => 'test', :target_id => profile.id, :email => 'visitor@invalid.com', :message => 'Hi, all')
158   -
159   - login_as(profile.identifier)
160   - get :index, :profile => ent.identifier
161   -
162   - assert_includes assigns(:tasks), task
163   - assert_tag :tag => 'li', :attributes => { :class => 'task-Contact' }, :content => 'Someone sent a new message'
164   - end
165   -
166 155 end
... ...
test/unit/contact_sender_test.rb 0 → 100644
... ... @@ -0,0 +1,54 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class ContactSenderTest < Test::Unit::TestCase
  4 + FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures'
  5 + CHARSET = "utf-8"
  6 +
  7 + def setup
  8 + ActionMailer::Base.delivery_method = :test
  9 + ActionMailer::Base.perform_deliveries = true
  10 + ActionMailer::Base.deliveries = []
  11 +
  12 + @expected = TMail::Mail.new
  13 + @expected.set_content_type "text", "plain", { "charset" => CHARSET }
  14 + end
  15 +
  16 + should 'be able to deliver mail' do
  17 + ent = Enterprise.new(:name => 'my enterprise', :identifier => 'myent', :environment => Environment.default)
  18 + ent.contact_email = 'contact@invalid.com'
  19 + c = Contact.new(:dest => ent, :subject => 'hi molly')
  20 + response = Contact::Sender.deliver_mail(c)
  21 + assert_equal c.email, response.from
  22 + assert_equal c.subject, response.subject
  23 + end
  24 +
  25 + should 'deliver mail to contact_email' do
  26 + ent = Enterprise.new(:name => 'my enterprise', :identifier => 'myent', :environment => Environment.default)
  27 + ent.contact_email = 'contact@invalid.com'
  28 + c = Contact.new(:dest => ent)
  29 + response = Contact::Sender.deliver_mail(c)
  30 + assert_includes response.to, c.dest.contact_email
  31 + end
  32 +
  33 + should 'deliver mail to admins of enterprise' do
  34 + admin = create_user('admin_test').person
  35 + ent = Enterprise.create!(:name => 'my enterprise', :identifier => 'myent', :environment => Environment.default)
  36 + ent.contact_email = 'contact@invalid.com'
  37 + ent.add_admin(admin)
  38 + assert ent.save!
  39 + c = Contact.new(:dest => ent)
  40 + response = Contact::Sender.deliver_mail(c)
  41 + assert_includes response.to, admin.email
  42 + end
  43 +
  44 + private
  45 +
  46 + def read_fixture(action)
  47 + IO.readlines("#{FIXTURES_PATH}/mail_sender/#{action}")
  48 + end
  49 +
  50 + def encode(subject)
  51 + quoted_printable(subject, CHARSET)
  52 + end
  53 +
  54 +end
... ...
test/unit/contact_test.rb
... ... @@ -2,24 +2,39 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
2 2  
3 3 class ContactTest < ActiveSupport::TestCase
4 4  
5   - should 'have serialized data' do
6   - t = Contact.new
7   - t.data[:test] = 'test'
8   -
9   - assert_equal({:test => 'test'}, t.data)
10   - end
11   -
12 5 should 'validates required fields' do
13 6 contact = Contact.new
14 7 assert !contact.valid?
  8 +
15 9 contact.subject = 'Hi'
16 10 assert !contact.valid?
  11 +
  12 + contact.name = 'john'
  13 + assert !contact.valid?
  14 +
17 15 contact.email = 'visitor@invalid.com'
18 16 assert !contact.valid?
  17 +
19 18 contact.message = 'Hi, all'
20   - assert !contact.valid?
21   - contact.target = create_user('contact_user_test').person
22   - assert contact.save!
  19 + assert contact.valid?
  20 + end
  21 +
  22 + should 'validates format of email only if not empty' do
  23 + contact = Contact.new
  24 + contact.valid?
  25 + assert_equal "Email can't be blank", contact.errors[:email]
  26 + end
  27 +
  28 + should 'inicialize fields on instanciate' do
  29 + assert_nothing_raised ArgumentError do
  30 + Contact.new(:name => 'john', :email => 'contact@invalid.com')
  31 + end
  32 + end
  33 +
  34 + should 'deliver message' do
  35 + ent = Enterprise.create!(:name => 'my enterprise', :identifier => 'myent', :environment => Environment.default)
  36 + c = Contact.new(:name => 'john', :email => 'john@invalid.com', :subject => 'hi', :message => 'hi, all', :dest => ent)
  37 + assert c.deliver
23 38 end
24 39  
25 40 end
... ...
test/unit/profile_test.rb
... ... @@ -1112,6 +1112,14 @@ class ProfileTest &lt; Test::Unit::TestCase
1112 1112 assert_nil p.blog
1113 1113 end
1114 1114  
  1115 + should 'list admins' do
  1116 + c = Profile.create!(:name => 'my test profile', :identifier => 'mytestprofile')
  1117 + p = create_user('mytestuser').person
  1118 + c.add_admin(p)
  1119 +
  1120 + assert_equal [p], c.admins
  1121 + end
  1122 +
1115 1123 private
1116 1124  
1117 1125 def assert_invalid_identifier(id)
... ...
vendor/plugins/access_control/lib/acts_as_accessible.rb
... ... @@ -27,6 +27,9 @@ class ActiveRecord::Base
27 27 def members
28 28 role_assignments.map(&:accessor).uniq
29 29 end
  30 + def members_by_role(role)
  31 + role_assignments.select{|i| i.role.key == role.key}.map(&:accessor).uniq
  32 + end
30 33  
31 34 def roles
32 35 Role.find(:all).select do |r|
... ...
vendor/plugins/access_control/test/acts_as_accessor_test.rb
... ... @@ -55,4 +55,13 @@ class ActAsAccessorTest &lt; Test::Unit::TestCase
55 55 assert !a.role_assignments.map{|ra|[ra.role, ra.accessor, ra.resource]}.include?([role, a, res])
56 56 assert !a.remove_role(role, res)
57 57 end
  58 +
  59 + def test_get_members_by_role
  60 + res = AccessControlTestResource.create!(:name => 'bla')
  61 + a = AccessControlTestAccessor.create!(:name => 'ze')
  62 + role = Role.create!(:name => 'another_content_author', :permissions => ['bli'])
  63 + assert a.add_role(role, res)
  64 + assert_equal [a], a.members_by_role(role)
  65 + end
  66 +
58 67 end
... ...
vendor/plugins/active_record_tableless/MIT-LICENSE 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +Copyright (c) 2008 [name of plugin creator]
  2 +
  3 +Permission is hereby granted, free of charge, to any person obtaining
  4 +a copy of this software and associated documentation files (the
  5 +"Software"), to deal in the Software without restriction, including
  6 +without limitation the rights to use, copy, modify, merge, publish,
  7 +distribute, sublicense, and/or sell copies of the Software, and to
  8 +permit persons to whom the Software is furnished to do so, subject to
  9 +the following conditions:
  10 +
  11 +The above copyright notice and this permission notice shall be
  12 +included in all copies or substantial portions of the Software.
  13 +
  14 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
... ...
vendor/plugins/active_record_tableless/README 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +ActiveRecordTableless
  2 +=====================
  3 +
  4 +Rails plugin allowing ActiveRecord models to be used with validations but
  5 +without being backed by a database table.
  6 +
  7 +
  8 +Install
  9 +=======
  10 +
  11 +script/plugin install git://github.com/robinsp/active_record_tableless.git
  12 +
  13 +
  14 +Example
  15 +=======
  16 +
  17 +class TablelessModel < ActiveRecord::Base
  18 + tableless :columns => [
  19 + [:email, :string],
  20 + [:password, :string],
  21 + [:password_confirmation]
  22 + ]
  23 +
  24 + validates_presence_of :email, :password, :password_confirmation
  25 + validates_confirmation_of :password
  26 +
  27 +end
  28 +
  29 +
  30 +Copyright (c) 2008 [name of plugin creator], released under the MIT license
... ...
vendor/plugins/active_record_tableless/Rakefile 0 → 100644
... ... @@ -0,0 +1,22 @@
  1 +require 'rake'
  2 +require 'rake/testtask'
  3 +require 'rake/rdoctask'
  4 +
  5 +desc 'Default: run unit tests.'
  6 +task :default => :test
  7 +
  8 +desc 'Test the active_record_tableless plugin.'
  9 +Rake::TestTask.new(:test) do |t|
  10 + t.libs << 'lib'
  11 + t.pattern = 'test/**/*_test.rb'
  12 + t.verbose = true
  13 +end
  14 +
  15 +desc 'Generate documentation for the active_record_tableless plugin.'
  16 +Rake::RDocTask.new(:rdoc) do |rdoc|
  17 + rdoc.rdoc_dir = 'rdoc'
  18 + rdoc.title = 'ActiveRecordTableless'
  19 + rdoc.options << '--line-numbers' << '--inline-source'
  20 + rdoc.rdoc_files.include('README')
  21 + rdoc.rdoc_files.include('lib/**/*.rb')
  22 +end
... ...
vendor/plugins/active_record_tableless/init.rb 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +# Include hook code here
  2 +ActiveRecord::Base.send(:include, ActiveRecord::Tableless)
0 3 \ No newline at end of file
... ...
vendor/plugins/active_record_tableless/lib/active_record/tableless.rb 0 → 100644
... ... @@ -0,0 +1,56 @@
  1 +module ActiveRecord
  2 + module Tableless
  3 +
  4 + def self.included(base)
  5 + # 'base' is assumed to be ActiveRecord::Base
  6 + base.extend(ClassMethods)
  7 + end
  8 +
  9 + module ClassMethods
  10 + def tableless( options = {} )
  11 + include ActiveRecord::Tableless::InstanceMethods
  12 + raise "No columns defined" unless options.has_key?(:columns) && !options[:columns].empty?
  13 +
  14 + self.extend(MetaMethods)
  15 +
  16 + for column_args in options[:columns]
  17 + column( *column_args )
  18 + end
  19 +
  20 + end
  21 + end
  22 +
  23 + module MetaMethods
  24 + def columns()
  25 + @columns ||= []
  26 + end
  27 +
  28 + def column(name, sql_type = nil, default = nil, null = true)
  29 + columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
  30 + reset_column_information
  31 + end
  32 +
  33 + # Do not reset @columns
  34 + def reset_column_information
  35 + generated_methods.each { |name| undef_method(name) }
  36 + @column_names = @columns_hash = @content_columns = @dynamic_methods_hash = @read_methods = nil
  37 + end
  38 + end
  39 +
  40 + module InstanceMethods
  41 + def create_or_update
  42 + errors.empty?
  43 + end
  44 +
  45 + def saved!(with_id = 1)
  46 + self.id = with_id
  47 +
  48 + def self.new_record?
  49 + false
  50 + end
  51 + end
  52 + alias_method :exists!, :saved!
  53 + end
  54 +
  55 + end
  56 +end
0 57 \ No newline at end of file
... ...
vendor/plugins/active_record_tableless/tasks/active_record_tableless_tasks.rake 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +# desc "Explaining what the task does"
  2 +# task :active_record_tableless do
  3 +# # Task goes here
  4 +# end
... ...
vendor/plugins/active_record_tableless/test/active_record_tableless_test.rb 0 → 100644
... ... @@ -0,0 +1,75 @@
  1 +require 'test/unit'
  2 +require 'rubygems'
  3 +require 'active_record'
  4 +require File.dirname(__FILE__) + '/../lib/active_record/tableless'
  5 +
  6 +
  7 +
  8 +ActiveRecord::Base.establish_connection( {
  9 + :adapter => 'sqlite3',
  10 + :database => ":memory:",
  11 + :timeout => 500
  12 +
  13 +})
  14 +
  15 +ActiveRecord::Base.send(:include, ActiveRecord::Tableless)
  16 +
  17 +class TablelessModel < ActiveRecord::Base
  18 + tableless :columns => [
  19 + [:email, :string],
  20 + [:password, :string],
  21 + [:password_confirmation] ]
  22 +
  23 + validates_presence_of :email, :password, :password_confirmation
  24 + validates_confirmation_of :password
  25 +
  26 +end
  27 +
  28 +class ActiveRecordTablelessTest < Test::Unit::TestCase
  29 +
  30 + def setup
  31 + super
  32 + @valid_attributes = {
  33 + :email => "robin@bogus.com",
  34 + :password => "password",
  35 + :password_confirmation => "password"
  36 + }
  37 + end
  38 +
  39 + def test_create
  40 + assert TablelessModel.create(@valid_attributes).valid?
  41 + end
  42 +
  43 + def test_validations
  44 + # Just check a few validations to make sure we didn't break ActiveRecord::Validations::ClassMethods
  45 + assert_not_nil TablelessModel.create(@valid_attributes.merge(:email => "")).errors[:email]
  46 + assert_not_nil TablelessModel.create(@valid_attributes.merge(:password => "")).errors[:password]
  47 + assert_not_nil TablelessModel.create(@valid_attributes.merge(:password_confirmation => "")).errors[:password]
  48 + end
  49 +
  50 + def test_save
  51 + assert TablelessModel.new(@valid_attributes).save
  52 + assert !TablelessModel.new(@valid_attributes.merge(:password => "no_match")).save
  53 + end
  54 +
  55 + def test_valid?
  56 + assert TablelessModel.new(@valid_attributes).valid?
  57 + assert !TablelessModel.new(@valid_attributes.merge(:password => "no_match")).valid?
  58 + end
  59 +
  60 +
  61 + def test_exists!
  62 + m = TablelessModel.new(@valid_attributes)
  63 +
  64 + assert_nil m.id
  65 + assert m.new_record?
  66 +
  67 + m.exists!
  68 + assert_equal 1, m.id
  69 + assert !m.new_record?
  70 +
  71 + m.exists!(250)
  72 + assert_equal 250, m.id
  73 + assert !m.new_record?
  74 + end
  75 +end
... ...