Approval workflows copied fron 1.5.0 devel

This commit is contained in:
Karel Picman 2013-05-09 12:17:12 +02:00
parent c2534d97c5
commit 0b40abb821
40 changed files with 1217 additions and 3 deletions

View File

@ -0,0 +1,171 @@
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2013 Karel Picman <karel.picman@kontron.com>
#
# 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.
class DmsfWorkflowsController < ApplicationController
unloadable
layout :workflows_layout
before_filter :find_workflow, :except => [:create, :new, :index]
before_filter :find_project
before_filter :authorize_global, :except => [:action, :new_action]
def index
if @project
@workflow_pages, @workflows = paginate DmsfWorkflow.where(:project_id => @project.id), :per_page => 25
else
@workflow_pages, @workflows = paginate DmsfWorkflow.where(:project_id => nil), :per_page => 25
end
end
def action
end
def new_action
logger.info '>>>>>>>>>>>>>>>>>>>>>>> YES!'
end
def log
end
def new
@workflow = DmsfWorkflow.new
end
def create
@workflow = DmsfWorkflow.new(:name => params[:dmsf_workflow][:name], :project_id => params[:project_id])
if request.post? && @workflow.save
flash[:notice] = l(:notice_successful_create)
if @project
redirect_to settings_project_path(@project, :tab => 'dmsf')
else
redirect_to dmsf_workflows_path
end
else
render :action => 'new'
end
end
def edit
end
def update
if request.put? && @workflow.update_attributes({:name => params[:dmsf_workflow][:name]})
flash[:notice] = l(:notice_successful_update)
if @project
redirect_to settings_project_path(@project, :tab => 'dmsf')
else
redirect_to dmsf_workflows_path
end
else
render :action => 'edit'
end
end
def destroy
begin
@workflow.destroy
rescue
flash[:error] = l(:error_unable_delete_dmsf_workflow)
end
if @project
redirect_to settings_project_path(@project, :tab => 'dmsf')
else
redirect_to dmsf_workflows_path
end
end
def autocomplete_for_user
respond_to do |format|
format.js
end
end
def add_step
if request.post?
users = User.find_all_by_id(params[:user_ids])
if params[:step] == '0'
if @workflow.steps.count > 0
step = @workflow.steps.last + 1
else
step = 1
end
else
step = params[:step].to_i
end
operator = 1 ? params[:commit] == l(:dmsf_and) : 0
users.each do |user|
@workflow.dmsf_workflow_steps << DmsfWorkflowStep.new(
:dmsf_workflow_id => @workflow.id,
:step => step,
:user_id => user.id,
:operator => operator)
end
@workflow.save
end
respond_to do |format|
format.html
end
end
def remove_step
if request.delete?
DmsfWorkflowStep.where(:dmsf_workflow_id => @workflow.id, :step => params[:step]).each do |ws|
@workflow.dmsf_workflow_steps.delete(ws)
end
@workflow.dmsf_workflow_steps.each do |ws|
n = ws.step.to_i
if n > params[:step].to_i
ws.step = n - 1
ws.save
end
end
end
respond_to do |format|
format.html
end
end
def reorder_steps
if request.put?
@workflow.reorder_steps params[:step].to_i, params[:workflow_step][:move_to]
end
respond_to do |format|
format.html
end
end
private
def find_workflow
@workflow = DmsfWorkflow.find_by_id(params[:id])
end
def find_project
if @workflow
@project = @workflow.project
elsif params[:project_id].present?
@project = Project.find_by_id params[:project_id]
end
end
def workflows_layout
find_workflow
find_project
@project ? 'base' : 'admin'
end
end

View File

@ -0,0 +1,45 @@
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2013 Karel Picman <karel.picman@kontron.com>
#
# 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.
module DmsfWorkflowsHelper
def render_principals_for_new_dmsf_workflow_users(workflow)
scope = User.active.sorted.like(params[:q])
principal_count = scope.count
principal_pages = Redmine::Pagination::Paginator.new principal_count, 100, params['page']
principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).all
s = content_tag('div', principals_check_box_tags('user_ids[]', principals), :id => 'principals')
links = pagination_links_full(principal_pages, principal_count, :per_page_links => false) {|text, parameters, options|
link_to text, autocomplete_for_user_dmsf_workflow_path(workflow, parameters.merge(:q => params[:q], :format => 'js')), :remote => true
}
s + content_tag('p', links, :class => 'pagination')
end
def dmsf_workflow_steps_options_for_select(steps)
options = Array.new
options << [l(:dmsf_new_step), 0]
steps.each do |step|
options << [step.to_s, step]
end
options_for_select(options, 0)
end
end

