From 91221f26d5f6d0cda044f421c2574c8b126c9e07 Mon Sep 17 00:00:00 2001 From: Karel Picman Date: Wed, 26 Jun 2013 14:30:34 +0200 Subject: [PATCH] approval email notifications implemented --- app/controllers/dmsf_workflows_controller.rb | 90 ++++++++++++++++--- app/helpers/dmsf_workflows_helper.rb | 4 +- app/models/dmsf_mailer.rb | 16 +++- app/models/dmsf_workflow.rb | 80 +++++++++-------- app/models/dmsf_workflow_step_assignment.rb | 7 +- .../workflow_notification.html.erb | 15 ++++ .../workflow_notification.text.erb | 4 + config/locales/en.yml | 3 +- lib/tasks/dmsf_alert_approvals.rake | 54 +++++++++++ .../dmsf_workflow_step_assignment_test.rb | 4 - test/unit/dmsf_workflow_test.rb | 5 ++ 11 files changed, 221 insertions(+), 61 deletions(-) create mode 100644 app/views/dmsf_mailer/workflow_notification.html.erb create mode 100644 app/views/dmsf_mailer/workflow_notification.text.erb create mode 100644 lib/tasks/dmsf_alert_approvals.rake diff --git a/app/controllers/dmsf_workflows_controller.rb b/app/controllers/dmsf_workflows_controller.rb index 5a409765..15b41adf 100644 --- a/app/controllers/dmsf_workflows_controller.rb +++ b/app/controllers/dmsf_workflows_controller.rb @@ -40,13 +40,72 @@ class DmsfWorkflowsController < ApplicationController if params[:commit] == l(:button_submit) action = DmsfWorkflowStepAction.new( :dmsf_workflow_step_assignment_id => params[:dmsf_workflow_step_assignment_id], - :action => params[:step_action], + :action => (params[:step_action].to_i >= 10) ? DmsfWorkflowStepAction::ACTION_DELEGATE : params[:step_action], :note => params[:note]) if request.post? if action.save - if @workflow.try_finish params[:dmsf_file_revision_id], action, params[:user_id] - file = DmsfFile.joins(:revisions).where(:dmsf_file_revisions => {:id => params[:dmsf_file_revision_id]}).first - file.unlock! if file + revision = DmsfFileRevision.find_by_id params[:dmsf_file_revision_id] + if revision + if @workflow.try_finish revision, action, (params[:step_action].to_i / 10) + file = DmsfFile.joins(:revisions).where(:dmsf_file_revisions => {:id => revision.id}).first + file.unlock! if file + if revision.workflow == DmsfWorkflow::STATE_APPROVED + # Just approved + DmsfMailer.workflow_notification( + revision.project.members.collect{ |member| member.user.mail}, + @workflow, + revision, + "Approval workflow #{@workflow.name} approved", + 'been finished and the document has been approved', + 'To see the approval history click on the workflow status of the document in').deliver + else + # Just rejected + owner = User.find_by_id revision.dmsf_workflow_assigned_by + DmsfMailer.workflow_notification( + @workflow.participiants.collect{ |user| user.mail} << owner.mail, + @workflow, + revision, + "Approval workflow #{@workflow.name} rejected", + "been finished and the document has been rejected because of '#{action.note}'", + 'To see the approval history click on the workflow status of the document in').deliver + end + else + if action.action == DmsfWorkflowStepAction::ACTION_DELEGATE + # Delegation + # TODO: Find the real delegate + delegate = User.current + DmsfMailer.workflow_notification( + delegate.mail, + @workflow, + revision, + "Approval workflow #{@workflow.name} step delegated", + "been delegated because of '#{action.note}' and you are expected to do an approval in the current approval step", + 'To proceed click on the check box icon next to the document in').deliver + else + # Next step + assignments = @workflow.next_assignments revision.id + unless assignments.empty? + if assignments.first.dmsf_workflow_step.step != action.dmsf_workflow_step_assignment.dmsf_workflow_step.step + # Next step + DmsfMailer.workflow_notification( + assignments.collect{ |assignment| assignment.user.mail}, + @workflow, + revision, + "Approval workflow #{@workflow.name} requires your approval", + 'finished one of the approval steps and you are expected to do an approval in the next approval step', + 'To proceed click on the check box icon next to the document in the').deliver + owner = User.find_by_id revision.dmsf_workflow_assigned_by + DmsfMailer.workflow_notification( + owner.mail, + @workflow, + revision, + "Approval workflow #{@workflow.name} updated", + 'finished one of the approval steps', + 'To see the current status of the approval workflow click on the workflow status the document in').deliver + end + end + end + end end flash[:notice] = l(:notice_successful_update) else @@ -190,11 +249,19 @@ class DmsfWorkflowsController < ApplicationController def start revision = DmsfFileRevision.find_by_id(params[:dmsf_file_revision_id]) - if revision - revision.set_workflow(@workflow.id, params[:action]) - if request.post? - if revision.save - flash[:notice] = l(:notice_successful_update) + if revision + if request.post? + revision.set_workflow(@workflow.id, params[:action]) + if revision.save + assignments = @workflow.next_assignments revision.id + DmsfMailer.workflow_notification( + assignments.collect{ |assignment| assignment.user.mail}, + @workflow, + revision, + "Approval workflow #{@workflow.name} started", + 'been started and you are expected to do an approval in the current approval step', + 'To proceed click on the check box icon next to the document in').deliver + flash[:notice] = l(:notice_workflow_started) else flash[:error] = l(:notice_cannot_start_workflow) end @@ -202,9 +269,8 @@ class DmsfWorkflowsController < ApplicationController end redirect_to :back end - - private - + +private def find_workflow @workflow = DmsfWorkflow.find_by_id(params[:id]) end diff --git a/app/helpers/dmsf_workflows_helper.rb b/app/helpers/dmsf_workflows_helper.rb index 896dfbe0..7181c5fb 100644 --- a/app/helpers/dmsf_workflows_helper.rb +++ b/app/helpers/dmsf_workflows_helper.rb @@ -49,7 +49,7 @@ module DmsfWorkflowsHelper def dmsf_workflows_for_select(project, dmsf_workflow_id) options = Array.new - options << [l(:option_workflow_none), nil] + #options << [l(:option_workflow_none), nil] DmsfWorkflow.where(['project_id = ? OR project_id IS NULL', project.id]).each do |wf| options << [wf.name, wf.id] end @@ -59,7 +59,7 @@ module DmsfWorkflowsHelper def principals_radio_button_tags(name, principals) s = '' principals.each do |principal| - s << "\n" + s << "\n" end s.html_safe end diff --git a/app/models/dmsf_mailer.rb b/app/models/dmsf_mailer.rb index 3528acd3..502aae56 100644 --- a/app/models/dmsf_mailer.rb +++ b/app/models/dmsf_mailer.rb @@ -57,6 +57,20 @@ class DmsfMailer < Mailer mail(:to => email_to, :cc => email_cc, :subject => email_subject, :from => user.mail) end + def workflow_notification(to, workflow, revision, subject, text1, text2) + set_language_if_valid User.current.language + @workflow = workflow + @revision = revision + @text1 = "The approval workflow '#{@workflow.name}' assigned to '#{@revision.file.name}' document has just #{text1}." + unless @revision.folder + url = url_for(:controller => 'dmsf', :action => 'edit_root', :id => @revision.project, :only_path => false) + else + url = url_for(:controller => 'dmsf', :action => 'edit', :id => @project, :folder_id => @revision.folder, :only_path => false) + end + @text2 = "#{text2} #{url}." + mail :to => to, :subject => subject + end + private def get_notify_user_emails(user, files) @@ -93,5 +107,5 @@ class DmsfMailer < Mailer notify_members.collect {|m| m.user.mail } end - + end diff --git a/app/models/dmsf_workflow.rb b/app/models/dmsf_workflow.rb index 12f149cc..b3846638 100644 --- a/app/models/dmsf_workflow.rb +++ b/app/models/dmsf_workflow.rb @@ -28,6 +28,14 @@ class DmsfWorkflow < ActiveRecord::Base STATE_WAITING_FOR_APPROVAL = 1 STATE_APPROVED = 2 STATE_REJECTED = 4 + + def participiants + users = Array.new + self.dmsf_workflow_steps.each do |step| + users << step.user + end + users + end def self.workflows(project) project ? where(:project_id => project) : where('project_id IS NULL') @@ -110,7 +118,7 @@ class DmsfWorkflow < ActiveRecord::Base def next_assignments(dmsf_file_revision_id) results = Array.new self.dmsf_workflow_steps.each do |step| - break unless results.empty? || results[0].step.step == step.step + break unless results.empty? || results[0].dmsf_workflow_step.step == step.step step.dmsf_workflow_step_assignments.each do |assignment| if assignment.dmsf_file_revision_id == dmsf_file_revision_id if assignment.dmsf_workflow_step_actions.empty? @@ -134,14 +142,11 @@ class DmsfWorkflow < ActiveRecord::Base def self.assignments_to_users_str(assignments) str = '' if assignments - assignments.each_with_index do |assignment, index| - user = User.find_by_id assignment.user_id - if user - if index > 0 - str << ', ' - end - str << user.name + assignments.each_with_index do |assignment, index| + if index > 0 + str << ', ' end + str << assignment.user.name end end str @@ -153,33 +158,38 @@ class DmsfWorkflow < ActiveRecord::Base end end - def try_finish(dmsf_file_revision_id, action, user_id) - revision = DmsfFileRevision.find_by_id dmsf_file_revision_id - if revision - case action.action - when DmsfWorkflowStepAction::ACTION_APPROVE - self.dmsf_workflow_steps.each do |step| - step.dmsf_workflow_step_assignments.each do |assignment| - if assignment.dmsf_file_revision_id == dmsf_file_revision_id.to_i - if assignment.dmsf_workflow_step_actions.empty? - return false - end - assignment.dmsf_workflow_step_actions.each do |act| - return false unless act.is_finished? - end - end - end - end - revision.update_attribute(:workflow, DmsfWorkflow::STATE_APPROVED) - return true - when DmsfWorkflowStepAction::ACTION_REJECT - revision.update_attribute(:workflow, DmsfWorkflow::STATE_REJECTED) - return true - when DmsfWorkflowStepAction::ACTION_DELEGATE - assignment = DmsfWorkflowStepAssignment.find_by_id(action.dmsf_workflow_step_assignment_id) - assignment.update_attribute(:user_id, user_id) if assignment - end + def try_finish(revision, action, user_id) + case action.action + when DmsfWorkflowStepAction::ACTION_APPROVE + self.dmsf_workflow_steps.each do |step| + step.dmsf_workflow_step_assignments.each do |assignment| + if assignment.dmsf_file_revision_id == revision.id + if assignment.dmsf_workflow_step_actions.empty? + return false + end + assignment.dmsf_workflow_step_actions.each do |act| + return false unless act.is_finished? + end + end + end + end + revision.update_attribute(:workflow, DmsfWorkflow::STATE_APPROVED) + return true + when DmsfWorkflowStepAction::ACTION_REJECT + revision.update_attribute(:workflow, DmsfWorkflow::STATE_REJECTED) + return true + when DmsfWorkflowStepAction::ACTION_DELEGATE + #assignment = DmsfWorkflowStepAssignment.find_by_id(action.dmsf_workflow_step_assignment_id) + #assignment.update_attribute(:user_id, user_id) if assignment + self.dmsf_workflow_steps.each do |step| + step.dmsf_workflow_step_assignments.each do |assignment| + if assignment.id == action.dmsf_workflow_step_assignment_id + assignment.update_attribute(:user_id, user_id) + return false + end + end + end end return false - end + end end \ No newline at end of file diff --git a/app/models/dmsf_workflow_step_assignment.rb b/app/models/dmsf_workflow_step_assignment.rb index 1a86ed6c..84b76549 100644 --- a/app/models/dmsf_workflow_step_assignment.rb +++ b/app/models/dmsf_workflow_step_assignment.rb @@ -18,14 +18,9 @@ class DmsfWorkflowStepAssignment < ActiveRecord::Base belongs_to :dmsf_workflow_step - + belongs_to :user has_many :dmsf_workflow_step_actions, :dependent => :destroy - validates :dmsf_workflow_step_id, :presence => true validates :dmsf_file_revision_id, :presence => true validates_uniqueness_of :dmsf_workflow_step_id, :scope => [:dmsf_file_revision_id] - - def step - DmsfWorkflowStep.find_by_id self.dmsf_workflow_step_id - end end \ No newline at end of file diff --git a/app/views/dmsf_mailer/workflow_notification.html.erb b/app/views/dmsf_mailer/workflow_notification.html.erb new file mode 100644 index 00000000..9741cca8 --- /dev/null +++ b/app/views/dmsf_mailer/workflow_notification.html.erb @@ -0,0 +1,15 @@ + + + + + + +

