diff --git a/Gemfile b/Gemfile
index d16ac58..5bf8e64 100644
--- a/Gemfile
+++ b/Gemfile
@@ -39,6 +39,7 @@ gem 'grape_logging'
gem 'rack-cors'
gem 'rack-contrib'
gem 'api-pagination', '>= 4.1.1'
+gem 'liquid', '~> 3.0.3'
# asset pipeline
gem 'uglifier', '>= 1.0.3'
diff --git a/app/controllers/admin/environment_email_templates_controller.rb b/app/controllers/admin/environment_email_templates_controller.rb
new file mode 100644
index 0000000..5326bc0
--- /dev/null
+++ b/app/controllers/admin/environment_email_templates_controller.rb
@@ -0,0 +1,15 @@
+class EnvironmentEmailTemplatesController < EmailTemplatesController
+
+ protect 'manage_email_templates', :environment
+
+ protected
+
+ def owner
+ environment
+ end
+
+ before_filter :only => :index do
+ @back_to = url_for(:controller => :admin_panel)
+ end
+
+end
diff --git a/app/controllers/my_profile/email_templates_controller.rb b/app/controllers/my_profile/email_templates_controller.rb
new file mode 100644
index 0000000..48eda4e
--- /dev/null
+++ b/app/controllers/my_profile/email_templates_controller.rb
@@ -0,0 +1,62 @@
+class EmailTemplatesController < ApplicationController
+
+ def index
+ @email_templates = owner.email_templates
+ end
+
+ def show
+ @email_template = owner.email_templates.find(params[:id])
+
+ respond_to do |format|
+ format.html # show.html.erb
+ format.json { render json: @email_template }
+ end
+ end
+
+ def show_parsed
+ @email_template = owner.email_templates.find(params[:id])
+ template_params = {:profile => owner, :environment => environment}
+ render json: {:parsed_body => @email_template.parsed_body(template_params), :parsed_subject => @email_template.parsed_subject(template_params)}
+ end
+
+ def new
+ @email_template = owner.email_templates.build(:owner => owner)
+ end
+
+ def edit
+ @email_template = owner.email_templates.find(params[:id])
+ end
+
+ def create
+ @email_template = owner.email_templates.build(params[:email_template])
+ @email_template.owner = owner
+
+ if @email_template.save
+ session[:notice] = _('Email template was successfully created.')
+ redirect_to url_for(:action => :index)
+ else
+ render action: "new"
+ end
+ end
+
+ def update
+ @email_template = owner.email_templates.find(params[:id])
+
+ if @email_template.update_attributes(params[:email_template])
+ session[:notice] = _('Email template was successfully updated.')
+ redirect_to url_for(:action => :index)
+ else
+ render action: "edit"
+ end
+ end
+
+ def destroy
+ @email_template = owner.email_templates.find(params[:id])
+ @email_template.destroy
+
+ respond_to do |format|
+ format.html { redirect_to url_for(:action => :index)}
+ format.json { head :no_content }
+ end
+ end
+end
diff --git a/app/controllers/my_profile/profile_email_templates_controller.rb b/app/controllers/my_profile/profile_email_templates_controller.rb
new file mode 100644
index 0000000..1aba8a7
--- /dev/null
+++ b/app/controllers/my_profile/profile_email_templates_controller.rb
@@ -0,0 +1,16 @@
+class ProfileEmailTemplatesController < EmailTemplatesController
+
+ needs_profile
+ protect 'manage_email_templates', :profile
+
+ protected
+
+ def owner
+ profile
+ end
+
+ before_filter :only => :index do
+ @back_to = url_for(:controller => :profile_editor)
+ end
+
+end
diff --git a/app/controllers/my_profile/tasks_controller.rb b/app/controllers/my_profile/tasks_controller.rb
index f31d673..5dfce4d 100644
--- a/app/controllers/my_profile/tasks_controller.rb
+++ b/app/controllers/my_profile/tasks_controller.rb
@@ -7,6 +7,9 @@ class TasksController < MyProfileController
helper CustomFieldsHelper
def index
+ @rejection_email_templates = profile.email_templates.find_all_by_template_type(:task_rejection)
+ @acceptance_email_templates = profile.email_templates.find_all_by_template_type(:task_acceptance)
+
@filter_type = params[:filter_type].presence
@filter_text = params[:filter_text].presence
@filter_responsible = params[:filter_responsible]
diff --git a/app/controllers/public/profile_controller.rb b/app/controllers/public/profile_controller.rb
index eec2a08..ee880b1 100644
--- a/app/controllers/public/profile_controller.rb
+++ b/app/controllers/public/profile_controller.rb
@@ -371,6 +371,7 @@ class ProfileController < PublicController
def send_mail
@mailing = profile.mailings.build(params[:mailing])
@mailing.data = session[:members_filtered] ? {:members_filtered => session[:members_filtered]} : {}
+ @email_templates = profile.email_templates.find_all_by_template_type(:organization_members)
if request.post?
@mailing.locale = locale
@mailing.person = user
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 1b5be1b..0519b6c 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -54,6 +54,8 @@ module ApplicationHelper
include ThemeLoaderHelper
+ include TaskHelper
+
def locale
(@page && !@page.language.blank?) ? @page.language : FastGettext.locale
end
diff --git a/app/helpers/email_template_helper.rb b/app/helpers/email_template_helper.rb
new file mode 100644
index 0000000..6f16ee4
--- /dev/null
+++ b/app/helpers/email_template_helper.rb
@@ -0,0 +1,12 @@
+module EmailTemplateHelper
+
+ def mail_with_template(params={})
+ if params[:email_template].present?
+ params[:body] = params[:email_template].parsed_body(params[:template_params])
+ params[:subject] = params[:email_template].parsed_subject(params[:template_params])
+ params[:content_type] = "text/html"
+ end
+ mail(params.except(:email_template))
+ end
+
+end
diff --git a/app/helpers/task_helper.rb b/app/helpers/task_helper.rb
new file mode 100644
index 0000000..86242f6
--- /dev/null
+++ b/app/helpers/task_helper.rb
@@ -0,0 +1,13 @@
+module TaskHelper
+
+ def task_email_template(description, email_templates, task, include_blank=true)
+ return '' unless email_templates.present?
+
+ content_tag(
+ :div,
+ labelled_form_field(description, select_tag("tasks[#{task.id}][task][email_template_id]", options_from_collection_for_select(email_templates, :id, :name), :include_blank => include_blank, 'data-url' => url_for(:controller => 'profile_email_templates', :action => 'show_parsed', :profile => profile.identifier))),
+ :class => 'template-selection'
+ )
+ end
+
+end
diff --git a/app/mailers/task_mailer.rb b/app/mailers/task_mailer.rb
index b2fa21c..ed7801e 100644
--- a/app/mailers/task_mailer.rb
+++ b/app/mailers/task_mailer.rb
@@ -1,5 +1,7 @@
class TaskMailer < ApplicationMailer
+ include EmailTemplateHelper
+
def target_notification(task, message)
self.environment = task.environment
@@ -38,10 +40,12 @@ class TaskMailer < ApplicationMailer
@requestor = task.requestor.name
@url = url_for(:host => task.requestor.environment.default_hostname, :controller => 'home')
- mail(
+ mail_with_template(
to: task.requestor.notification_emails,
from: self.class.generate_from(task),
- subject: '[%s] %s' % [task.requestor.environment.name, task.target_notification_description]
+ subject: '[%s] %s' % [task.requestor.environment.name, task.target_notification_description],
+ email_template: task.email_template,
+ template_params: {:environment => task.requestor.environment, :task => task, :message => @message, :url => @url, :requestor => task.requestor}
)
end
diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb
index 6b9549f..65f8ced 100644
--- a/app/mailers/user_mailer.rb
+++ b/app/mailers/user_mailer.rb
@@ -1,5 +1,7 @@
class UserMailer < ApplicationMailer
+ include EmailTemplateHelper
+
def activation_email_notify(user)
self.environment = user.environment
@@ -25,10 +27,12 @@ class UserMailer < ApplicationMailer
@redirection = (true if user.return_to)
@join = (user.community_to_join if user.community_to_join)
- mail(
+ mail_with_template(
from: "#{user.environment.name} <#{user.environment.contact_email}>",
to: user.email,
subject: _("[%s] Activate your account") % [user.environment.name],
+ template_params: {:environment => user.environment, :activation_code => @activation_code, :redirection => @redirection, :join => @join, :person => user.person, :url => @url},
+ email_template: user.environment.email_templates.find_by_template_type(:user_activation),
)
end
diff --git a/app/models/article.rb b/app/models/article.rb
index 0a5b8fc..aff781c 100644
--- a/app/models/article.rb
+++ b/app/models/article.rb
@@ -844,6 +844,10 @@ class Article < ActiveRecord::Base
true
end
+ def to_liquid
+ HashWithIndifferentAccess.new :name => name, :abstract => abstract, :body => body, :id => id, :parent_id => parent_id, :author => author
+ end
+
private
def sanitize_tag_list
diff --git a/app/models/change_password.rb b/app/models/change_password.rb
index 9d1a1dd..cd638ac 100644
--- a/app/models/change_password.rb
+++ b/app/models/change_password.rb
@@ -28,6 +28,13 @@ class ChangePassword < Task
validates_presence_of :password_confirmation, :on => :update, :if => lambda { |change| change.status != Task::Status::CANCELLED }
validates_confirmation_of :password, :if => lambda { |change| change.status != Task::Status::CANCELLED }
+ before_save :set_email_template
+
+ def set_email_template
+ template = environment.email_templates.find_by_template_type(:user_change_password)
+ data[:email_template_id] = template.id unless template.nil?
+ end
+
def environment
requestor.environment unless requestor.nil?
end
diff --git a/app/models/email_template.rb b/app/models/email_template.rb
new file mode 100644
index 0000000..6554bc6
--- /dev/null
+++ b/app/models/email_template.rb
@@ -0,0 +1,50 @@
+class EmailTemplate < ActiveRecord::Base
+
+ belongs_to :owner, :polymorphic => true
+
+ attr_accessible :template_type, :subject, :body, :owner, :name
+
+ validates_presence_of :name
+
+ validates :name, uniqueness: { scope: [:owner_type, :owner_id] }
+
+ validates :template_type, uniqueness: { scope: [:owner_type, :owner_id] }, if: :unique_by_type?
+
+ def parsed_body(params)
+ @parsed_body ||= parse(body, params)
+ end
+
+ def parsed_subject(params)
+ @parsed_subject ||= parse(subject, params)
+ end
+
+ def self.available_types
+ {
+ :task_rejection => {:description => _('Task Rejection'), :owner_type => Profile},
+ :task_acceptance => {:description => _('Task Acceptance'), :owner_type => Profile},
+ :organization_members => {:description => _('Organization Members'), :owner_type => Profile},
+ :user_activation => {:description => _('User Activation'), :unique => true, :owner_type => Environment},
+ :user_change_password => {:description => _('Change User Password'), :unique => true, :owner_type => Environment}
+ }
+ end
+
+ def available_types
+ HashWithIndifferentAccess.new EmailTemplate.available_types.select {|k, v| owner.kind_of?(v[:owner_type])}
+ end
+
+ def type_description
+ available_types.fetch(template_type, {})[:description]
+ end
+
+ def unique_by_type?
+ available_types.fetch(template_type, {})[:unique]
+ end
+
+ protected
+
+ def parse(source, params)
+ template = Liquid::Template.parse(source)
+ template.render(HashWithIndifferentAccess.new(params))
+ end
+
+end
diff --git a/app/models/environment.rb b/app/models/environment.rb
index b71e3bb..c3503e7 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -25,6 +25,7 @@ class Environment < ActiveRecord::Base
has_many :tasks, :dependent => :destroy, :as => 'target'
has_many :search_terms, :as => :context
has_many :custom_fields, :dependent => :destroy
+ has_many :email_templates, :foreign_key => :owner_id
IDENTIFY_SCRIPTS = /(php[0-9s]?|[sp]htm[l]?|pl|py|cgi|rb)/
@@ -55,6 +56,7 @@ class Environment < ActiveRecord::Base
'manage_environment_trusted_sites' => N_('Manage environment trusted sites'),
'edit_appearance' => N_('Edit appearance'),
'edit_raw_html_block' => N_('Edit Raw HTML block'),
+ 'manage_email_templates' => N_('Manage Email Templates'),
}
module Roles
@@ -1010,6 +1012,10 @@ class Environment < ActiveRecord::Base
self.licenses.any?
end
+ def to_liquid
+ HashWithIndifferentAccess.new :name => name
+ end
+
private
def default_language_available
diff --git a/app/models/profile.rb b/app/models/profile.rb
index 469ad4c..ebc22ee 100644
--- a/app/models/profile.rb
+++ b/app/models/profile.rb
@@ -84,6 +84,7 @@ class Profile < ActiveRecord::Base
'invite_members' => N_('Invite members'),
'send_mail_to_members' => N_('Send e-Mail to members'),
'manage_custom_roles' => N_('Manage custom roles'),
+ 'manage_email_templates' => N_('Manage Email Templates'),
}
acts_as_accessible
@@ -218,6 +219,8 @@ class Profile < ActiveRecord::Base
has_many :comments_received, :class_name => 'Comment', :through => :articles, :source => :comments
+ has_many :email_templates, :foreign_key => :owner_id
+
# Although this should be a has_one relation, there are no non-silly names for
# a foreign key on article to reference the template to which it is
# welcome_page... =P
@@ -542,6 +545,10 @@ class Profile < ActiveRecord::Base
).order('articles.published_at desc, articles.id desc')
end
+ def to_liquid
+ HashWithIndifferentAccess.new :name => name, :identifier => identifier
+ end
+
class << self
# finds a profile by its identifier. This method is a shortcut to
diff --git a/app/models/task.rb b/app/models/task.rb
index a1c45f5..9c89499 100644
--- a/app/models/task.rb
+++ b/app/models/task.rb
@@ -41,6 +41,8 @@ class Task < ActiveRecord::Base
attr_protected :status
+ settings_items :email_template_id, :type => :integer
+
def initialize(*args)
super
self.status = (args.first ? args.first[:status] : nil) || Task::Status::ACTIVE
@@ -273,6 +275,18 @@ class Task < ActiveRecord::Base
end
end
+ def email_template
+ @email_template ||= email_template_id.present? ? EmailTemplate.find_by_id(email_template_id) : nil
+ end
+
+ def to_liquid
+ HashWithIndifferentAccess.new({
+ :requestor => requestor,
+ :reject_explanation => reject_explanation,
+ :code => code
+ })
+ end
+
scope :pending, -> { where status: Task::Status::ACTIVE }
scope :hidden, -> { where status: Task::Status::HIDDEN }
scope :finished, -> { where status: Task::Status::FINISHED }
diff --git a/app/views/admin_panel/index.html.erb b/app/views/admin_panel/index.html.erb
index eab20d5..6dbca60 100644
--- a/app/views/admin_panel/index.html.erb
+++ b/app/views/admin_panel/index.html.erb
@@ -11,6 +11,7 @@
<%= link_to _('Homepage'), :action => 'set_portal_community' %> |
<%= link_to _('Licenses'), :controller =>'licenses' %> |
<%= link_to _('Trusted sites'), :controller =>'trusted_sites' %> |
+ <%= link_to _('Email templates'), :controller =>'environment_email_templates' %> |
<%= _('Profiles') %>
diff --git a/app/views/email_templates/_form.html.erb b/app/views/email_templates/_form.html.erb
new file mode 100644
index 0000000..6db38a1
--- /dev/null
+++ b/app/views/email_templates/_form.html.erb
@@ -0,0 +1,31 @@
+<%= form_for(@email_template, :url => {:action => @email_template.persisted? ? :update : :create, :id => @email_template.id}) do |f| %>
+
+ <%= error_messages_for :email_template if @email_template.errors.any? %>
+
+
+
+
+
+
+ <%= _('The following parameters may be used in subject and body:') %>
+
+
+ {{profile.name}}, {{profile.identifier}}, {{environment.name}}
+
+
+ <%= render :file => 'shared/tiny_mce' %>
+ <%= labelled_form_field(_('Body:'), f.text_area(:body, :class => 'mceEditor')) %>
+
+
+
+ <%= submit_button(:save, _('Save')) %>
+ <%= button(:back, _('Back'), :action => :index) %>
+
+
+<% end %>
diff --git a/app/views/email_templates/edit.html.erb b/app/views/email_templates/edit.html.erb
new file mode 100644
index 0000000..0a9b078
--- /dev/null
+++ b/app/views/email_templates/edit.html.erb
@@ -0,0 +1,3 @@
+Editing Email Template
+
+<%= render 'form' %>
diff --git a/app/views/email_templates/index.html.erb b/app/views/email_templates/index.html.erb
new file mode 100644
index 0000000..45cd2e8
--- /dev/null
+++ b/app/views/email_templates/index.html.erb
@@ -0,0 +1,27 @@
+
+
<%= _('Email Templates') %>
+
+
+
+ <%= _('Name') %> |
+ <%= _('Type') %> |
+ <%= _('Actions') %> |
+
+
+ <% @email_templates.each do |email_template| %>
+
+ <%= email_template.name %> |
+ <%= email_template.type_description %> |
+
+ <%= button_without_text(:edit, _('Edit'), {:action => :edit, :id => email_template.id}) %>
+ <%= button_without_text(:remove, _('Remove'), {:action => :destroy, :id => email_template.id}, method: :delete, data: { confirm: 'Are you sure?' }) %>
+ |
+
+ <% end %>
+
+
+
+
+ <%= button(:new, _('New template'), :action => :new) %>
+ <%= button(:back, _('Back'), @back_to) %>
+
diff --git a/app/views/email_templates/new.html.erb b/app/views/email_templates/new.html.erb
new file mode 100644
index 0000000..739ff6b
--- /dev/null
+++ b/app/views/email_templates/new.html.erb
@@ -0,0 +1,3 @@
+New Email Template
+
+<%= render 'form' %>
diff --git a/app/views/email_templates/show.html.erb b/app/views/email_templates/show.html.erb
new file mode 100644
index 0000000..14493b7
--- /dev/null
+++ b/app/views/email_templates/show.html.erb
@@ -0,0 +1,5 @@
+<%= notice %>
+
+
+<%= link_to 'Edit', url_for(:action => :edit, :id => @email_template.id) %> |
+<%= link_to 'Back', url_for(:action => :index) %>
diff --git a/app/views/profile/send_mail.html.erb b/app/views/profile/send_mail.html.erb
index b994e88..ce93620 100644
--- a/app/views/profile/send_mail.html.erb
+++ b/app/views/profile/send_mail.html.erb
@@ -6,6 +6,11 @@
<% to = @mailing.data[:members_filtered].present? ? @mailing.recipients.map{|r| r.name}.join(', ') : _('All members')%>
<%= labelled_form_field(_('To:'), text_area(:data, 'members_filtered', :value => to, :rows => 4, :disabled => 'disabled', :class => 'send-mail-recipients')) %>
+
+ <% if @email_templates.present? %>
+ <%= labelled_form_field(_('Select a template:'), select_tag(:template, options_from_collection_for_select(@email_templates, :id, :name), :include_blank => true, 'data-url' => url_for(:controller => 'email_templates', :action => 'show_parsed'))) %>
+
+<% end %>
<%= form_for :mailing, :url => {:action => 'send_mail'}, :html => {:id => 'mailing-form'} do |f| %>
diff --git a/app/views/profile_editor/index.html.erb b/app/views/profile_editor/index.html.erb
index 8f51c95..15326f4 100644
--- a/app/views/profile_editor/index.html.erb
+++ b/app/views/profile_editor/index.html.erb
@@ -72,6 +72,8 @@
<%= control_panel_button(_('Edit welcome page'), 'welcome-page', :action => 'welcome_page') if has_welcome_page %>
+ <%= control_panel_button(_('Email Templates'), 'email-templates', :controller => :profile_email_templates) if profile.organization? %>
+
<% @plugins.dispatch(:control_panel_buttons).each do |button| %>
<%= control_panel_button(button[:title], button[:icon], button[:url], button[:html_options]) %>
<% end %>
diff --git a/app/views/tasks/_approve_article_accept_details.html.erb b/app/views/tasks/_approve_article_accept_details.html.erb
index d0760c9..389e168 100644
--- a/app/views/tasks/_approve_article_accept_details.html.erb
+++ b/app/views/tasks/_approve_article_accept_details.html.erb
@@ -1,3 +1,5 @@
+<%= task_email_template(_('Select an acceptance email template:'), @acceptance_email_templates, task) %>
+
<%= render :file => 'shared/tiny_mce' %>
<%= labelled_form_field(_('Create a link'), f.check_box(:create_link)) %>
diff --git a/app/views/tasks/_task_reject_details.html.erb b/app/views/tasks/_task_reject_details.html.erb
index 13bc80c..bfc8043 100644
--- a/app/views/tasks/_task_reject_details.html.erb
+++ b/app/views/tasks/_task_reject_details.html.erb
@@ -1 +1,3 @@
+<%= task_email_template(_('Select a rejection email template:'), @rejection_email_templates, task) %>
+
<%= labelled_form_field(_('Rejection explanation'), f.text_area(:reject_explanation, :rows => 5)) %>
diff --git a/db/migrate/20160311184534_create_email_template.rb b/db/migrate/20160311184534_create_email_template.rb
new file mode 100644
index 0000000..1da1751
--- /dev/null
+++ b/db/migrate/20160311184534_create_email_template.rb
@@ -0,0 +1,12 @@
+class CreateEmailTemplate < ActiveRecord::Migration
+ def change
+ create_table :email_templates do |t|
+ t.string :name
+ t.string :template_type
+ t.string :subject
+ t.text :body
+ t.references :owner, :polymorphic => true
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index fb6a2aa..7de0ff2 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -357,6 +357,17 @@ ActiveRecord::Schema.define(version: 20160324132518) do
add_index "domains", ["owner_id", "owner_type", "is_default"], name: "index_domains_on_owner_id_and_owner_type_and_is_default", using: :btree
add_index "domains", ["owner_id", "owner_type"], name: "index_domains_on_owner_id_and_owner_type", using: :btree
+ create_table "email_templates", force: :cascade do |t|
+ t.string "name"
+ t.string "template_type"
+ t.string "subject"
+ t.text "body"
+ t.integer "owner_id"
+ t.string "owner_type"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
create_table "environments", force: :cascade do |t|
t.string "name"
t.string "contact_email"
diff --git a/public/javascripts/application.js b/public/javascripts/application.js
index d3329ae..a23bd43 100644
--- a/public/javascripts/application.js
+++ b/public/javascripts/application.js
@@ -33,6 +33,7 @@
*= require require_login.js
*= require slick.js
*= require block-store.js
+*= require email_templates.js
*/
// lodash configuration
diff --git a/public/javascripts/email_templates.js b/public/javascripts/email_templates.js
new file mode 100644
index 0000000..87a35d8
--- /dev/null
+++ b/public/javascripts/email_templates.js
@@ -0,0 +1,10 @@
+jQuery(document).ready(function($) {
+ $('.template-selection select').change(function() {
+ if(!$(this).val()) return;
+
+ $.getJSON($(this).data('url'), {id: $(this).val()}, function(data) {
+ $('#mailing-form #mailing_subject').val(data.parsed_subject);
+ $('#mailing-form .mceEditor').val(data.parsed_body);
+ });
+ });
+});
diff --git a/public/javascripts/tasks.js b/public/javascripts/tasks.js
index 4bc028d..069821a 100644
--- a/public/javascripts/tasks.js
+++ b/public/javascripts/tasks.js
@@ -2,18 +2,28 @@
$("input.task_accept_radio").click(function(){
task_id = this.getAttribute("task_id");
- $('#on-accept-information-' + task_id).show('fast');
- $('#on-reject-information-' + task_id).hide('fast');
+ var accept_container = $('#on-accept-information-' + task_id);
+ var reject_container = $('#on-reject-information-' + task_id);
+
+ accept_container.show('fast');
+ reject_container.hide('fast');
$('#on-skip-information-' + task_id).hide('fast');
$('#custom-field-information-' + task_id).show('fast');
+ reject_container.find('input, select').prop('disabled', true);
+ accept_container.find('input, select').prop('disabled', false);
})
$("input.task_reject_radio").click(function(){
task_id = this.getAttribute("task_id");
- $('#on-accept-information-' + task_id).hide('fast');
- $('#on-reject-information-' + task_id).show('fast');
+ var accept_container = $('#on-accept-information-' + task_id);
+ var reject_container = $('#on-reject-information-' + task_id);
+
+ accept_container.hide('fast');
+ reject_container.show('fast');
$('#on-skip-information-' + task_id).hide('fast');
$('#custom-field-information-' + task_id).show('fast');
+ reject_container.find('input, select').prop('disabled', false);
+ accept_container.find('input, select').prop('disabled', true);
})
$("input.task_skip_radio").click(function(){
diff --git a/public/proposal-app b/public/proposal-app
new file mode 160000
index 0000000..e7ad784
--- /dev/null
+++ b/public/proposal-app
@@ -0,0 +1 @@
+Subproject commit e7ad7849b3ef685639d4c5c0bef7b323255e2afa
diff --git a/public/stylesheets/email-templates.css b/public/stylesheets/email-templates.css
new file mode 100644
index 0000000..556ecd0
--- /dev/null
+++ b/public/stylesheets/email-templates.css
@@ -0,0 +1,18 @@
+.template-fields .header-fields, .template-fields .available-params {
+ width: 48%;
+ display: inline-block;
+}
+.template-fields .available-params .values {
+ color: rgb(111, 111, 111);
+}
+.template-fields .available-params .reference {
+ text-align: right;
+}
+.template-fields .available-params .reference a {
+ color: rgb(32, 101, 229);
+ text-decoration: none;
+ font-size: 10px;
+}
+.email-templates table td {
+ text-align: center;
+}
diff --git a/test/fixtures/roles.yml b/test/fixtures/roles.yml
index 11a7b04..ae763d7 100644
--- a/test/fixtures/roles.yml
+++ b/test/fixtures/roles.yml
@@ -38,6 +38,7 @@ four:
- manage_environment_organizations
- manage_environment_templates
- manage_environment_licenses
+ - manage_email_templates
profile_admin:
id: 5
environment_id: 1
@@ -60,6 +61,7 @@ profile_admin:
- manage_friends
- validate_enterprise
- publish_content
+ - manage_email_templates
profile_member:
id: 6
environment_id: 1
@@ -101,3 +103,4 @@ environment_administrator:
- manage_environment_templates
- manage_environment_licenses
- edit_raw_html_block
+ - manage_email_templates
diff --git a/test/functional/environment_email_templates_controller_test.rb b/test/functional/environment_email_templates_controller_test.rb
new file mode 100644
index 0000000..b7e4a87
--- /dev/null
+++ b/test/functional/environment_email_templates_controller_test.rb
@@ -0,0 +1,65 @@
+require 'test_helper'
+
+class EnvironmentEmailTemplatesControllerTest < ActionController::TestCase
+
+ setup do
+ @email_template = EmailTemplate.create!(:name => 'template', :owner => Environment.default)
+ person = create_user_with_permission('template_manager', 'manage_email_templates', Environment.default)
+ login_as(person.user.login)
+ end
+
+ test "should get index" do
+ get :index
+ assert_response :success
+ assert_not_nil assigns(:email_templates)
+ end
+
+ test "should get new" do
+ get :new
+ assert_response :success
+ end
+
+ test "should create email_template" do
+ assert_difference('EmailTemplate.count') do
+ post :create, email_template: { :name => 'test' }
+ end
+
+ assert_redirected_to url_for(:action => :index)
+ end
+
+ test "should show email_template" do
+ get :show, id: @email_template
+ assert_response :success
+ end
+
+ test "should get edit" do
+ get :edit, id: @email_template
+ assert_response :success
+ end
+
+ test "should update email_template" do
+ put :update, id: @email_template, email_template: { }
+ assert_redirected_to url_for(:action => :index)
+ end
+
+ test "should destroy email_template" do
+ assert_difference('EmailTemplate.count', -1) do
+ delete :destroy, id: @email_template
+ end
+
+ assert_redirected_to url_for(:action => :index)
+ end
+
+ test "should get parsed template" do
+ environment = Environment.default
+ @email_template.subject = '{{environment.name}}'
+ @email_template.body = '{{environment.name}}'
+ @email_template.save!
+ get :show_parsed, id: @email_template
+ assert_response :success
+ json_response = ActiveSupport::JSON.decode(@response.body)
+ assert_equal "#{environment.name}", json_response['parsed_subject']
+ assert_equal "#{environment.name}", json_response['parsed_body']
+ end
+
+end
diff --git a/test/functional/profile_controller_test.rb b/test/functional/profile_controller_test.rb
index 834394c..cefa4bd 100644
--- a/test/functional/profile_controller_test.rb
+++ b/test/functional/profile_controller_test.rb
@@ -1527,6 +1527,29 @@ class ProfileControllerTest < ActionController::TestCase
assert_redirected_to :action => 'members'
end
+ should 'display email templates as an option to send mail' do
+ community = fast_create(Community)
+ create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
+ login_as('profile_moderator_user')
+
+ template1 = EmailTemplate.create!(:owner => community, :name => "Template 1", :template_type => :organization_members)
+ template2 = EmailTemplate.create!(:owner => community, :name => "Template 2")
+
+ get :send_mail, :profile => community.identifier, :mailing => {:subject => 'Hello', :body => 'We have some news'}
+ assert_select '.template-selection'
+ assert_equal [template1], assigns(:email_templates)
+ end
+
+ should 'do not display email template selection when there is no template for organization members' do
+ community = fast_create(Community)
+ create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
+ login_as('profile_moderator_user')
+
+ get :send_mail, :profile => community.identifier, :mailing => {:subject => 'Hello', :body => 'We have some news'}
+ assert_select '.template-selection'
+ assert assigns(:email_templates).empty?
+ end
+
should 'show all fields to anonymous user' do
viewed = create_user('person_1').person
Environment.any_instance.stubs(:active_person_fields).returns(['sex', 'birth_date'])
diff --git a/test/functional/profile_editor_controller_test.rb b/test/functional/profile_editor_controller_test.rb
index d931ee3..0602082 100644
--- a/test/functional/profile_editor_controller_test.rb
+++ b/test/functional/profile_editor_controller_test.rb
@@ -623,6 +623,17 @@ class ProfileEditorControllerTest < ActionController::TestCase
assert_tag :tag => 'a', :attributes => { :href => '/myprofile/default_user/cms' }
end
+ should 'display email template link for organizations in control panel' do
+ profile = fast_create(Organization)
+ get :index, :profile => profile.identifier
+ assert_tag :tag => 'a', :attributes => { :href => "/myprofile/#{profile.identifier}/profile_email_templates" }
+ end
+
+ should 'not display email template link in control panel for person' do
+ get :index, :profile => profile.identifier
+ assert_no_tag :tag => 'a', :attributes => { :href => "/myprofile/#{profile.identifier}/email_templates" }
+ end
+
should 'offer to create blog in control panel' do
get :index, :profile => profile.identifier
assert_tag :tag => 'a', :attributes => { :href => "/myprofile/default_user/cms/new?type=Blog" }
diff --git a/test/functional/profile_email_templates_controller_test.rb b/test/functional/profile_email_templates_controller_test.rb
new file mode 100644
index 0000000..612dc15
--- /dev/null
+++ b/test/functional/profile_email_templates_controller_test.rb
@@ -0,0 +1,68 @@
+require 'test_helper'
+
+class ProfileEmailTemplatesControllerTest < ActionController::TestCase
+
+ setup do
+ @profile = fast_create(Community)
+ @email_template = EmailTemplate.create!(:name => 'template', :owner => @profile)
+ @person = create_user_with_permission('templatemanager', 'manage_email_templates', @profile)
+ login_as(@person.user.login)
+ end
+
+ attr_accessor :profile, :person
+
+ test "should get index" do
+ get :index, :profile => profile.identifier
+ assert_response :success
+ assert_not_nil assigns(:email_templates)
+ end
+
+ test "should get new" do
+ get :new, :profile => profile.identifier
+ assert_response :success
+ end
+
+ test "should create email_template" do
+ assert_difference('EmailTemplate.count') do
+ post :create, email_template: { :name => 'test' }, :profile => profile.identifier
+ end
+
+ assert_redirected_to url_for(:action => :index)
+ end
+
+ test "should show email_template" do
+ get :show, id: @email_template, :profile => profile.identifier
+ assert_response :success
+ end
+
+ test "should get edit" do
+ get :edit, id: @email_template, :profile => profile.identifier
+ assert_response :success
+ end
+
+ test "should update email_template" do
+ put :update, id: @email_template, email_template: { }, :profile => profile.identifier
+ assert_redirected_to url_for(:action => :index)
+ end
+
+ test "should destroy email_template" do
+ assert_difference('EmailTemplate.count', -1) do
+ delete :destroy, id: @email_template, :profile => profile.identifier
+ end
+
+ assert_redirected_to url_for(:action => :index)
+ end
+
+ test "should get parsed template" do
+ environment = Environment.default
+ @email_template.subject = '{{profile.name}} - {{profile.identifier}}'
+ @email_template.body = '{{profile.name}} - {{profile.identifier}} - {{environment.name}}'
+ @email_template.save!
+ get :show_parsed, id: @email_template, :profile => profile.identifier
+ assert_response :success
+ json_response = ActiveSupport::JSON.decode(@response.body)
+ assert_equal "#{profile.name} - #{profile.identifier}", json_response['parsed_subject']
+ assert_equal "#{profile.name} - #{profile.identifier} - #{environment.name}", json_response['parsed_body']
+ end
+
+end
diff --git a/test/functional/tasks_controller_test.rb b/test/functional/tasks_controller_test.rb
index cb6d589..a3033ad 100644
--- a/test/functional/tasks_controller_test.rb
+++ b/test/functional/tasks_controller_test.rb
@@ -704,4 +704,30 @@ class TasksControllerTest < ActionController::TestCase
assert_tag :tag=> 'div', :attributes => { :class => 'field-name' }, :content => /great_field: new value for community!/
end
+ should "display email template selection when accept a task" do
+ community = fast_create(Community)
+ @controller.stubs(:profile).returns(community)
+ person = create_user_with_permission('taskviewer', 'view_tasks', community)
+ login_as person.user.login
+
+ email_template = EmailTemplate.create!(:name => 'template', :owner => community, :template_type => :task_acceptance)
+ task = ApproveArticle.create!(:requestor => person, :target => community, :responsible => person)
+ get :index
+ assert_select "#on-accept-information-#{task.id} .template-selection"
+ assert_equal [email_template], assigns(:acceptance_email_templates)
+ end
+
+ should "display email template selection when reject a task" do
+ community = fast_create(Community)
+ @controller.stubs(:profile).returns(community)
+ person = create_user_with_permission('taskviewer', 'view_tasks', community)
+ login_as person.user.login
+
+ email_template = EmailTemplate.create!(:name => 'template', :owner => community, :template_type => :task_rejection)
+ task = ApproveArticle.create!(:requestor => person, :target => community, :responsible => person)
+ get :index
+ assert_select "#on-reject-information-#{task.id} .template-selection"
+ assert_equal [email_template], assigns(:rejection_email_templates)
+ end
+
end
diff --git a/test/unit/change_password_test.rb b/test/unit/change_password_test.rb
index 5157f1f..5a28f66 100644
--- a/test/unit/change_password_test.rb
+++ b/test/unit/change_password_test.rb
@@ -71,4 +71,10 @@ class ChangePasswordTest < ActiveSupport::TestCase
assert_match(/#{task.requestor.name} wants to change its password/, email.subject)
end
+ should 'set email template when it exists' do
+ template = EmailTemplate.create!(:template_type => :user_change_password, :name => 'template1', :owner => Environment.default)
+ task = ChangePassword.create!(:requestor => person)
+ assert_equal template.id, task.email_template_id
+ end
+
end
diff --git a/test/unit/email_template_helper_test.rb b/test/unit/email_template_helper_test.rb
new file mode 100644
index 0000000..1bfb2c8
--- /dev/null
+++ b/test/unit/email_template_helper_test.rb
@@ -0,0 +1,20 @@
+require_relative "../test_helper"
+
+class EmailTemplateHelperTest < ActionView::TestCase
+
+ should 'replace body and subject with parsed values from template' do
+ template = mock
+ template.expects(:parsed_body).returns('parsed body')
+ template.expects(:parsed_subject).returns('parsed subject')
+ params = {:subject => 'subject', :body => 'body', :email_template => template}
+ expects(:mail).with({:subject => 'parsed subject', :body => 'parsed body', :content_type => 'text/html'})
+ mail_with_template(params)
+ end
+
+ should 'do not change params if there is no email template' do
+ params = {:subject => 'subject', :body => 'body'}
+ expects(:mail).with(params)
+ mail_with_template(params)
+ end
+
+end
diff --git a/test/unit/email_template_test.rb b/test/unit/email_template_test.rb
new file mode 100644
index 0000000..e44d4a6
--- /dev/null
+++ b/test/unit/email_template_test.rb
@@ -0,0 +1,53 @@
+require_relative "../test_helper"
+
+class EmailTemplateTest < ActiveSupport::TestCase
+
+ should 'filter templates by type' do
+ EmailTemplate.create!(:template_type => :type1, :name => 'template1')
+ EmailTemplate.create!(:template_type => :type2, :name => 'template2')
+ EmailTemplate.create!(:template_type => :type2, :name => 'template3')
+ assert_equal ['template2', 'template3'], EmailTemplate.find_all_by_template_type(:type2).map(&:name)
+ end
+
+ should 'parse body using params' do
+ template = EmailTemplate.new(:body => 'Hi {{person}}')
+ assert_equal 'Hi John', template.parsed_body({:person => 'John'})
+ end
+
+ should 'parse subject using params' do
+ template = EmailTemplate.new(:subject => 'Hi {{person}}')
+ assert_equal 'Hi John', template.parsed_subject({:person => 'John'})
+ end
+
+ should 'not create template with the same name of other' do
+ template1 = EmailTemplate.new(:template_type => :type1, :name => 'template', :owner => Environment.default)
+ template2 = EmailTemplate.new(:template_type => :type1, :name => 'template', :owner => Environment.default)
+ assert template1.save
+ assert !template2.save
+ end
+
+ should 'not create duplicated template when template type is unique' do
+ template1 = EmailTemplate.new(:template_type => :user_activation, :name => 'template1', :owner => Environment.default)
+ template2 = EmailTemplate.new(:template_type => :user_activation, :name => 'template2', :owner => Environment.default)
+ assert template1.save
+ assert !template2.save
+ end
+
+ should 'create duplicated template when template type is not unique' do
+ template1 = EmailTemplate.new(:template_type => :task_rejection, :name => 'template1', :owner => Environment.default)
+ template2 = EmailTemplate.new(:template_type => :task_rejection, :name => 'template2', :owner => Environment.default)
+ assert template1.save
+ assert template2.save
+ end
+
+ should 'return available types when the owner is an environment' do
+ template = EmailTemplate.new(:owner => Environment.default)
+ assert_equal [:user_activation, :user_change_password], template.available_types.symbolize_keys.keys
+ end
+
+ should 'return available types when the owner is a profile' do
+ template = EmailTemplate.new(:owner => Profile.new)
+ assert_equal [:task_rejection, :task_acceptance, :organization_members], template.available_types.symbolize_keys.keys
+ end
+
+end
diff --git a/test/unit/task_helper_test.rb b/test/unit/task_helper_test.rb
new file mode 100644
index 0000000..289a693
--- /dev/null
+++ b/test/unit/task_helper_test.rb
@@ -0,0 +1,23 @@
+require_relative "../test_helper"
+
+class TaskHelperTest < ActionView::TestCase
+
+ include ApplicationHelper
+
+ def setup
+ @profile = fast_create(Profile)
+ @task = fast_create(Task, :target_id => @profile.id)
+ end
+
+ attr_accessor :task, :profile
+
+ should 'return select field for template selection when there is templates to choose' do
+ email_templates = 3.times.map { EmailTemplate.new }
+ assert_tag_in_string task_email_template('Description', email_templates, task), :tag => 'div', :attributes => {:class => 'template-selection'}
+ end
+
+ should 'not return select field for template selection when there is no templates to choose' do
+ assert task_email_template('Description', [], task).blank?
+ end
+
+end
diff --git a/test/unit/task_mailer_test.rb b/test/unit/task_mailer_test.rb
index 1dbf834..2daa8c3 100644
--- a/test/unit/task_mailer_test.rb
+++ b/test/unit/task_mailer_test.rb
@@ -123,6 +123,53 @@ class TaskMailerTest < ActiveSupport::TestCase
assert_match(/#{url_to_compare}/, mail.body.to_s)
end
+ should 'be able to send rejection notification based on a selected template' do
+ task = Task.new
+ task.expects(:task_cancelled_message).returns('the message')
+ task.reject_explanation = 'explanation'
+
+ profile = fast_create(Community)
+ email_template = EmailTemplate.create!(:owner => profile, :name => 'Template 1', :subject => 'template subject - {{environment.name}}', :body => 'template body - {{environment.name}} - {{task.requestor.name}} - {{task.reject_explanation}}')
+ task.email_template_id = email_template.id
+
+ requestor = Profile.new(:name => 'my name')
+ requestor.expects(:notification_emails).returns(['requestor@example.com']).at_least_once
+
+ task.expects(:requestor).returns(requestor).at_least_once
+ requestor.expects(:environment).returns(@environment).at_least_once
+ task.expects(:environment).returns(@environment).at_least_once
+
+ task.send(:send_notification, :cancelled).deliver
+ assert !ActionMailer::Base.deliveries.empty?
+ mail = ActionMailer::Base.deliveries.last
+ assert_match /text\/html/, mail.content_type
+ assert_equal 'template subject - example', mail.subject.to_s
+ assert_equal 'template body - example - my name - explanation', mail.body.to_s
+ end
+
+ should 'be able to send accept notification based on a selected template' do
+ task = Task.new
+ task.expects(:task_finished_message).returns('the message')
+
+ profile = fast_create(Community)
+ email_template = EmailTemplate.create!(:owner => profile, :name => 'Template 1', :subject => 'template subject - {{environment.name}}', :body => 'template body - {{environment.name}} - {{task.requestor.name}}')
+ task.email_template_id = email_template.id
+
+ requestor = Profile.new(:name => 'my name')
+ requestor.expects(:notification_emails).returns(['requestor@example.com']).at_least_once
+
+ task.expects(:requestor).returns(requestor).at_least_once
+ requestor.expects(:environment).returns(@environment).at_least_once
+ task.expects(:environment).returns(@environment).at_least_once
+
+ task.send(:send_notification, :finished).deliver
+ assert !ActionMailer::Base.deliveries.empty?
+ mail = ActionMailer::Base.deliveries.last
+ assert_match /text\/html/, mail.content_type
+ assert_equal 'template subject - example', mail.subject.to_s
+ assert_equal 'template body - example - my name', mail.body.to_s
+ end
+
private
def read_fixture(action)
IO.readlines("#{FIXTURES_PATH}/task_mailer/#{action}")
diff --git a/test/unit/user_mailer_test.rb b/test/unit/user_mailer_test.rb
index b119336..92770aa 100644
--- a/test/unit/user_mailer_test.rb
+++ b/test/unit/user_mailer_test.rb
@@ -26,6 +26,24 @@ fast_create(Person))
assert_match /profile\/some-user\/friends\/suggest/, email.body.to_s
end
+ should 'deliver activation code email' do
+ assert_difference 'ActionMailer::Base.deliveries.size' do
+ u = create_user('some-user')
+ UserMailer.activation_code(u).deliver
+ end
+ end
+
+ should 'deliver activation code email with template' do
+ EmailTemplate.create!(:template_type => :user_activation, :name => 'template1', :subject => 'activation template subject', :body => 'activation template body', :owner => Environment.default)
+ assert_difference 'ActionMailer::Base.deliveries.size' do
+ u = create_user('some-user')
+ UserMailer.activation_code(u).deliver
+ end
+ mail = ActionMailer::Base.deliveries.last
+ assert_equal 'activation template subject', mail.subject.to_s
+ assert_equal 'activation template body', mail.body.to_s
+ end
+
private
def read_fixture(action)
--
libgit2 0.21.2