110
app/models/dmsf_workflow.rb Normal file
View File

@ -0,0 +1,110 @@
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2013 Karel Picman <karel.picman@kontron.com>
#
# 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.
class DmsfWorkflow < ActiveRecord::Base
belongs_to :project
has_many :dmsf_workflow_steps, :dependent => :destroy
validates_uniqueness_of :name
validates :name, :presence => true
validates_length_of :name, :maximum => 255
def self.workflows(project)
project ? where(:project_id => project) : where('project_id IS NULL')
end
def project
@project = Project.find_by_id(project_id) unless @project
@project
end
def to_s
name
end
def approvals(step)
wa = Array.new
dmsf_workflow_steps.each do |s|
if s.step == step
wa << s
end
end
wa.sort_by { |obj| -obj.operator }
end
def steps
ws = Array.new
dmsf_workflow_steps.each do |s|
unless ws.include? s.step
ws << s.step
end
end
ws.sort
end
def reorder_steps(step, move_to)
case move_to
when 'highest'
unless step == 1
dmsf_workflow_steps.each do |ws|
if ws.step < step
ws.update_attribute('step', ws.step + 1)
elsif ws.step == step
ws.update_attribute('step', 1)
end
end
end
when 'higher'
unless step == 1
dmsf_workflow_steps.each do |ws|
if ws.step == step - 1
ws.update_attribute('step', step)
elsif ws.step == step
ws.update_attribute('step', step - 1)
end
end
end
when 'lower'
unless step == steps.count
dmsf_workflow_steps.each do |ws|
if ws.step == step + 1
ws.update_attribute('step', step)
elsif ws.step == step
ws.update_attribute('step', step + 1)
end
end
end
when 'lowest'
size = steps.count
unless step == size
dmsf_workflow_steps.each do |ws|
if ws.step > step
ws.update_attribute('step', ws.step - 1)
elsif ws.step == step
ws.update_attribute('step', size)
end
end
end
end
end
def delegates
User.all
end
end

View File

@ -0,0 +1,37 @@
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2013 Karel Picman <karel.picman@kontron.com>
#
# 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.
class DmsfWorkflowStep < ActiveRecord::Base
belongs_to :workflow
has_many :dmsf_workflow_step_assignments, :dependent => :destroy
validates :dmsf_workflow_id, :presence => true
validates :step, :presence => true
validates :user_id, :presence => true
validates :operator, :presence => true
validates_uniqueness_of :user_id, :scope => [:dmsf_workflow_id, :step]
def soperator
operator == 1 ? l(:dmsf_and) : l(:dmsf_or)
end
def user
User.find(user_id)
end
end

View File

@ -0,0 +1,24 @@
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2013 Karel Picman <karel.picman@kontron.com>
#
# 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.
class DmsfWorkflowStepAction < ActiveRecord::Base
belongs_to :dmsf_workflow_step_assignment
validates :dmsf_workflow_step_assignment_id, :presence => true
validates :action, :presence => true
end

View File

@ -0,0 +1,26 @@
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2013 Karel Picman <karel.picman@kontron.com>
#
# 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.
class DmsfWorkflowStepAssignment < ActiveRecord::Base
belongs_to :dmsf_workflow_step
has_many :dmsf_workflow_step_actions, :dependent => :destroy
validates :dmsf_workflow_step_id, :presence => true
validates :dmsf_file_revision_id, :presence => true
end

View File

@ -0,0 +1,33 @@
<h3 class="title"><%= l(:label_dmsf_workflow) %></h3>
<%= form_tag({:controller => 'dmsf_workflows',
:action => 'new_action',
:object_type => DmsfWorkflow,
:object_id => @workflow,
:project_id => @project},
:remote => true,
:method => :post,
:id => 'new-action-form') do %>
<p><label><%= radio_button_tag 'action', 'approve', true %> <%= l(:label_dmsf_wokflow_action_approve) %></label><br/></p>
<p>
<label><%= radio_button_tag 'action', 'reject' %> <%= l(:label_dmsf_wokflow_action_reject) %></label><br/>
<%= text_area_tag :note, '', :placeholder => l(:message_dmsf_wokflow_note), :size => '38x2' %>
</p>
<p>
<label><%= radio_button_tag 'action', 'delegate' %> <%= l(:label_dmsf_wokflow_action_delegate) %></label><br/>
<%= text_field_tag 'user_search', nil %>
</p>
<%= javascript_tag "observeSearchfield('user_search', 'users_for_delegate', '#{ escape_javascript autocomplete_for_user_dmsf_workflow_path(@workflow) }')" %>
<div id="users_for_delegate">
<%= principals_check_box_tags 'watcher[user_ids][]', @workflow.delegates %>
</div>
<p class="buttons">
<%= submit_tag l(:button_add), :name => nil, :onclick => "hideModal(this);" %>
<%= submit_tag l(:button_cancel), :name => nil, :onclick => "hideModal(this);", :type => 'button' %>
</p>
<% end %>