Dear user,

+

+ <%= @text1 %> +

+

+ <%= @text2 %> +

+ + \ No newline at end of file diff --git a/app/views/dmsf_mailer/workflow_notification.text.erb b/app/views/dmsf_mailer/workflow_notification.text.erb new file mode 100644 index 00000000..370fbd1d --- /dev/null +++ b/app/views/dmsf_mailer/workflow_notification.text.erb @@ -0,0 +1,4 @@ +Dear user, +<%= @text1 %> + +<%= @text2 %> diff --git a/config/locales/en.yml b/config/locales/en.yml index fd1423ad..79843539 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -241,4 +241,5 @@ en: dmsf_new_step: New step message_dmsf_wokflow_note: Your note... info_revision: "r%{rev}" - link_workflow: Workflow \ No newline at end of file + link_workflow: Workflow + notice_workflow_started: 'Approval workflow successfully started' \ No newline at end of file diff --git a/lib/tasks/dmsf_alert_approvals.rake b/lib/tasks/dmsf_alert_approvals.rake new file mode 100644 index 00000000..be038c22 --- /dev/null +++ b/lib/tasks/dmsf_alert_approvals.rake @@ -0,0 +1,54 @@ +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2013 Karel Picman +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +desc <<-END_DESC +Alert all users who are expected to do an approval in the current approval steps + +Available options: + +Example: + rake redmine:dmsf_alert_approvals RAILS_ENV="production" +END_DESC +require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment") + +class DmsfAlertApprovals + + def self.alert + revisions = DmsfFileRevision.where(:workflow => 1) + revisions.each do |revision| + next unless revision.file.last_revision == revision + workflow = DmsfWorkflow.find_by_id revision.dmsf_workflow_id + next unless workflow + assignments = workflow.next_assignments revision.id + DmsfMailer.workflow_notification( + assignments.collect{|a| a.user.mail}, + workflow, + revision, + "Approval workflow #{workflow.name} requires your approval", + 'finished one of the approval steps and you are expected to do an approval in the next approval step', + 'To proceed click on the check box icon next to the document in the').deliver + Rails.logger.info "#{assignments.collect{|a| a.user.login}.join(',')} were alerted in order to do an approval of [workflow = #{workflow.id}, revision = #{revision.id}]" + end + end +end + +namespace :redmine do + task :dmsf_alert_approvals => :environment do + DmsfAlertApprovals.alert + end +end diff --git a/test/unit/dmsf_workflow_step_assignment_test.rb b/test/unit/dmsf_workflow_step_assignment_test.rb index e95e4ec4..8faa8d5e 100644 --- a/test/unit/dmsf_workflow_step_assignment_test.rb +++ b/test/unit/dmsf_workflow_step_assignment_test.rb @@ -58,8 +58,4 @@ class WorkflowStepAssignmentTest < RedmineDmsf::Test::UnitTest assert_nil DmsfWorkflowStepAssignment.find_by_id(1) assert_nil DmsfWorkflowStepAction.find_by_id(1) end - - def test_step - assert_equal @wfsa1.step, DmsfWorkflowStep.find_by_id(@wfsa1.dmsf_workflow_step_id) - end end diff --git a/test/unit/dmsf_workflow_test.rb b/test/unit/dmsf_workflow_test.rb index 58de2b13..457d346e 100644 --- a/test/unit/dmsf_workflow_test.rb +++ b/test/unit/dmsf_workflow_test.rb @@ -175,4 +175,9 @@ class DmsfWorkflowTest < RedmineDmsf::Test::UnitTest # assert_equal @revision2.workflow, DmsfWorkflow::STATE_WAITING_FOR_APPROVAL assert true end + + def test_participiants + # TODO: + assert true + end end