545 lines
19 KiB
Ruby
545 lines
19 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# Redmine plugin for Document Management System "Features"
|
|
#
|
|
# Karel Pičman <karel.picman@kontron.com>
|
|
#
|
|
# This file is part of Redmine DMSF plugin.
|
|
#
|
|
# Redmine DMSF plugin 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 3 of the License, or (at your option) any
|
|
# later version.
|
|
#
|
|
# Redmine DMSF plugin 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 Redmine DMSF plugin. If not, see
|
|
# <https://www.gnu.org/licenses/>.
|
|
|
|
# WorkflowsController
|
|
class DmsfWorkflowsController < ApplicationController
|
|
model_object DmsfWorkflow
|
|
menu_item :dmsf_approvalworkflows
|
|
self.main_menu = false
|
|
|
|
before_action :find_model_object, except: %i[create new index assign assignment]
|
|
before_action :find_project
|
|
before_action :authorize_custom
|
|
before_action :permissions?, only: %i[new_action assignment start]
|
|
before_action :approver_candidates, only: %i[remove_step show reorder_steps add_step]
|
|
before_action :prevent_from_editing, only: %i[destroy remove_step update add_step update_step reorder_steps]
|
|
|
|
layout :workflows_layout
|
|
|
|
helper :dmsf
|
|
|
|
def permissions?
|
|
revision = DmsfFileRevision.find_by(id: params[:dmsf_file_revision_id]) if params[:dmsf_file_revision_id].present?
|
|
render_403 unless revision&.dmsf_file || DmsfFolder.permissions?(revision&.dmsf_file&.dmsf_folder)
|
|
true
|
|
end
|
|
|
|
def initialize
|
|
@dmsf_workflow = nil
|
|
@project = nil
|
|
super
|
|
end
|
|
|
|
def index
|
|
@status = params[:status] || 1
|
|
@workflow_pages, @workflows = paginate(DmsfWorkflow.status(@status).global.sorted, per_page: 25)
|
|
@path = dmsf_workflows_path
|
|
end
|
|
|
|
def action
|
|
# Noting to do
|
|
end
|
|
|
|
def show
|
|
# Noting to do
|
|
end
|
|
|
|
def new_action
|
|
unless params[:commit] == l(:button_submit) && request.post?
|
|
redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder)
|
|
return
|
|
end
|
|
|
|
action = DmsfWorkflowStepAction.new(
|
|
dmsf_workflow_step_assignment_id: params[:dmsf_workflow_step_assignment_id],
|
|
action: params[:step_action].to_i >= 10 ? DmsfWorkflowStepAction::ACTION_DELEGATE : params[:step_action],
|
|
note: params[:note]
|
|
)
|
|
revision = DmsfFileRevision.find_by(id: params[:dmsf_file_revision_id])
|
|
result = call_hook(:dmsf_workflow_controller_before_approval,
|
|
{ dmsf_file_revision: revision, step_action: params[:step_action] })
|
|
if (result.blank? || result.first) && action.save
|
|
if revision
|
|
if @dmsf_workflow.try_finish? revision, action, (params[:step_action].to_i / 10)
|
|
if revision.dmsf_file
|
|
begin
|
|
revision.dmsf_file.unlock!(force_file_unlock_allowed: true) unless RedmineDmsf.dmsf_keep_documents_locked?
|
|
rescue DmsfLockError => e
|
|
flash[:info] = e.message
|
|
end
|
|
end
|
|
if revision.workflow == DmsfWorkflow::STATE_APPROVED
|
|
# Just approved
|
|
if Setting.notified_events.include?('dmsf_workflow_plural')
|
|
recipients = DmsfMailer.get_notify_users(@project, revision.dmsf_file, force_notification: true)
|
|
DmsfMailer.deliver_workflow_notification(
|
|
recipients,
|
|
@dmsf_workflow,
|
|
revision,
|
|
:text_email_subject_approved,
|
|
:text_email_finished_approved,
|
|
:text_email_to_see_history
|
|
)
|
|
if RedmineDmsf.dmsf_display_notified_recipients? && recipients.present?
|
|
max_notifications = RedmineDmsf.dmsf_max_notification_receivers_info
|
|
to = recipients.collect(&:name).first(max_notifications).join(', ')
|
|
if to.present?
|
|
to << (recipients.count > max_notifications ? ',...' : '.')
|
|
flash[:warning] = l(:warning_email_notifications, to: to)
|
|
end
|
|
end
|
|
end
|
|
elsif Setting.notified_events.include?('dmsf_workflow_plural') # Just rejected
|
|
recipients = @dmsf_workflow.participiants
|
|
recipients.push revision.dmsf_workflow_assigned_by_user
|
|
recipients.uniq!
|
|
recipients &= DmsfMailer.get_notify_users(@project, revision.dmsf_file, force_notification: true)
|
|
DmsfMailer.deliver_workflow_notification(
|
|
recipients,
|
|
@dmsf_workflow,
|
|
revision,
|
|
:text_email_subject_rejected,
|
|
:text_email_finished_rejected,
|
|
:text_email_to_see_history,
|
|
action.note
|
|
)
|
|
if RedmineDmsf.dmsf_display_notified_recipients? && recipients.present?
|
|
max_notifications = RedmineDmsf.dmsf_max_notification_receivers_info
|
|
to = recipients.collect(&:name).first(max_notifications).join(', ')
|
|
if to.present?
|
|
to << (recipients.count > max_notifications ? ',...' : '.')
|
|
flash[:warning] = l(:warning_email_notifications, to: to)
|
|
end
|
|
end
|
|
end
|
|
elsif action.action == DmsfWorkflowStepAction::ACTION_DELEGATE
|
|
# Delegation
|
|
if Setting.notified_events.include?('dmsf_workflow_plural')
|
|
delegate = User.active.find_by(id: params[:step_action].to_i / 10)
|
|
if DmsfMailer.get_notify_users(@project, revision.dmsf_file, force_notification: true).include?(delegate)
|
|
DmsfMailer.deliver_workflow_notification(
|
|
[delegate],
|
|
@dmsf_workflow,
|
|
revision,
|
|
:text_email_subject_delegated,
|
|
:text_email_finished_delegated,
|
|
:text_email_to_proceed,
|
|
action.note,
|
|
action.dmsf_workflow_step_assignment.dmsf_workflow_step
|
|
)
|
|
if RedmineDmsf.dmsf_display_notified_recipients?
|
|
flash[:warning] = l(:warning_email_notifications, to: delegate.name)
|
|
end
|
|
end
|
|
end
|
|
else
|
|
# Next step
|
|
assignments = @dmsf_workflow.next_assignments revision.id
|
|
if assignments.any? &&
|
|
Setting.notified_events.include?('dmsf_workflow_plural') &&
|
|
assignments.first.dmsf_workflow_step.step != action.dmsf_workflow_step_assignment.dmsf_workflow_step.step
|
|
# Next step
|
|
assignments.each do |assignment|
|
|
next if assignment.user.nil? ||
|
|
DmsfMailer.get_notify_users(@project, revision.dmsf_file, force_notification: true)
|
|
.exclude?(assignment.user)
|
|
|
|
DmsfMailer.deliver_workflow_notification(
|
|
[assignment.user],
|
|
@dmsf_workflow,
|
|
revision,
|
|
:text_email_subject_requires_approval,
|
|
:text_email_finished_step,
|
|
:text_email_to_proceed,
|
|
nil,
|
|
assignment.dmsf_workflow_step
|
|
)
|
|
end
|
|
to = revision.dmsf_workflow_assigned_by_user
|
|
if to && DmsfMailer.get_notify_users(@project, revision.dmsf_file, force_notification: true)
|
|
.include?(to)
|
|
DmsfMailer.deliver_workflow_notification(
|
|
[to],
|
|
@dmsf_workflow,
|
|
revision,
|
|
:text_email_subject_updated,
|
|
:text_email_finished_step_short,
|
|
:text_email_to_see_status
|
|
)
|
|
end
|
|
if RedmineDmsf.dmsf_display_notified_recipients?
|
|
recipients = assignments.collect(&:user)
|
|
recipients << to if to
|
|
recipients.uniq!
|
|
recipients &= DmsfMailer.get_notify_users(@project, revision.dmsf_file, force_notification: true)
|
|
unless recipients.empty?
|
|
max_notifications = RedmineDmsf.dmsf_max_notification_receivers_info
|
|
to = recipients.collect(&:name).first(max_notifications).join(', ')
|
|
if to.present?
|
|
to << (recipients.count > max_notifications ? ',...' : '.')
|
|
flash[:warning] = l(:warning_email_notifications, to: to)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
flash[:notice] = l(:notice_successful_update)
|
|
elsif action.action != DmsfWorkflowStepAction::ACTION_APPROVE && action.note.blank?
|
|
flash[:error] = l(:error_empty_note)
|
|
end
|
|
redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder)
|
|
end
|
|
|
|
def assign; end
|
|
|
|
def assignment
|
|
if params[:commit] == l(:button_submit) && params[:dmsf_workflow_id].present? && params[:dmsf_workflow_id] != '-1'
|
|
# DMS file
|
|
if params[:dmsf_file_revision_id].present? && params[:dmsf_link_id].blank? && params[:attachment_id].blank?
|
|
revision = DmsfFileRevision.find_by(id: params[:dmsf_file_revision_id])
|
|
begin
|
|
if revision
|
|
@project ||= revision.dmsf_file.project
|
|
revision.set_workflow(params[:dmsf_workflow_id], params[:action])
|
|
revision.assign_workflow(params[:dmsf_workflow_id])
|
|
if request.post?
|
|
if revision.save
|
|
file = DmsfFile.find_by(id: revision.dmsf_file_id)
|
|
if file
|
|
begin
|
|
file.lock!
|
|
rescue DmsfLockError => e
|
|
Rails.logger.warn e.message
|
|
end
|
|
flash[:notice] = l(:notice_successful_update)
|
|
end
|
|
else
|
|
flash[:error] = l(:error_workflow_assign)
|
|
end
|
|
end
|
|
end
|
|
rescue StandardError => e
|
|
flash[:error] = e.message
|
|
end
|
|
# DMS link (attached)
|
|
elsif params[:dmsf_link_id].present?
|
|
@dmsf_link_id = params[:dmsf_link_id]
|
|
@dmsf_workflow_id = params[:dmsf_workflow_id]
|
|
# Attachment (attached)
|
|
elsif params[:attachment_id].present?
|
|
@attachment_id = params[:attachment_id]
|
|
@dmsf_workflow_id = params[:dmsf_workflow_id]
|
|
end
|
|
end
|
|
respond_to do |format|
|
|
format.html { redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder) }
|
|
format.js
|
|
end
|
|
end
|
|
|
|
def log
|
|
if params[:dmsf_file_revision_id].present?
|
|
@revision = DmsfFileRevision.find_by(id: params[:dmsf_file_revision_id])
|
|
elsif params[:dmsf_file_id].present?
|
|
dmsf_file = DmsfFile.find_by(id: params[:dmsf_file_id])
|
|
@revision = dmsf_file.last_revision if dmsf_file
|
|
elsif params[:dmsf_link_id].present?
|
|
dmsf_link = DmsfLink.find_by(id: params[:dmsf_link_id])
|
|
if dmsf_link
|
|
dmsf_file = dmsf_link.target_file
|
|
@revision = dmsf_file.last_revision
|
|
end
|
|
end
|
|
respond_to do |format|
|
|
format.html
|
|
format.js
|
|
end
|
|
end
|
|
|
|
def new
|
|
@dmsf_workflow = DmsfWorkflow.new
|
|
# Reload
|
|
if params[:dmsf_workflow] && params[:dmsf_workflow][:name].present?
|
|
@dmsf_workflow.name = params[:dmsf_workflow][:name]
|
|
elsif params[:dmsf_workflow] && params[:dmsf_workflow][:id].present?
|
|
names = DmsfWorkflow.where(id: params[:dmsf_workflow][:id]).pluck(:name)
|
|
@dmsf_workflow.name = names.first
|
|
end
|
|
render layout: !request.xhr?
|
|
end
|
|
|
|
def edit
|
|
redirect_to dmsf_workflow_path(@dmsf_workflow)
|
|
end
|
|
|
|
def create
|
|
if params[:dmsf_workflow]
|
|
if params[:dmsf_workflow][:id].to_i.positive?
|
|
wf = DmsfWorkflow.find_by(id: params[:dmsf_workflow][:id])
|
|
@dmsf_workflow = wf.copy_to(@project, params[:dmsf_workflow][:name]) if wf
|
|
else
|
|
@dmsf_workflow = DmsfWorkflow.new
|
|
@dmsf_workflow.name = params[:dmsf_workflow][:name]
|
|
@dmsf_workflow.project_id = @project.id if @project
|
|
@dmsf_workflow.author = User.current
|
|
@dmsf_workflow.save
|
|
end
|
|
end
|
|
if request.post? && @dmsf_workflow&.valid?
|
|
flash[:notice] = l(:notice_successful_create)
|
|
if @project
|
|
redirect_to settings_project_path(@project, tab: 'dmsf_workflow')
|
|
else
|
|
redirect_to dmsf_workflows_path
|
|
end
|
|
else
|
|
render action: 'new'
|
|
end
|
|
end
|
|
|
|
def update
|
|
if params[:dmsf_workflow]
|
|
res = @dmsf_workflow.update(name: params[:dmsf_workflow][:name]) if params[:dmsf_workflow][:name].present?
|
|
res = @dmsf_workflow.update(status: params[:dmsf_workflow][:status]) if params[:dmsf_workflow][:status].present?
|
|
if res
|
|
flash[:notice] = l(:notice_successful_update)
|
|
if @project
|
|
redirect_to settings_project_path(@project, tab: 'dmsf_workflow')
|
|
else
|
|
redirect_to dmsf_workflows_path
|
|
end
|
|
else
|
|
flash[:error] = @dmsf_workflow.errors.full_messages.to_sentence
|
|
redirect_to dmsf_workflow_path(@dmsf_workflow)
|
|
end
|
|
else
|
|
redirect_to dmsf_workflow_path(@dmsf_workflow)
|
|
end
|
|
end
|
|
|
|
def destroy
|
|
begin
|
|
@dmsf_workflow.destroy
|
|
flash[:notice] = l(:notice_successful_delete)
|
|
rescue StandardError
|
|
flash[:error] = l(:error_unable_delete_dmsf_workflow)
|
|
end
|
|
if @project
|
|
redirect_to settings_project_path(@project, tab: 'dmsf_workflow')
|
|
else
|
|
redirect_to dmsf_workflows_path
|
|
end
|
|
end
|
|
|
|
def autocomplete_for_user
|
|
respond_to do |format|
|
|
format.js
|
|
end
|
|
end
|
|
|
|
def new_step
|
|
@steps = @dmsf_workflow.dmsf_workflow_steps.select('step, MAX(name) AS name').group(:step)
|
|
respond_to do |format|
|
|
format.html
|
|
format.js
|
|
end
|
|
end
|
|
|
|
def add_step
|
|
if request.post?
|
|
step = if params[:step] == '0'
|
|
@dmsf_workflow.dmsf_workflow_steps.collect(&:step).uniq.count + 1
|
|
else
|
|
params[:step].to_i
|
|
end
|
|
operator = params[:commit] == l(:dmsf_and) ? DmsfWorkflowStep::OPERATOR_AND : DmsfWorkflowStep::OPERATOR_OR
|
|
user_ids = User.where(id: params[:user_ids]).ids
|
|
if user_ids.any?
|
|
user_ids.each do |user_id|
|
|
ws = DmsfWorkflowStep.new
|
|
ws.dmsf_workflow_id = @dmsf_workflow.id
|
|
ws.step = step
|
|
ws.user_id = user_id
|
|
ws.operator = operator
|
|
ws.name = params[:name]
|
|
if ws.save
|
|
@dmsf_workflow.dmsf_workflow_steps << ws
|
|
else
|
|
flash[:error] = ws.errors.full_messages.to_sentence
|
|
end
|
|
end
|
|
else
|
|
flash[:error] = l(:error_workflow_assign)
|
|
end
|
|
end
|
|
respond_to do |format|
|
|
format.html
|
|
end
|
|
end
|
|
|
|
def remove_step
|
|
if request.delete?
|
|
DmsfWorkflowStep.where(dmsf_workflow_id: @dmsf_workflow.id, step: params[:step]).find_each do |ws|
|
|
@dmsf_workflow.dmsf_workflow_steps.delete ws
|
|
end
|
|
@dmsf_workflow.dmsf_workflow_steps.each do |ws|
|
|
n = ws.step.to_i
|
|
next unless n > params[:step].to_i
|
|
|
|
ws.step = n - 1
|
|
flash[:error] = l(:notice_cannot_renumber_steps) unless ws.save
|
|
end
|
|
end
|
|
redirect_back_or_default dmsf_workflow_path(@dmsf_workflow)
|
|
end
|
|
|
|
def reorder_steps
|
|
if request.put?
|
|
if @assigned
|
|
flash[:error] = l(:error_dmsf_workflow_assigned)
|
|
else
|
|
@dmsf_workflow.reorder_steps params[:step].to_i, params[:dmsf_workflow][:position].to_i
|
|
end
|
|
end
|
|
respond_to do |format|
|
|
format.html
|
|
format.js
|
|
end
|
|
end
|
|
|
|
def start
|
|
revision = DmsfFileRevision.find_by(id: params[:dmsf_file_revision_id])
|
|
if revision
|
|
revision.set_workflow(@dmsf_workflow.id, params[:action])
|
|
if revision.save
|
|
if Setting.notified_events.include?('dmsf_workflow_plural')
|
|
@dmsf_workflow.notify_users(@project, revision, self)
|
|
end
|
|
flash[:notice] = l(:notice_workflow_started)
|
|
else
|
|
flash[:error] = l(:notice_cannot_start_workflow)
|
|
end
|
|
end
|
|
redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder)
|
|
end
|
|
|
|
def update_step
|
|
# Name
|
|
if params[:dmsf_workflow].present?
|
|
index = params[:step].to_i
|
|
step = @dmsf_workflow.dmsf_workflow_steps[index]
|
|
step.name = params[:dmsf_workflow][:step_name]
|
|
if step.save
|
|
@dmsf_workflow.dmsf_workflow_steps.where(step: step.step).find_each do |s|
|
|
s.name = step.name
|
|
flash[:error] = s.errors.full_messages.to_sentence unless s.save
|
|
end
|
|
else
|
|
flash[:error] = step.errors.full_messages.to_sentence
|
|
end
|
|
end
|
|
# Operators/Assignees
|
|
if params[:operator_step].present?
|
|
params[:operator_step].each do |id, operator|
|
|
step = DmsfWorkflowStep.find_by(id: id)
|
|
next unless step
|
|
|
|
step.operator = operator.to_i
|
|
step.user_id = params[:assignee][id]
|
|
next if step.save
|
|
|
|
flash[:error] = step.errors.full_messages.to_sentence
|
|
Rails.logger.error step.errors.full_messages.to_sentence
|
|
end
|
|
end
|
|
redirect_to dmsf_workflow_path(@dmsf_workflow)
|
|
end
|
|
|
|
def delete_step
|
|
step = DmsfWorkflowStep.find_by(id: params[:step])
|
|
if step
|
|
# Safe the name
|
|
if step.name.present?
|
|
@dmsf_workflow.dmsf_workflow_steps.each do |s|
|
|
if s.step == step.step
|
|
s.name = step.name
|
|
s.save!
|
|
end
|
|
end
|
|
end
|
|
# Destroy
|
|
step.destroy
|
|
end
|
|
redirect_to dmsf_workflow_path(@dmsf_workflow)
|
|
end
|
|
|
|
private
|
|
|
|
def find_project
|
|
if @dmsf_workflow
|
|
if @dmsf_workflow.project # Project workflow
|
|
@project = @dmsf_workflow.project
|
|
elsif params[:dmsf_file_revision_id].present? # Global workflow
|
|
revision = DmsfFileRevision.find_by(id: params[:dmsf_file_revision_id])
|
|
@project = revision.dmsf_file.project if revision&.dmsf_file
|
|
elsif params[:project_id].present?
|
|
@project = Project.find params[:project_id]
|
|
end
|
|
elsif params[:dmsf_workflow].present?
|
|
@project = Project.find params[:dmsf_workflow][:project_id]
|
|
elsif params[:project_id].present?
|
|
@project = Project.find params[:project_id]
|
|
elsif params[:id].present?
|
|
@project = Project.find(params[:id])
|
|
end
|
|
rescue ActiveRecord::RecordNotFound
|
|
@project = nil
|
|
end
|
|
|
|
def workflows_layout
|
|
@project ? 'base' : 'admin'
|
|
end
|
|
|
|
def authorize_custom
|
|
if @project
|
|
authorize
|
|
else
|
|
require_admin
|
|
end
|
|
end
|
|
|
|
def approver_candidates
|
|
@approving_candidates = @project ? @project.users.to_a : User.active.to_a
|
|
end
|
|
|
|
def prevent_from_editing
|
|
# A workflow in use can be neither edited nor deleted
|
|
@assigned = DmsfFileRevision.exists?(dmsf_workflow_id: @dmsf_workflow.id)
|
|
return unless @assigned && !request.put?
|
|
|
|
flash[:error] = l(:error_dmsf_workflow_assigned)
|
|
if @project
|
|
redirect_back_or_default settings_project_path(@project, tab: 'dmsf_workflow')
|
|
else
|
|
redirect_back_or_default dmsf_workflows_path
|
|
end
|
|
end
|
|
end
|