View File

@ -0,0 +1,32 @@
<% @workflows = DmsfWorkflow.where(:project_id => @project.id) if @project && @workflows.nil? %>
<div class="contextual">
<% if @project %>
<%= link_to l(:label_dmsf_workflow_new), new_dmsf_workflow_path(:project_id => @project.id), :class => 'icon icon-add' %>
<% else %>
<%= link_to l(:label_dmsf_workflow_new), new_dmsf_workflow_path, :class => 'icon icon-add' %>
<% end %>
</div>
<h2><%=l(:label_dmsf_workflow_plural)%></h2>
<table class="list">
<thead><tr>
<th><%=l(:field_name)%></th>
<th></th>
</tr></thead>
<tbody>
<% for workflow in @workflows %>
<tr id="workflow-<%= workflow.id %>" class="<%= cycle('odd', 'even') %>">
<td><%= link_to(h(workflow.name), edit_dmsf_workflow_path(workflow)) %></td>
<td class="buttons">
<%= delete_link dmsf_workflow_path(workflow) %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% if @workflow_pages %>
<p class="pagination"><%= pagination_links_full @workflow_pages %></p>
<% end %>

View File

@ -0,0 +1,81 @@
<% if @workflow.project %>
<h2><%= link_to l(:label_dmsf_workflow_plural), settings_project_path(@project, :tab => 'dmsf') %> &#187; <%=h @workflow %></h2>
<% else %>
<h2><%= link_to l(:label_dmsf_workflow_plural), dmsf_workflows_path %> &#187; <%=h @workflow %></h2>
<% end %>
<% if @project %>
<div class="contextual">
<%= link_to 'Action', action_dmsf_workflow_path(:project_id => @project.id, :id => @workflow, :step => 1), :remote => true %>
</div>
<% end %>
<%= labelled_form_for @workflow do |f| %>
<%= error_messages_for 'workflow' %>
<div class="box tabular">
<p><%= f.text_field :name, :required => true %><%= submit_tag l(:button_save) %></p>
</div>
<% end %>
<div class="tab-content" id="tab-content-members">
<div class="splitcontentleft">
<% if @workflow.steps.any? %>
<table class="list steps">
<thead><tr>
<th><%= l(:label_dmsf_workflow_step) %></th>
<th><%= l(:label_dmsf_workflow_approval_plural) %></th>
<th><%=l(:button_sort)%></th>
<th style="width:15%"></th>
</tr></thead>
<tbody>
<% @workflow.steps.each do |step| %>
<tr id="step-<%= step %>" class="<%= cycle 'odd', 'even' %> step">
<td class="step"><%= step %></td>
<td class="approval">
<% @workflow.approvals(step).each_with_index do |approval, i| %>
<% if i != 0 %>
<b><%= approval.soperator %></b>&nbsp
<% end %>
<%= link_to_user approval.user %>
<% end %>
</td>
<td align="center" style="width:15%;">
<%= reorder_links('workflow_step', {:action => 'edit', :id => @workflow, :step => step}, :put) %>
</td>
<td class="buttons">
<%= delete_link edit_dmsf_workflow_path(@workflow, :step => step) %>
</td>
</tr>
<% end; reset_cycle %>
</tbody>
</table>
<% else %>
<p class="nodata"><%= l(:label_no_data) %></p>
<% end %>
</div>
<div class="splitcontentright">
<%= form_for(@workflow, :url => edit_dmsf_workflow_path(@workflow),
:html => {:method => :post}) do |f| %>
<fieldset><legend><%=l(:label_user_new)%></legend>
<p><%= label_tag 'user_search', l(:label_user_search) %><%= text_field_tag 'user_search', nil %></p>
<%= javascript_tag "observeSearchfield('user_search', null, '#{ escape_javascript autocomplete_for_user_dmsf_workflow_path(@workflow) }')" %>
<div id="users">
<%= render_principals_for_new_dmsf_workflow_users(@workflow) %>
</div>
<p>
<%= l(:label_dmsf_workflow_step) %>
<%= select_tag 'step',
dmsf_workflow_steps_options_for_select(@workflow.steps),
:id => 'selected_step', :style => "width:100px" %>
</p>
<p><%= submit_tag l(:dmsf_and) %>&nbsp<%= submit_tag l(:dmsf_or) %></p>
</fieldset>
<% end %>
</div>
</div>

View File

@ -0,0 +1 @@
<h2>WorkflowController#action</h2>

View File

@ -0,0 +1,3 @@
$('#ajax-modal').html('<%= escape_javascript(render :partial => 'action', :locals => {:workflow => @workflow}) %>');
showModal('ajax-modal', '400px');
$('#ajax-modal').addClass('new-action');

View File

@ -0,0 +1 @@
<%= render 'steps' %>

View File

@ -0,0 +1 @@
$('#users').html('<%= escape_javascript(render_principals_for_new_dmsf_workflow_users(@workflow)) %>');

View File

@ -0,0 +1 @@
<%= render 'steps' %>

View File

@ -0,0 +1 @@
<%= render 'main' %>

View File

@ -0,0 +1 @@
<h2>WorkflowController#log</h2>

View File

@ -0,0 +1,12 @@
<h2><%= link_to l(:label_dmsf_workflow_plural), dmsf_workflows_path %> &#187; <%=l(:label_dmsf_workflow_new)%></h2>
<%= labelled_form_for @workflow do |f| %>
<%= error_messages_for 'workflow' %>
<div class="box tabular">
<p><%= f.text_field :name, :required => true %></p>
<% if params[:project_id] %>
<p><%= hidden_field_tag :project_id, params[:project_id] %></p>
<% end %>
</div>
<%= submit_tag l(:button_create) %>
<% end %>

View File

@ -0,0 +1 @@
<%= render 'steps' %>

View File

@ -0,0 +1 @@
<%= render 'steps' %>

BIN
assets/images/ticket_go.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 B

View File

@ -0,0 +1,50 @@
/* An image next to the approval workflow item in the administration menu */
#admin-menu a.workflow { background-image: url(../images/ticket_go.png); }
.dmsf-entity-container {
padding: 0% 2%;
}
.dmsf-entity-container .dmsf-entity {
padding: 5px;
line-height: 28px;
}
li.dmsf-entity-file,
li.dmsf-entity-folder {
border: 1px solid #628DB6;
border-width: 1px 0;
list-style: none;
zoom: 1;
overflow: hidden;
}
.dmsf-entity-container .dmsf-entity .dmsf-col {
float: left;
padding-top: 1px;
}
.dmsf-entity-container .dmsf-entity .dmsf-col.dmsf-entity-name {
width: 30%;
}
.dmsf-entity-container .dmsf-entity .dmsf-col.dmsf-entity-cat {
width: 15%;
}
.dmsf-entity-container .dmsf-entity .dmsf-col.dmsf-entity-lock {
width: 5%;
}
.dmsf-entity-container .dmsf-entity .dmsf-col.dmsf-entity-size {
width: 10%;
}
.dmsf-entity-container .dmsf-entity .dmsf-col.dmsf-entity-modified {
width: 10%;
}
.dmsf-entity-container .dmsf-entity .dmsf-col.dmsf-entity-action {
width: 20%;
}
#users_for_delegate {height: 200px; overflow:auto;}
#users_for_delegate label {display: block;}

View File

@ -233,3 +233,8 @@ table.access-table tbody td, table.access-table tbody tr:hover td {
display: block;
float: left;
}
/* Approval workflow */
#admin-menu a.approvalworkflows { background-image: url(../images/ticket_go.png); }
#users_for_delegate {height: 200px; overflow:auto;}
#users_for_delegate label {display: block;}

View File

@ -208,3 +208,20 @@ en:
:select_option_webdav_readwrite: "Read/Write"
:label_webdav_strategy: "Webdav strategy"
:note_webdav_strategy: "Enables the administrator to decide if webdav is a read-only or read-write platform for end users."
label_dmsf_workflow_new: New approval workflow
label_dmsf_workflow: Approval workflow
label_dmsf_workflow_plural: Approval workflows
error_unable_delete_dmsf_workflow: Unable to delete the workflow
label_dmsf_workflow_step: Step
label_dmsf_workflow_step_plural: Steps
label_dmsf_workflow_approval: Approval
label_dmsf_workflow_approval_plural: Approvals
dmsf_and: AND
dmsf_or: OR
dmsf_new_step: New step
label_dmsf: DMSF
label_dmsf_wokflow_action_approve: Approve
label_dmsf_wokflow_action_reject: Reject
label_dmsf_wokflow_action_delegate: 'Delegate to:'
message_dmsf_wokflow_note: Your note...

View File

@ -100,4 +100,17 @@ RedmineApp::Application.routes.draw do
:resource_class => RedmineDmsf::Webdav::ResourceProxy,
:controller_class => RedmineDmsf::Webdav::Controller
), :at => "/dmsf/webdav"
# Approval workflow
resources :dmsf_workflows do
member do
get 'autocomplete_for_user'
get 'action'
post 'new_action'
end
end
match 'dmsf_workflows/:id/edit', :controller => 'dmsf_workflows', :action => 'add_step', :id => /\d+/, :via => :post
match 'dmsf_workflows/:id/edit', :controller => 'dmsf_workflows', :action => 'remove_step', :id => /\d+/, :via => :delete
match 'dmsf_workflows/:id/edit', :controller => 'dmsf_workflows', :action => 'reorder_steps', :id => /\d+/, :via => :put
end

View File

@ -0,0 +1,13 @@
class CreateDmsfWorkflows < ActiveRecord::Migration
def self.up
create_table :dmsf_workflows do |t|
t.string :name, :null => false
t.references :project
end
add_index :dmsf_workflows, [:name], :unique => true
end
def self.down
drop_table :dmsf_workflows
end
end

View File

@ -0,0 +1,15 @@
class CreateDmsfWorkflowSteps < ActiveRecord::Migration
def self.up
create_table :dmsf_workflow_steps do |t|
t.references :dmsf_workflow, :null => false
t.integer :step, :null => false
t.references :user, :null => false
t.integer :operator, :null => false
end
add_index :dmsf_workflow_steps, :dmsf_workflow_id
end
def self.down
drop_table :dmsf_workflow_steps
end
end

View File

@ -0,0 +1,17 @@
class CreateDmsfWorkflowStepAssignments < ActiveRecord::Migration
def self.up
create_table :dmsf_workflow_step_assignments do |t|
t.references :dmsf_workflow_step, :null => false
t.references :user, :null => false
t.references :dmsf_file_revision, :null => false
end
add_index :dmsf_workflow_step_assignments,
[:dmsf_workflow_step_id, :dmsf_file_revision_id],
# The default index name exceeds the index name limit
{:name => 'index_dmsf_wrkfl_step_assigns_on_wrkfl_step_id_and_frev_id'}
end
def self.down
drop_table :dmsf_workflow_step_assignments
end
end

View File

@ -0,0 +1,17 @@
class CreateDmsfWorkflowStepActions < ActiveRecord::Migration
def self.up
create_table :dmsf_workflow_step_actions do |t|
t.references :dmsf_workflow_step_assignment, :null => false
t.integer :action, :null => false
t.text :note
t.timestamp :created_at
end
add_index :dmsf_workflow_step_actions,
:dmsf_workflow_step_assignment_id,
# The default index name exceeds the index name limit
{:name => 'index_dmsf_workflow_step_actions_on_workflow_step_assignment_id'}
end
def self.down
drop_table :dmsf_workflow_step_actions
end
end

13
init.rb
View File

@ -58,6 +58,19 @@ Redmine::Plugin.register :redmine_dmsf do
permission :file_approval, {:dmsf_files => [:delete_revision, :notify_activate, :notify_deactivate],
:dmsf => [:notify_activate, :notify_deactivate]}
permission :force_file_unlock, {}
permission :approval_workflows, {:dmsf_workflows => [:new, :create, :destroy, :edit, :add_step, :remove_step, :reorder_steps, :update]}
end
# Administration menu extension
Redmine::MenuManager.map :admin_menu do |menu|
menu.push :approvalworkflows, {:controller => 'dmsf_workflows', :action => 'index'}, :caption => :label_dmsf_workflow_plural
end
# Adds javascript and stylesheet tags for project tree view
class DmsfViewListener < Redmine::Hook::ViewListener
def view_layouts_base_html_head(context)
stylesheet_link_tag('dmsf', :plugin => :redmine_dmsf)
end
end
Redmine::WikiFormatting::Macros.register do

View File

@ -33,7 +33,7 @@ module RedmineDmsf
has_many :dmsf_files, :class_name => "DmsfFile", :foreign_key => "project_id", :conditions => { :dmsf_folder_id => nil }
#Fix: should only be root folders not, all folders
has_many :dmsf_folders, :class_name => "DmsfFolder", :foreign_key => "project_id", :conditions => {:dmsf_folder_id => nil}, :dependent => :destroy
has_many :dmsf_workflows, :dependent => :destroy
end
end

View File

@ -39,8 +39,9 @@ module RedmineDmsf
def project_settings_tabs_with_dmsf
tabs = project_settings_tabs_without_dmsf
if @project.module_enabled?("dmsf")
tabs.push({:name => 'dmsf', :controller => :dmsf_state, :action => :user_pref_save, :partial => 'dmsf_state/user_pref', :label => :dmsf})
if @project.module_enabled? 'dmsf'
#tabs.push({:name => 'dmsf', :controller => :dmsf_state, :action => :user_pref_save, :partial => 'dmsf_state/user_pref', :label => :dmsf})
tabs << {:name => 'dmsf', :controller => 'dmsf_workflows', :action => 'index', :partial => 'dmsf_workflows/main', :label => 'label_dmsf'}
end
return tabs
end

View File

@ -0,0 +1,22 @@
---
wfsac1:
id: 1
dmsf_workflow_step_assignment_id: 1
action: 1
note: 'Approval'
created_at: '2013-05-03 10:45:35'
wfsac2:
id: 2
dmsf_workflow_step_assignment_id: 1
action: 2
note: 'Rejection'
created_at: '2013-05-03 10:45:36'
wfsac3:
id: 3
dmsf_workflow_step_assignment_id: 1
action: 3
note: 'Delegation'
created_at: '2013-05-03 10:45:37'

View File

@ -0,0 +1,6 @@
---
wfsa1:
id: 1
dmsf_workflow_step_id: 1
user_id: 1
dmsf_file_revision_id: 2

21
test/fixtures/dmsf_workflow_steps.yml vendored Normal file
View File

@ -0,0 +1,21 @@
---
wfs1:
id: 1
dmsf_workflow_id: 1
step: 1
user_id: 1
operator: 1
wfs2:
id: 2
dmsf_workflow_id: 1
step: 2
user_id: 2
operator: 1
wfs3:
id: 3
dmsf_workflow_id: 1
step: 3
user_id: 1
operator: 1

8
test/fixtures/dmsf_workflows.yml vendored Normal file
View File

@ -0,0 +1,8 @@
---
wf1:
id: 1
name: wf1
wf2:
id: 2
name: wf2

View File

@ -0,0 +1,171 @@
require File.expand_path('../../test_helper', __FILE__)
class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase
fixtures :users, :dmsf_workflows, :dmsf_workflow_steps, :projects, :roles,
:members, :member_roles
def setup
User.current = nil
@manager_role = Role.find_by_name('Manager')
@project = Project.find(5)
end
def test_index_admin
@request.session[:user_id] = 1 # admin
get :index
assert_response :success
assert_template 'index'
end
def test_index_user
@request.session[:user_id] = 2 # non admin
get :index
assert_response :forbidden
end
def test_index_member
old_controller = @controller
@controller = ProjectsController.new
@request.session[:user_id] = 2
get :settings, :id => @project.id
assert_response :success
assert_template 'settings'
@controller = old_controller
end
def test_new_admin
@request.session[:user_id] = 1 # admin
get :new
assert_response :success
assert_template 'new'
end
def test_new_member_no_permission
@project.enable_module!(:dmsf)
@manager_role.remove_permission! :approval_workflows
@request.session[:user_id] = 2
get :new, {:project_id => @project.id}
assert_response :forbidden
end
def test_new_member_no_module
@project.disable_module!(:dmsf)
@manager_role.add_permission! :approval_workflows
@request.session[:user_id] = 2
get :new, {:project_id => @project.id}
assert_response :forbidden
end
def test_new_member
@manager_role.add_permission! :approval_workflows
@request.session[:user_id] = 2
@project.enable_module!(:dmsf)
get :new, {:project_id => @project.id}
assert_response :success
assert_template 'new'
end
def test_new_no_member
@manager_role.add_permission! :approval_workflows
@request.session[:user_id] = 3
@project.enable_module!(:dmsf)
get :new, {:project_id => @project.id}
assert_response :forbidden
end
def test_edit
@request.session[:user_id] = 1 # admin
get :edit, :id => 1
assert_response :success
assert_template 'edit'
end
def test_create
@request.session[:user_id] = 1 # admin
assert_difference 'DmsfWorkflow.count', +1 do
post :create, :dmsf_workflow => {:name => 'wf3'}
end
workflow = DmsfWorkflow.first(:order => 'id DESC')
assert_redirected_to dmsf_workflows_path
assert_equal 'wf3', workflow.name
end
def test_update
@request.session[:user_id] = 1 # admin
put :update, :id => 1, :dmsf_workflow => {:name => 'wf1a'}
workflow = DmsfWorkflow.find(1)
assert_equal 'wf1a', workflow.name
end
def test_destroy
@request.session[:user_id] = 1 # admin
assert_difference 'DmsfWorkflow.count', -1 do
delete :destroy, :id => 1
end
assert_redirected_to dmsf_workflows_path
assert_nil DmsfWorkflow.find_by_id(1)
end
def test_add_step
@request.session[:user_id] = 1 # admin
assert_difference 'DmsfWorkflowStep.count', +1 do
post :add_step, :commit => 'OR', :step => 1, :id => 1, :user_ids =>[3]
end
assert_response :success
ws = DmsfWorkflowStep.first(:order => 'id DESC')
assert_equal 1, ws.dmsf_workflow_id
assert_equal 1, ws.step
assert_equal 3, ws.user_id
assert_equal 0, ws.operator
end
def test_remove_step
@request.session[:user_id] = 1 # admin
n = DmsfWorkflowStep.where(:dmsf_workflow_id => 1, :step => 1).count
assert_difference 'DmsfWorkflowStep.count', -n do
delete :remove_step, :step => 1, :id => 1
end
assert_response :success
ws = DmsfWorkflowStep.where(:dmsf_workflow_id => 1).first(:order => 'id ASC')
assert_equal 1, ws.step
end
def test_reorder_steps_to_lower
@request.session[:user_id] = 1 # admin
put :reorder_steps, :step => 1, :id => 1, :workflow_step => {:move_to => 'lower'}
assert_response :success
assert_equal 2, DmsfWorkflowStep.find(1).step
assert_equal 1, DmsfWorkflowStep.find(2).step
assert_equal 3, DmsfWorkflowStep.find(3).step
end
def test_reorder_steps_to_lowest
@request.session[:user_id] = 1 # admin
put :reorder_steps, :step => 1, :id => 1, :workflow_step => {:move_to => 'lowest'}
assert_response :success
assert_equal 3, DmsfWorkflowStep.find(1).step
assert_equal 1, DmsfWorkflowStep.find(2).step
assert_equal 2, DmsfWorkflowStep.find(3).step
end
def test_reorder_steps_to_higher
@request.session[:user_id] = 1 # admin
put :reorder_steps, :step => 2, :id => 1, :workflow_step => {:move_to => 'higher'}
assert_response :success
assert_equal 2, DmsfWorkflowStep.find(1).step
assert_equal 1, DmsfWorkflowStep.find(2).step
assert_equal 3, DmsfWorkflowStep.find(3).step
end
def test_reorder_steps_to_highest
@request.session[:user_id] = 1 # admin
put :reorder_steps, :step => 3, :id => 1, :workflow_step => {:move_to => 'highest'}
assert_response :success
assert_equal 2, DmsfWorkflowStep.find(1).step
assert_equal 3, DmsfWorkflowStep.find(2).step
assert_equal 1, DmsfWorkflowStep.find(3).step
end
end

View File

@ -0,0 +1,60 @@
require File.expand_path('../../test_helper', __FILE__)
class DmsfWorkflowStepActionTest < RedmineDmsf::Test::UnitTest
fixtures :dmsf_workflow_steps
fixtures :dmsf_workflow_step_actions
def setup
@wfsac1 = DmsfWorkflowStepAction.find(1)
@wfsac2 = DmsfWorkflowStepAction.find(2)
@wfsac3 = DmsfWorkflowStepAction.find(3)
end
def test_truth
assert_kind_of DmsfWorkflowStepAction, @wfsac1
assert_kind_of DmsfWorkflowStepAction, @wfsac2
assert_kind_of DmsfWorkflowStepAction, @wfsac3
end
def test_create
wfsac = DmsfWorkflowStepAction.new(
:dmsf_workflow_step_assignment_id => 1,
:action => 1,
:note => 'Approvement')
assert wfsac.save
wfsac.reload
assert wfsac.created_at
end
def test_update
@wfsac1.dmsf_workflow_step_assignment_id = 2
@wfsac1.action = 2
@wfsac1.note = 'Rejection'
assert @wfsac1.save
@wfsac1.reload
assert_equal 2, @wfsac1.dmsf_workflow_step_assignment_id
assert_equal 2, @wfsac1.action
assert_equal 'Rejection', @wfsac1.note
end
def test_validate_workflow_step_assignment_id_presence
@wfsac1.dmsf_workflow_step_assignment_id = nil
assert !@wfsac1.save
assert_equal 1, @wfsac1.errors.count
end
def test_validate_action_presence
@wfsac1.action = nil
assert !@wfsac1.save
assert_equal 1, @wfsac1.errors.count
end
def test_destroy
@wfsac1.destroy
assert_nil DmsfWorkflowStepAction.find_by_id(1)
end
end

View File

@ -0,0 +1,53 @@
require File.expand_path('../../test_helper', __FILE__)
class WorkflowStepAssignmentTest < RedmineDmsf::Test::UnitTest
fixtures :users, :dmsf_file_revisions, :dmsf_workflow_steps, :dmsf_workflow_step_assignments
def setup
@wfsa1 = DmsfWorkflowStepAssignment.find(1)
end
def test_truth
assert_kind_of DmsfWorkflowStepAssignment, @wfsa1
end
def test_create
wfsa = DmsfWorkflowStepAssignment.new(
:dmsf_workflow_step_id => 2,
:user_id => 2,
:dmsf_file_revision_id => 2)
assert wfsa.save
end
def test_update
@wfsa1.dmsf_workflow_step_id = 2
@wfsa1.user_id = 2
@wfsa1.dmsf_file_revision_id = 2
assert @wfsa1.save
@wfsa1.reload
assert_equal 2, @wfsa1.dmsf_workflow_step_id
assert_equal 2, @wfsa1.user_id
assert_equal 2, @wfsa1.dmsf_file_revision_id
end
def test_validate_workflow_step_id_presence
@wfsa1.dmsf_workflow_step_id = nil
assert !@wfsa1.save
assert_equal 1, @wfsa1.errors.count
end
def test_validate_workflow_entity_id_presence
@wfsa1.dmsf_file_revision_id = nil
assert !@wfsa1.save
assert_equal 1, @wfsa1.errors.count
end
def test_destroy
@wfsa1.destroy
assert_nil DmsfWorkflowStepAssignment.find_by_id(1)
assert_nil DmsfWorkflowStepAction.find_by_id(1)
end
end

View File

@ -0,0 +1,76 @@
require File.expand_path('../../test_helper', __FILE__)
class DmsfWorkflowStepTest < RedmineDmsf::Test::UnitTest
fixtures :users, :dmsf_workflows, :dmsf_workflow_steps
def setup
@wfs1 = DmsfWorkflowStep.find(1)
@wfs2 = DmsfWorkflowStep.find(2)
end
def test_truth
assert_kind_of DmsfWorkflowStep, @wfs1
end
def test_create
wfs = DmsfWorkflowStep.new(
:dmsf_workflow_id => 1,
:step => 2,
:user_id => 1,
:operator => 1)
assert wfs.save
end
def test_update
@wfs1.dmsf_workflow_id = 2
@wfs1.step = 2
@wfs1.user_id = 2
@wfs1.operator = 2
assert @wfs1.save
@wfs1.reload
assert_equal 2, @wfs1.dmsf_workflow_id
assert_equal 2, @wfs1.step
assert_equal 2, @wfs1.user_id
assert_equal 2, @wfs1.operator
end
def test_validate_workflow_id_presence
@wfs1.dmsf_workflow_id = nil
assert !@wfs1.save
assert_equal 1, @wfs1.errors.count
end
def test_validate_step_presence
@wfs1.step = nil
assert !@wfs1.save
assert_equal 1, @wfs1.errors.count
end
def test_validate_user_id_presence
@wfs1.user_id = nil
assert !@wfs1.save
assert_equal 1, @wfs1.errors.count
end
def test_validate_operator_presence
@wfs1.operator = nil
assert !@wfs1.save
assert_equal 1, @wfs1.errors.count
end
def test_validate_user_id_uniqueness
@wfs2.user_id = @wfs1.user_id
@wfs2.dmsf_workflow_id = @wfs1.dmsf_workflow_id
@wfs2.step = @wfs1.step
assert !@wfs2.save
assert_equal 1, @wfs2.errors.count
end
def test_destroy
@wfs2.destroy
assert_nil DmsfWorkflowStep.find_by_id(2)
end
end

View File

@ -0,0 +1,54 @@
require File.expand_path('../../test_helper', __FILE__)
class DmsfWorkflowTest < RedmineDmsf::Test::UnitTest
fixtures :projects, :dmsf_workflows, :dmsf_workflow_steps
def setup
@wf1 = DmsfWorkflow.find(1)
@wf2 = DmsfWorkflow.find(2)
@wfs1 = DmsfWorkflowStep.find(1)
end
def test_truth
assert_kind_of DmsfWorkflow, @wf1
end
def test_create
workflow = DmsfWorkflow.new(:name => 'wf')
assert workflow.save
end
def test_update
@wf1.name = 'wf1a'
@wf1.project_id = 5
assert @wf1.save
@wf1.reload
assert_equal 'wf1a', @wf1.name
assert_equal 5, @wf1.project_id
end
def test_validate_name_length
@wf1.name = 'a' * 256
assert !@wf1.save
assert_equal 1, @wf1.errors.count
end
def test_validate_name_presence
@wf1.name = ''
assert !@wf1.save
assert_equal 1, @wf1.errors.count
end
def test_validate_name_uniqueness
@wf2.name = @wf1.name
assert !@wf2.save
assert_equal 1, @wf2.errors.count
end
def test_destroy
@wf1.destroy
assert_nil DmsfWorkflow.find_by_id(1)
assert_nil DmsfWorkflowStep.find_by_id(@wfs1.id)
end
end