Merge pull request #129 from picman/devel-1.4.6

Approval workflow copied from 1.5.0 devel
This commit is contained in:
Daniel Munn 2013-06-06 21:23:08 -07:00
commit 96f57ea3ad
80 changed files with 1860 additions and 775 deletions

View File

@ -255,8 +255,22 @@ class DmsfController < ApplicationController
redirect_to params[:current] ? params[:current] :
{:controller => "dmsf", :action => "show", :id => @project, :folder_id => @folder.folder}
end
def assign
end
def assignment
revision = DmsfFileRevision.find_by_id params[:dmsf_file_revision_id]
if revision
revision.set_workflow(params[:dmsf_workflow_id], params[:action])
revision.assign_workflow(params[:dmsf_workflow_id])
if request.post? && revision.save
flash[:notice] = l(:notice_successful_create)
end
end
redirect_to params[:current] ? params[:current] :
{:controller => "dmsf", :action => "show", :id => @project, :folder_id => @folder.folder}
end
private

View File

@ -26,6 +26,7 @@ class DmsfFilesController < ApplicationController
before_filter :authorize
helper :all
helper :dmsf_workflows
def show
# download is put here to provide more clear and usable links
@ -82,7 +83,7 @@ class DmsfFilesController < ApplicationController
@revision.major_version = last_revision.major_version
@revision.minor_version = last_revision.minor_version
@revision.workflow = last_revision.workflow
#@revision.workflow = last_revision.workflow
version = params[:version].to_i
file_upload = params[:file_upload]
if file_upload.nil?
@ -96,13 +97,14 @@ class DmsfFilesController < ApplicationController
@revision.disk_filename = @revision.new_storage_filename
@revision.mime_type = Redmine::MimeType.of(file_upload.original_filename)
end
@revision.set_workflow(params[:workflow])
@revision.set_workflow(params[:dmsf_workflow_id], params[:commit])
@file.name = @revision.name
@file.folder = @revision.folder
if @revision.valid? && @file.valid?
@revision.save!
@revision.assign_workflow(params[:dmsf_workflow_id])
unless file_upload.nil?
@revision.copy_file_content(file_upload)
end

View File

@ -26,6 +26,7 @@ class DmsfUploadController < ApplicationController
before_filter :find_folder, :except => [:upload_file]
helper :all
helper :dmsf_workflows
def upload_files
uploaded_files = params[:uploaded_files]
@ -107,7 +108,7 @@ class DmsfUploadController < ApplicationController
new_revision.source_revision = last_revision
new_revision.major_version = last_revision.major_version
new_revision.minor_version = last_revision.minor_version
new_revision.workflow = last_revision.workflow
#new_revision.workflow = last_revision.workflow
end
commited_disk_filepath = "#{DmsfHelper.temp_dir}/#{commited_file["disk_filename"].gsub(/[\/\\]/,'')}"
@ -120,8 +121,8 @@ class DmsfUploadController < ApplicationController
new_revision.title = commited_file["title"]
new_revision.description = commited_file["description"]
new_revision.comment = commited_file["comment"]
new_revision.increase_version(commited_file["version"].to_i, true)
new_revision.set_workflow(commited_file["workflow"])
new_revision.increase_version(commited_file["version"].to_i, true)
new_revision.set_workflow(commited_file[:dmsf_workflow_id], nil)
new_revision.mime_type = Redmine::MimeType.of(new_revision.name)
new_revision.size = File.size(commited_disk_filepath)
@ -147,6 +148,7 @@ class DmsfUploadController < ApplicationController
end
if new_revision.save
new_revision.assign_workflow(commited_file[:dmsf_workflow_id])
file.reload
new_revision.copy_file_content(file_upload)

View File

@ -0,0 +1,216 @@
# 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, :assign, :assignment]
before_filter :find_project, :except => [:start]
before_filter :authorize_global
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
if params[:commit] == l(:button_submit)
action = DmsfWorkflowStepAction.new(
:dmsf_workflow_step_assignment_id => params[:dmsf_workflow_step_assignment_id],
:action => params[:step_action],
:note => params[:note])
if request.post?
if action.save
@workflow.try_finish params[:dmsf_file_revision_id], action, params[:user_id]
flash[:notice] = l(:notice_successful_update)
else
flash[:error] = l(:error_empty_note)
end
end
end
redirect_to :back
end
def assign
end
def assignment
if params[:commit] == l(:button_submit)
revision = DmsfFileRevision.find_by_id params[:dmsf_file_revision_id]
if revision
revision.set_workflow(params[:dmsf_workflow_id], params[:action])
revision.assign_workflow(params[:dmsf_workflow_id])
if request.post?
if revision.save
flash[:notice] = l(:notice_successful_update)
else
flash[:error] = l(:error_workflow_assign)
end
end
end
end
redirect_to :back
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
flash[:notice] = l(:notice_successful_delete)
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
render :layout => false
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 = (params[:commit] == l(:dmsf_and)) ? DmsfWorkflowStep::OPERATOR_AND : DmsfWorkflowStep::OPERATOR_OR
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
def start
revision = DmsfFileRevision.find_by_id(params[:dmsf_file_revision_id])
if revision
revision.set_workflow(@workflow.id, params[:action])
if request.post? && revision.save
flash[:notice] = l(:notice_successful_update)
end
end
redirect_to :back
end
private
def find_workflow
@workflow = DmsfWorkflow.find_by_id(params[:id])
end
def find_project
if @workflow && @workflow.project
@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,66 @@
# 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, dmsf_workflow_step_assignment_id, dmsf_file_revision_id)
scope = workflow.delegates(params[:q], dmsf_workflow_step_assignment_id, dmsf_file_revision_id, nil)
principal_count = scope.count
principal_pages = Redmine::Pagination::Paginator.new principal_count, 10, params['page']
principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).all
if dmsf_workflow_step_assignment_id
s = content_tag('div', principals_radio_button_tags('step_action', principals), :id => 'users_for_delegate')
else
s = content_tag('div', principals_check_box_tags('user_ids[]', principals), :id => 'users')
end
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')
s.html_safe
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
def dmsf_workflows_for_select(project, dmsf_workflow_id)
options = Array.new
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
options_for_select(options, :selected => dmsf_workflow_id)
end
def principals_radio_button_tags(name, principals)
s = ''
principals.each do |principal|
s << "<label>#{ radio_button_tag name, principal.id, false, :id => nil } #{h principal}</label>\n"
end
s.html_safe
end
end

View File

@ -25,6 +25,7 @@ class DmsfFileRevision < ActiveRecord::Base
belongs_to :deleted_by_user, :class_name => "User", :foreign_key => "deleted_by_user_id"
belongs_to :project
has_many :access, :class_name => "DmsfFileRevisionAccess", :foreign_key => "dmsf_file_revision_id", :dependent => :destroy
has_many :dmsf_workflow_step_assignment, :dependent => :destroy
#Returns a list of revisions that are not deleted here, or deleted at parent level either
scope :visible, lambda {|*args| joins(:file).where(DmsfFile.visible_condition(args.shift || User.current, *args)).where("#{self.table_name}.deleted = :false", :false => false ).readonly(false) }
@ -154,32 +155,52 @@ class DmsfFileRevision < ActiveRecord::Base
return new_revision
end
#TODO: validate if it isn't doubled or move it to view
def workflow_str
def workflow_str(name)
if name && dmsf_workflow_id
wf = DmsfWorkflow.find_by_id(dmsf_workflow_id)
str = "#{wf.name} - " if wf
else
str = ''
end
case workflow
when 1 then l(:title_waiting_for_approval)
when 2 then l(:title_approved)
else nil
when DmsfWorkflow::STATE_WAITING_FOR_APPROVAL
str + l(:title_waiting_for_approval)
when DmsfWorkflow::STATE_APPROVED
str + l(:title_approved)
when DmsfWorkflow::STATE_DRAFT
str + l(:title_draft)
when DmsfWorkflow::STATE_REJECTED
str + l(:title_rejected)
else
str
end
end
def set_workflow(workflow)
def set_workflow(dmsf_workflow_id, commit)
if User.current.allowed_to?(:file_approval, self.file.project)
self.workflow = workflow
else
if self.source_revision.nil?
self.workflow = workflow == 2 ? 1 : workflow
else
if workflow == 2 || self.source_revision.workflow == 1 || self.source_revision.workflow == 2
self.workflow = 1
unless dmsf_workflow_id.blank?
self.dmsf_workflow_id = dmsf_workflow_id
if commit == 'start'
self.workflow = DmsfWorkflow::STATE_WAITING_FOR_APPROVAL
self.dmsf_workflow_started_by = User.current.id if User.current
self.dmsf_workflow_started_at = DateTime.now
else
self.workflow = workflow
end
self.workflow = DmsfWorkflow::STATE_DRAFT
self.dmsf_workflow_assigned_by = User.current.id if User.current
self.dmsf_workflow_assigned_at = DateTime.now
end
end
end
end
def assign_workflow(dmsf_workflow_id)
if User.current.allowed_to?(:file_approval, self.file.project)
wf = DmsfWorkflow.find_by_id(dmsf_workflow_id)
wf.assign(self.id) if wf && self.id
end
end
def increase_version(version_to_increase, new_content)
if new_content
self.minor_version = case version_to_increase

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

@ -0,0 +1,183 @@
# 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
has_many :dmsf_workflow_steps, :dependent => :destroy, :order => 'step ASC, operator DESC'
validates_uniqueness_of :name
validates :name, :presence => true
validates_length_of :name, :maximum => 255
STATE_NONE = nil
STATE_DRAFT = 3
STATE_WAITING_FOR_APPROVAL = 1
STATE_APPROVED = 2
STATE_REJECTED = 4
def self.workflows(project)
project ? where(:project_id => project) : where('project_id IS NULL')
end
def project
Project.find_by_id(project_id) if project_id
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
end
def steps
ws = Array.new
dmsf_workflow_steps.each do |s|
unless ws.include? s.step
ws << s.step
end
end
ws
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(q, dmsf_workflow_step_assignment_id, dmsf_file_revision_id, project_id)
if project_id
sql = ['id IN (SELECT user_id FROM members WHERE project_id = ?', project_id]
elsif dmsf_workflow_step_assignment_id && dmsf_file_revision_id
sql = [
'id NOT IN (SELECT a.user_id FROM dmsf_workflow_step_assignments a WHERE id = ?) AND id IN (SELECT m.user_id FROM members m JOIN dmsf_file_revisions r ON m.project_id = r.project_id WHERE r.id = ?)',
dmsf_workflow_step_assignment_id,
dmsf_file_revision_id]
else
sql = '1=1'
end
unless q.nil? || q.empty?
User.active.sorted.where(sql).like(q)
else
User.active.sorted.where(sql)
end
end
def next_assignments(dmsf_file_revision_id)
self.dmsf_workflow_steps.each do |step|
unless step.finished?(dmsf_file_revision_id)
return step.next_assignments(dmsf_file_revision_id)
end
end
return nil
end
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
end
end
end
str
end
def assign(dmsf_file_revision_id)
dmsf_workflow_steps.each do |ws|
ws.assign(dmsf_file_revision_id)
end
end
def try_finish(dmsf_file_revision_id, action, user_id)
res = nil
case action.action
when DmsfWorkflowStepAction::ACTION_APPROVE
self.dmsf_workflow_steps.each do |step|
res = step.result dmsf_file_revision_id
unless step.finished? dmsf_file_revision_id
return
end
end
when DmsfWorkflowStepAction::ACTION_REJECT
res = DmsfWorkflow::STATE_REJECTED
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
if res
revision = DmsfFileRevision.find_by_id dmsf_file_revision_id
revision.update_attribute(:workflow, res) if revision
end
end
end

View File

@ -0,0 +1,96 @@
# 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]
OPERATOR_OR = 0
OPERATOR_AND = 1
def soperator
operator == 1 ? l(:dmsf_and) : l(:dmsf_or)
end
def user
User.find(user_id)
end
def assign(dmsf_file_revision_id)
step_assignment = DmsfWorkflowStepAssignment.new(
:dmsf_workflow_step_id => id,
:user_id => user_id,
:dmsf_file_revision_id => dmsf_file_revision_id)
step_assignment.save
end
def finished?(dmsf_file_revision_id)
res = self.result(dmsf_file_revision_id)
res == DmsfWorkflow::STATE_APPROVED || res == DmsfWorkflow::STATE_REJECTED
end
def result(dmsf_file_revision_id)
assignments = DmsfWorkflowStepAssignment.where(
:dmsf_workflow_step_id => self.id, :dmsf_file_revision_id => dmsf_file_revision_id).all
assignments.each do |assignment|
actions = DmsfWorkflowStepAction.where(
:dmsf_workflow_step_assignment_id => assignment.id).all
if actions.empty?
return
end
actions.each do |action|
if DmsfWorkflowStepAction.is_finished?(action.action)
case action.action
when DmsfWorkflowStepAction::ACTION_APPROVE
return DmsfWorkflow::STATE_APPROVED
when DmsfWorkflowStepAction::ACTION_REJECT
return DmsfWorkflow::STATE_REJECTED
else
return
end
end
end
end
end
def next_assignments(dmsf_file_revision_id)
results = Array.new
assignments = DmsfWorkflowStepAssignment.where(
:dmsf_workflow_step_id => self.id,
:dmsf_file_revision_id => dmsf_file_revision_id)
assignments.each do |assignment|
add = true
assignment.dmsf_workflow_step_actions.each do |action|
if action.is_finished?
add = false
break
end
end
results << assignment if add
end
return results
end
end

View File

@ -0,0 +1,65 @@
# 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
validates :note, :presence => true, :unless => lambda { self.action == DmsfWorkflowStepAction::ACTION_APPROVE }
validates :author_id, :presence => true
ACTION_APPROVE = 1
ACTION_REJECT = 2
ACTION_DELEGATE = 3
ACTION_ASSIGN = 4
ACTION_START = 5
def initialize(*args)
super
self.author_id = User.current.id if User.current
end
def self.is_finished?(action)
action == DmsfWorkflowStepAction::ACTION_APPROVE ||
action == DmsfWorkflowStepAction::ACTION_REJECT
end
def is_finished?
DmsfWorkflowStepAction.is_finished? self.action
end
def self.action_str(action)
if action
case action.to_i
when ACTION_APPROVE
l(:title_approved)
when ACTION_REJECT
l(:title_rejected)
when ACTION_DELEGATE
l(:title_delegated)
when ACTION_ASSIGN
l(:title_assigned)
when ACTION_START
l(:title_started)
end
end
end
end

View File

@ -0,0 +1,27 @@
# 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
has_one :dmsf_workflow_step_assignment
validates :dmsf_workflow_step_id, :presence => true
validates :dmsf_file_revision_id, :presence => true
end

View File

@ -27,8 +27,9 @@
<p>
<%= label_tag("", l(:field_folder) + ":") %>
<%= f.select(:dmsf_folder_id,
options_for_select(DmsfFolder.directory_tree(@project, @folder),
:selected => @folder.nil? ? (@pathfolder.id unless @pathfolder.nil?) : (@folder.folder.id unless @folder.folder.nil?))) %>
options_for_select(DmsfFolder.directory_tree(@project, @folder),
:selected => @parent? @parent.id : (@pathfolder.id if @pathfolder)))
%>
</p>
</div>
</div>

View File

@ -43,6 +43,9 @@
</div>
<%= render "custom_fields", :object => @folder %>
</div>
<%= error_messages_for('dmsf_workflow') %>
<%= form_tag({:action => :entries_operation, :id => @project, :folder_id => @folder}, :method => :post,
:class => "dmfs_entries", :id => "entries_form") do %>
<%= hidden_field_tag("action") %>
@ -62,8 +65,9 @@
<th><%= l(:link_title) %></th>
<th><%= l(:link_size) %></th>
<th><%= l(:link_modified) %></th>
<th><%= l(:link_ver) %></th>
<th><%= l(:link_author) %></th>
<th title="<%= l(:label_version) %>"><%= l(:link_ver) %></th>
<th><%= l(:link_workflow) %></th>
<th><%= l(:link_author) %></th>
<th></th>
<th class="hidden"></th>
<th class="hidden"></th>
@ -91,10 +95,11 @@
<% end %>
</td>
<td class="version"></td>
<td class="workflow"></td>
<td class="author"><%= h(subfolder.user) %></td>
<td class="actions">
<% if User.current.allowed_to?(:file_approval, @project) %>
<div class="right_icon_box">
<div class="right_icon_box" style="width:26px;">
<% if subfolder.notification %>
<%= link_to_function(image_tag("notify.png", :plugin => :redmine_dmsf),
"manipulation_link('#{url_for(:action => 'notify_deactivate', :id => @project, :folder_id => subfolder)}')",
@ -103,7 +108,7 @@
<%= link_to_function(image_tag("notifynot.png", :plugin => :redmine_dmsf),
"manipulation_link('#{url_for(:action => 'notify_activate', :id => @project, :folder_id => subfolder)}')",
:title => l(:title_notifications_not_active_activate)) %>
<% end %>
<% end %>
</div>
<% end %>
<div class="right_icon_box" style="width: 70px;">
@ -145,6 +150,7 @@
</tr>
<% end %>
<% @files.each do |file| %>
<% wf = DmsfWorkflow.find_by_id(file.last_revision.dmsf_workflow_id) %>
<tr class="file">
<td class="check"><%= check_box_tag("files[]", file.id, false, :title => l(:title_check_for_zip_download_or_email)) %></td>
<td class="title">
@ -168,19 +174,23 @@
<%= image_tag("lockedbycurrent.png", :title => l(:title_locked_by_you), :plugin => :redmine_dmsf) %>
<% end %>
</td>
<td class="version">
<%= file.last_revision.version %>
<% case file.last_revision.workflow
when 1 then %><%= image_tag("waitingforapproval.png", :title => l(:title_waiting_for_approval),
:plugin => :redmine_dmsf) %>
<% when 2 then %><%= image_tag("approved.png", :title => l(:title_approved),
:plugin => :redmine_dmsf) %>
<% end %>
<td class="version"><%= file.last_revision.version %></td>
<td class="workflow">
<% if wf %>
<%= link_to(
file.last_revision.workflow_str(false),
log_dmsf_workflow_path(
:project_id => @project.id,
:id => wf.id,
:dmsf_file_revision_id => file.last_revision.id),
:title => DmsfWorkflow.assignments_to_users_str(wf.next_assignments(file.last_revision.id)),
:remote => true) %>
<% end %>
</td>
<td class="author"><%= h(file.last_revision.user) %></td>
<td class="actions">
<% if User.current.allowed_to?(:file_approval, @project) %>
<div class="right_icon_box">
<div class="right_icon_box" style="width:26px;">
<% if file.notification %>
<%= link_to_function(image_tag("notify.png", :plugin => :redmine_dmsf),
"manipulation_link('#{url_for(:controller => "dmsf_files", :action => 'notify_deactivate', :id => file)}')",
@ -190,9 +200,51 @@
"manipulation_link('#{url_for(:controller => "dmsf_files", :action => 'notify_activate', :id => file)}')",
:title => l(:title_notifications_not_active_activate)) %>
<% end %>
</div>
<% case file.last_revision.workflow %>
<% when DmsfWorkflow::STATE_WAITING_FOR_APPROVAL %>
<% if wf %>
<% assignments = wf.next_assignments(file.last_revision.id) %>
<% index = assignments.find_index{|assignment| assignment.user_id == User.current.id} if assignments %>
<% if index %>
<%= link_to(
image_tag('waiting_for_approval.png', :plugin => :redmine_dmsf),
action_dmsf_workflow_path(
:project_id => @project.id,
:id => wf.id,
:dmsf_workflow_step_assignment_id => assignments[index].id,
:dmsf_file_revision_id => file.last_revision.id),
:title => l(:title_waiting_for_approval),
:remote => true) %>
<% else %>
<%= image_tag('waiting_for_approval.png', :title => "#{l(:label_dmsf_wokflow_action_approve)} #{l(:label_dmsf_wokflow_action_reject)} #{l(:label_dmsf_wokflow_action_delegate)}", :plugin => :redmine_dmsf) %>
<% end %>
<% end %>
<% when DmsfWorkflow::STATE_APPROVED %>
<%= image_tag('approved.png', :title => l(:title_approved), :plugin => :redmine_dmsf) %>
<% when DmsfWorkflow::STATE_DRAFT %>
<% if User.current && (file.last_revision.dmsf_workflow_assigned_by == User.current.id) %>
<%= link_to_function(image_tag('draft.png', :plugin => :redmine_dmsf),
"manipulation_link('#{start_dmsf_workflow_path(
:id => file.last_revision.dmsf_workflow_id,
:dmsf_file_revision_id => file.last_revision.id)}')",
:title => l(:label_dmsf_wokflow_action_start)) %>
<% else %>
<%= image_tag('draft.png', :title => l(:label_dmsf_wokflow_action_start), :plugin => :redmine_dmsf) %>
<% end %>
<% when DmsfWorkflow::STATE_REJECTED %>
<%= image_tag('rejected.png', :title => l(:title_rejected), :plugin => :redmine_dmsf) %>
<% else %>
<%= link_to(
image_tag('none.png', :plugin => :redmine_dmsf),
assign_dmsf_workflow_path(
:project_id => @project.id,
:dmsf_file_revision_id => file.last_revision.id),
:title => l(:label_dmsf_wokflow_action_assign),
:remote => true) %>
<% end %>
</div>
<% end %>
<div class="right_icon_box" style="width: 70px;">
<div class="right_icon_box" style="width: 70px">
<div style="float: left">
<%= link_to(image_tag("filedetails.png", :plugin => :redmine_dmsf, :class =>"detail_icon"),
{:controller => "dmsf_files", :action => :show, :id => file },
@ -219,7 +271,7 @@
<%= link_to_function(image_tag("delete.png", :plugin => :redmine_dmsf),
"confirmation_link('#{url_for(:controller => "dmsf_files", :action => 'delete', :id => file)}')",
:title => l(:title_delete)) %>
<% end %>
<% end %>
</div>
</div>
<br class="clear" />
@ -257,7 +309,7 @@ sUrl = "jquery.dataTables/#{I18n.locale.to_s.downcase}.json" if I18n.locale && !
%>
<% content_for :header_tags do %>
<%= stylesheet_link_tag "jquery-ui/jquery-ui-1.8.13.css", :plugin => "redmine_dmsf" %>
<%= stylesheet_link_tag "jquery-ui/jquery-ui-1.9.2.css", :plugin => "redmine_dmsf" %>
<%= stylesheet_link_tag "plupload/jquery.ui.plupload.css", :plugin => "redmine_dmsf" %>
<%= stylesheet_link_tag "jquery.dataTables/jquery-ui.dataTables.css", :plugin => "redmine_dmsf" %>
<%= stylesheet_link_tag "dmsf", :plugin => "redmine_dmsf" %>
@ -278,9 +330,9 @@ sUrl = "jquery.dataTables/#{I18n.locale.to_s.downcase}.json" if I18n.locale && !
"aaSorting": [[1,'asc']],
"aaSortingFixed": [[7,'asc']],
"aoColumnDefs": [
{ "bSearchable": false, "aTargets": [0, 6, 7, 8] },
{ "bSortable": false, "aTargets": [0, 6, 7] },
{ "iDataSort": 8, "aTargets": [ 2 ] }
{ "bSearchable": false, "aTargets": [0, 7, 8, 9] },
{ "bSortable": false, "aTargets": [0, 7, 8] },
{ "iDataSort": 9, "aTargets": [ 2 ] }
],
"fnInitComplete": function() {
jQuery("div.controls").prependTo(jQuery("#browser_wrapper div.fg-toolbar")[0]);

View File

@ -4,21 +4,6 @@
<% if @file.locked_for_user? %>
<p class="warning"><%= l(:info_file_locked) %></p>
<% else %>
<%
disabled_workflow = []
selected_workflow = nil
if !User.current.allowed_to?(:file_approval, @project)
disabled_workflow << 2
current_workflow = @file.last_revision.workflow
if current_workflow == 1 || current_workflow == 2
disabled_workflow << nil
selected_workflow = 1
end
else
selected_workflow = @file.last_revision.workflow
end
%>
<%= form_for(@revision, :url => {:action => "create_revision", :id => @file},
:html => {:method=>:post, :multipart => true, :id => "new_revision_form"}) do |f| %>
<div class="clear">
@ -57,16 +42,7 @@ end
<%= @file.last_revision.major_version %>.<%= @file.last_revision.minor_version + 1 %> <%= l(:option_version_minor) %><br />
<%= radio_button_tag("version", 2, @revision.major_version != @file.last_revision.major_version) %>
<%= @file.last_revision.major_version + 1 %>.0 <%= l(:option_version_major) %><br />
</div>
<p>
<%= label_tag("workflow", l(:label_workflow) + ":") %>
<%= select_tag("workflow",
options_for_select([
[l(:option_workflow_none), nil],
[l(:option_workflow_waiting_for_approval), 1],
[l(:option_workflow_approved), 2]],
:selected => selected_workflow, :disabled => disabled_workflow)) %>
</p>
</div>
</div>
<div class="splitcontentright clear">
<p>
@ -78,7 +54,7 @@ end
(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)
</small>
</div>
</p>
</p>
</div>
<br style="clear: both"/>
<div class="custom_fields">
@ -96,6 +72,7 @@ end
</div>
<br />
<%= submit_tag(l(:submit_create)) %>
<% end %>
<% end %>
</div>

View File

@ -57,77 +57,72 @@
<h3><%= l(:heading_revisions) %></h3>
<% @file.revisions.visible[@revision_pages.current.offset,@revision_pages.items_per_page].each do |revision| %>
<div class="box dmsf_detail">
<div style="float:right">
<%= link_to(image_tag("download.png", :plugin => "redmine_dmsf"),
{:action => "show", :id => @file, :download => revision},
:title => l(:title_title_version_version_download, :title => h(revision.title), :version => revision.version)) %>
<% if User.current.allowed_to?(:file_approval, @project) %>
&nbsp;
<%= link_to_function(image_tag("delete.png", :plugin => "redmine_dmsf"),
"confirmation_link('#{url_for(:action => 'delete_revision', :id => revision)}', '#{l(:question_do_you_really_want_to_delete_this_revision)}')",
:title => l(:title_delete_revision)) %>
<% end %>
</div>
<p class="no-ident">
<%=label_tag("", (revision.source_revision.nil? ? l(:label_created) : l(:label_changed)) + ":")%>
<%= l(:info_changed_by_user, :changed => format_time(revision.updated_at), :user => h(revision.user)) %>
</p>
<div class="clear">
<div class="splitcontentleft">
<p>
<%= label_tag("", l(:label_title) + ":") %>
<%= h(revision.title) %>
</p>
</div>
<div class="splitcontentright">
<p>
<%= label_tag("", l(:label_file) + ":") %>
<%= (h(revision.folder.dmsf_path_str) + "/") unless revision.folder.nil? %><%= h(revision.name) %>
</p>
</div>
</div>
<p class="no-ident">
<%= label_tag("", l(:label_description) + ":") %>
</p>
<div class="wiki clear" style="margin-left: 110px">
<%= textilizable(revision.description) %>
</div>
<div class="splitcontentleft">
<p>
<%= label_tag("", l(:label_version) + ":") %>
<%= revision.major_version %>.<%= revision.minor_version %>
</p>
<p>
<%= label_tag("", l(:label_workflow) + ":") %>
<%= case revision.workflow
when 1 then l(:option_workflow_waiting_for_approval)
when 2 then l(:option_workflow_approved)
else l(:option_workflow_none)
end %>
</p>
</div>
<div class="splitcontentright clear">
<p>
<%= label_tag("", l(:label_mime) + ":") %>
<%= h(revision.mime_type) %>&nbsp;
</p>
<p>
<%= label_tag("", l(:label_size) + ":") %>
<%= number_to_human_size(revision.size) %>
</p>
</div>
<br style="clear: both"/>
<div class="no-ident clear">
<%= render "dmsf/custom_fields", :object => revision %>
</div>
<p class="no-ident clear">
<%= label_tag("", l(:label_comment) + ":") %>
<%= h(revision.comment) %>
</p>
<%= render(:partial => "revision_access", :locals => {:revision => revision}) if User.current.allowed_to?(:file_approval, @file.project) %>
</div>
<div class="revision_box dmsf_detail dataTables_wrapper">
<div class="fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix">
<div class="dataTables_lenght">
<div class="controls" style="float: right">
<%= link_to_function(image_tag('rev_downloads.png', :plugin => 'redmine_dmsf'), "$('#revision_access-#{revision.id}').toggle()", :title => 'Download entries')%>
<%= link_to(image_tag('rev_download.png', :plugin => 'redmine_dmsf'),
{:action => 'show', :id => @file, :download => revision},
:title => l(:title_title_version_version_download, :title => h(revision.title), :version => revision.version)) %>
<% if User.current.allowed_to?(:file_approval, @project) %>
<%= link_to_function(image_tag('rev_delete.png', :plugin => 'redmine_dmsf'),
"confirmation_link('#{url_for(:action => 'delete_revision', :id => revision)}', '#{l(:question_do_you_really_want_to_delete_this_revision)}')",
:title => l(:title_delete_revision)) %>
<% end %>
</div>
<i><%= l(:info_revision, :rev => revision.id) %></i>
<%= (revision.source_revision.nil? ? l(:label_created) : l(:label_changed)).downcase %>
<%= l(:info_changed_by_user, :changed => format_time(revision.updated_at)) %>
<%= link_to(revision.user) %>
</div>
</div>
<div style="border: 1px solid #e4e4e4;">
<div class="clear">
<div class="splitcontentleft">
<%= label_tag("", l(:label_title) + ":") %>
<%= h(revision.title) %>
</div>
<div class="splitcontentright">
<%= label_tag("", l(:label_file) + ":") %>
<%= (h(revision.folder.dmsf_path_str) + "/") unless revision.folder.nil? %><%= h(revision.name) %>
</div>
</div>
<p class="no-ident">
<%= label_tag("", l(:label_description) + ":") %>
</p>
<div class="wiki clear" style="margin-left: 110px">
<%= textilizable(revision.description) %>
</div>
<div class="splitcontentleft">
<%= label_tag("", l(:label_version) + ":") %>
<%= revision.major_version %>.<%= revision.minor_version %>
<br/>
<%= label_tag('', l(:label_workflow) + ':') %>
<%= revision.workflow_str true %>
</div>
<div class="splitcontentright clear">
<%= label_tag("", l(:label_mime) + ":") %>
<%= h(revision.mime_type) %>&nbsp;
<br/>
<%= label_tag("", l(:label_size) + ":") %>
<%= number_to_human_size(revision.size) %>
</div>
<div class="no-ident clear">
<%= render "dmsf/custom_fields", :object => revision %>
</div>
<div class="splitcontentleft clear">
<%= label_tag("", l(:label_comment) + ":") %>
<%= h(revision.comment) %>
</div>
<br/>
<div id="<%= "revision_access-#{revision.id}" %>" style="display:none">
<%= render(:partial => "revision_access", :locals => {:revision => revision}) if User.current.allowed_to?(:file_approval, @file.project) %>
</div>
</div>
</div>
<br/>
<br/>
<% end %>
<p class="pagination"><%= pagination_links_full @revision_pages, @file.revisions.visible.count %></p>
@ -176,7 +171,7 @@ sUrl = "jquery.dataTables/#{I18n.locale.to_s.downcase}.json" if I18n.locale && !
<% end %>
<% content_for :header_tags do %>
<%= stylesheet_link_tag "jquery-ui/jquery-ui-1.8.13.css", :plugin => "redmine_dmsf" %>
<%= stylesheet_link_tag "jquery-ui/jquery-ui-1.9.2.css", :plugin => "redmine_dmsf" %>
<%= stylesheet_link_tag "jquery.dataTables/jquery-ui.dataTables.css", :plugin => "redmine_dmsf" %>
<%= stylesheet_link_tag "dmsf", :plugin => "redmine_dmsf" %>
<%= javascript_include_tag "jquery-1.6.1.min.js", :plugin => "redmine_dmsf" %>

View File

@ -13,7 +13,7 @@
:download => ""}) %> (<%= file.name %>),
<%= number_to_human_size(file.last_revision.size) %>,
version: <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %>,
<%= "#{file.last_revision.workflow_str}," unless file.last_revision.workflow_str.blank? %>
<%= "#{file.last_revision.workflow_str(true)}," unless file.last_revision.workflow_str(true).blank? %>
<%= link_to("Details",
{:only_path => false, :controller => "dmsf_files", :action => "show", :id => file}) %>
<% unless file.last_revision.comment.blank? %>

View File

@ -1,6 +1,6 @@
User <%= @user %> actualized DMSF files in project <%= @project.name %>:
<% @files.each do |file| %>
<%= file.dmsf_path_str %> (<%= file.name %>), <%= number_to_human_size(file.last_revision.size) %>, version: <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %><%= ", #{file.last_revision.workflow_str}" unless file.last_revision.workflow_str.blank? %>
<%= file.dmsf_path_str %> (<%= file.name %>), <%= number_to_human_size(file.last_revision.size) %>, version: <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %><%= ", #{file.last_revision.workflow_str(true)}" unless file.last_revision.workflow_str(true).blank? %>
<%= url_for({:only_path => false, :controller => "dmsf_files", :action => "show", :id => file}) %>
<% unless file.last_revision.comment.blank? %> comment: <%= file.last_revision.comment %><% end %>
<% end %>

View File

@ -13,7 +13,7 @@
:download => ""}) %> (<%= file.name %>),
<%= number_to_human_size(file.last_revision.size) %>,
version: <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %>,
<%= "#{file.last_revision.workflow_str}," unless file.last_revision.workflow_str.blank? %>
<%= "#{file.last_revision.workflow_str(true)}," unless file.last_revision.workflow_str(true).blank? %>
<%= link_to("Details",
{:only_path => false, :controller => "dmsf_files", :action => "show", :id => file}) %>
<% unless file.last_revision.comment.blank? %>

View File

@ -1,6 +1,6 @@
User <%= @user %> actualized DMSF files in project <%= @project.name %>:
<% @files.each do |file| %>
<%= file.dmsf_path_str %> (<%= file.name %>), <%= number_to_human_size(file.last_revision.size) %>, version: <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %><%= ", #{file.last_revision.workflow_str}" unless file.last_revision.workflow_str.blank? %>
<%= file.dmsf_path_str %> (<%= file.name %>), <%= number_to_human_size(file.last_revision.size) %>, version: <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %><%= ", #{file.last_revision.workflow_str(true)}" unless file.last_revision.workflow_str(true).blank? %>
<%= url_for({:only_path => false, :controller => "dmsf_files", :action => "show", :id => file}) %>
<% unless file.last_revision.comment.blank? %> comment: <%= file.last_revision.comment %><% end %>
<% end %>

View File

@ -1,17 +1,4 @@
<%
disabled_workflow = []
selected_workflow = nil
unless User.current.allowed_to?(:file_approval, @project)
disabled_workflow << 2
current_workflow = upload.workflow
if current_workflow == 1 || current_workflow == 2
disabled_workflow << nil
selected_workflow = 1
end
else
selected_workflow = upload.workflow
end
%>
<div class="box dmsf_detail">
<%= hidden_field_tag("commited_files[#{i}][disk_filename]", upload.disk_filename) %>
<div class="clear">
@ -44,16 +31,7 @@ end
<%= upload.major_version %>.<%= upload.minor_version + 1 %> <%= l(:option_version_minor) %><br />
<%= radio_button_tag("commited_files[#{i}][version]", 2) %>
<%= upload.major_version + 1 %>.0 <%= l(:option_version_major) %><br />
</div>
<p>
<%= label_tag("commited_files[#{i}][workflow]", l(:label_workflow) + ":") %>
<%= select_tag("commited_files[#{i}][workflow]",
options_for_select([
[l(:option_workflow_none), nil],
[l(:option_workflow_waiting_for_approval), 1],
[l(:option_workflow_approved), 2]],
:selected => selected_workflow, :disabled => disabled_workflow)) %>
</p>
</div>
</div>
<div class="splitcontentright clear">
<p>

View File

@ -27,15 +27,7 @@
<p>
<%= label_tag("", l(:label_version) + ":") %>
<%= upload.major_version %>.<%= upload.minor_version %>
</p>
<p>
<%= label_tag("", l(:label_workflow) + ":") %>
<%= case upload.workflow
when 1 then l(:option_workflow_waiting_for_approval)
when 2 then l(:option_workflow_approved)
else l(:option_workflow_none)
end %>
</p>
</p>
</div>
<div class="splitcontentright clear">
<p>

View File

@ -0,0 +1,27 @@
<h3 class="title"><%= l(:label_dmsf_workflow) %></h3>
<%= form_tag({:controller => 'dmsf_workflows', :action => 'new_action'},
:method => :post,
:id => 'new-action-form') do %>
<%= hidden_field_tag :dmsf_workflow_step_assignment_id, params[:dmsf_workflow_step_assignment_id] %>
<%= hidden_field_tag :dmsf_file_revision_id, params[:dmsf_file_revision_id] %>
<p><label><%= radio_button_tag 'step_action', DmsfWorkflowStepAction::ACTION_APPROVE, true %> <%= l(:label_dmsf_wokflow_action_approve) %></label><br/></p>
<p>
<label><%= radio_button_tag 'step_action', DmsfWorkflowStepAction::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_tag 'delegate', l(:label_dmsf_wokflow_action_delegate) %><br/>
<%= text_field_tag 'user_search', nil %>
<%= javascript_tag "observeSearchfield('user_search', 'users_for_delegate', '#{ escape_javascript autocomplete_for_user_dmsf_workflow_path(@workflow, :dmsf_workflow_step_assignment_id => params[:dmsf_workflow_step_assignment_id], :dmsf_file_revision_id => params[:dmsf_file_revision_id]) }')" %>
<%= content_tag('div', principals_radio_button_tags('step_action', @workflow.delegates(nil, params[:dmsf_workflow_step_assignment_id], params[:dmsf_file_revision_id], nil)), :id => 'users_for_delegate') %>
</p>
<p class="buttons">
<%= submit_tag l(:button_submit), :name => 'commit', :onclick => 'hideModal(this);' %>
<%= submit_tag l(:button_cancel), :name => 'commit', :onclick => 'hideModal(this);' %>
</p>
<% end %>

View File

@ -0,0 +1,16 @@
<h3 class="title"><%= l(:label_dmsf_workflow) %></h3>
<%= form_tag({:controller => 'dmsf_workflows', :action => 'assignment'},
:method => :post,
:id => 'assignment-form') do %>
<%= hidden_field_tag :dmsf_file_revision_id, params[:dmsf_file_revision_id] %>
<p>
<%= label_tag('workflow', l(:label_workflow) + ':') %>
<%= select_tag(
'dmsf_workflow_id',
dmsf_workflows_for_select(@project, nil))%>
</p>
<p class="buttons">
<%= submit_tag l(:button_submit), :name => 'commit', :onclick => 'hideModal(this);' %>
<%= submit_tag l(:button_cancel), :name => 'commit', :onclick => 'hideModal(this);' %>
</p>
<% end %>

View File

@ -0,0 +1,57 @@
<h3 class="title"><%= l(:title_dmsf_workflow_log) %></h3>
<p>
<% if params[:dmsf_file_revision_id].present? %>
<% revision = DmsfFileRevision.find_by_id(params[:dmsf_file_revision_id]) %>
<% if revision %>
<div class="log_header_box">
<div class="log_header_left">
<%= label_tag 'workflow_name', "#{l(:field_name)}: " %>
<%= link_to @workflow.name, edit_dmsf_workflow_path(@workflow) %>
</div>
<div>
<%= label_tag 'workflow_status', "#{l(:field_status)}: " %>
<%= revision.workflow_str false %>
</div>
</div>
<div class="tab-content" id="tab-content-members">
<table class="list">
<thead><tr>
<th><%= l(:label_dmsf_workflow_step) %></th>
<th><%= l(:label_user) %> </th>
<th><%= l(:label_action) %></th>
<th><%= l(:label_note) %></th>
<th><%=l(:label_date)%></th>
</tr></thead>
<tbody>
<tr id="step-0" class="odd">
<td class="step"></td>
<td class="user"><%= link_to_user User.find_by_id(revision.dmsf_workflow_assigned_by) if revision.dmsf_workflow_assigned_by %></td>
<td class="action"><%= DmsfWorkflowStepAction.action_str(DmsfWorkflowStepAction::ACTION_ASSIGN) %></td>
<td class="note"></td>
<td class="date" align="center"><%= format_time(revision.dmsf_workflow_assigned_at) if revision.dmsf_workflow_assigned_at %></td>
</tr>
<tr id="step-1" class="even">
<td class="step"></td>
<td class="user"><%= link_to_user User.find_by_id(revision.dmsf_workflow_started_by) if revision.dmsf_workflow_started_by %></td>
<td class="action"><%= DmsfWorkflowStepAction.action_str(DmsfWorkflowStepAction::ACTION_START) %></td>
<td class="note"></td>
<td class="date" align="center"><%= format_time(revision.dmsf_workflow_started_at) if revision.dmsf_workflow_started_at %></td>
</tr>
<% sql = "SELECT c.action, c.note, c.created_at, c.author_id, a.user_id, s.step FROM dmsf_workflow_step_actions c RIGHT JOIN dmsf_workflow_step_assignments a ON a.id = c.dmsf_workflow_step_assignment_id JOIN dmsf_workflow_steps s ON s.id = a.dmsf_workflow_step_id WHERE a.dmsf_file_revision_id = #{revision.id} ORDER BY s.step, c.created_at" %>
<% result = DmsfWorkflowStep.connection.execute sql %>
<% result.each_with_index do |row, i| %>
<tr id="step-<%= i + 2 %> " class="<%= cycle 'odd', 'even' %>">
<td class="step"><%= row[5] %></td>
<td class="user"><%= link_to_user User.find_by_id(row[3].present? ? row[3] : row[4]) %></td>
<td class="action"><%= DmsfWorkflowStepAction.action_str(row[0]) %></td>
<td class="note"><%= row[1] %></td>
<td class="date" align="center"><%= format_time(row[2]) if row[2].present? %></td>
</tr>
<% end; reset_cycle %>
</tbody>
</table>
</div>
<% end %>
<% end %>
</p>

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', 'users', '#{ escape_javascript autocomplete_for_user_dmsf_workflow_path(@workflow, :dmsf_workflow_step_assignment_id => nil, :dmsf_file_revision_id => nil, :project_id => @project ? @project.id : nil) }')" %>
<div id="users">
<%= render_principals_for_new_dmsf_workflow_users(@workflow, nil, nil) %>
</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,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,3 @@
$('#ajax-modal').html('<%= escape_javascript(render :partial => 'assign', :locals => {:workflow => @workflow}) %>');
showModal('ajax-modal', '400px');
$('#ajax-modal').addClass('assignment');

View File

@ -0,0 +1 @@
<%= render_principals_for_new_dmsf_workflow_users(@workflow, params[:dmsf_workflow_step_assignment_id], params[:dmsf_file_revision_id]) %>

View File

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

View File

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

View File

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

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' %>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 641 B

BIN
assets/images/draft.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

BIN
assets/images/none.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 B

BIN
assets/images/rejected.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

BIN
assets/images/ticket_go.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 412 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -14,17 +14,17 @@ table.entries {
}
table.entries td.modified {
min-width: 140px;
width: 140px;
min-width: 104px;
width: 104px;
}
table.entries td.actions {
min-width: 108px;
width: 108px;
min-width: 116px;
width: 116px;
}
table.entries td.title {
width: 50%;
width: 40%;
}
table.entries th.check, table.entries td.check {
@ -136,6 +136,11 @@ td.version img {
vertical-align:text-top;
}
td.workflow {
font-size: 0.8em;
white-space: nowrap;
}
/* DMSF entries list icons */
div.right_icon_box {
@ -233,3 +238,48 @@ 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;}
.revision_box{
padding: 0px 0px 0px 0px;
margin-bottom: 10px;
background-color:#f6f6f6;
color:#505050;
line-height:1.5em;
}
div.revision_box .ui-widget-header {
font-weight: normal;
}
table.list td.note {
width: 30%;
}
table.list td.step, td.date {
text-align: center;
}
.log_header_box{
padding:6px;
margin-bottom: 10px;
}
.log_header_left {
width: 50%;
float: left;
}
.log_header_box label{
font-weight: bold;
margin-left: 0px;
margin-right: 3px;
padding: 3px 0 3px 0;
}
.modal a, a:link, a:visited{ color: #169; text-decoration: none; }
.modal a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
.modal{ font-size: 12px}

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -1,578 +0,0 @@
/*
* jQuery UI CSS Framework 1.8.13
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Theming/API
*/
/* Layout helpers
----------------------------------*/
.ui-helper-hidden { display: none; }
.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
.ui-helper-clearfix { display: inline-block; }
/* required comment for clearfix to work in Opera \*/
* html .ui-helper-clearfix { height:1%; }
.ui-helper-clearfix { display:block; }
/* end clearfix */
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
/* Interaction Cues
----------------------------------*/
.ui-state-disabled { cursor: default !important; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
/*
* jQuery UI CSS Framework 1.8.13
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Theming/API
*
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
*/
/* Component containers
----------------------------------*/
.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; }
.ui-widget .ui-widget { font-size: 1em; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; }
.ui-widget-content a { color: #222222; }
.ui-widget-header { border: 1px solid #aaaaaa; background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; }
.ui-widget-header a { color: #222222; }
/* Interaction states
----------------------------------*/
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; }
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; }
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; }
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; }
.ui-widget :active { outline: none; }
/* Interaction Cues
----------------------------------*/
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; }
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; }
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; }
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; }
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); }
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
/* positioning */
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-off { background-position: -96px -144px; }
.ui-icon-radio-on { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
/* Misc visuals
----------------------------------*/
/* Corner radius */
.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; }
.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; }
.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; }
.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
.ui-corner-right { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
.ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; }
/* Overlays */
.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
* jQuery UI Resizable 1.8.13
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Resizable#theming
*/
.ui-resizable { position: relative;}
.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;
/* http://bugs.jqueryui.com/ticket/7233
- Resizable: resizable handles fail to work in IE if transparent and content overlaps
*/
background-image:url(data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=);
}
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
* jQuery UI Selectable 1.8.13
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Selectable#theming
*/
.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
/*
* jQuery UI Accordion 1.8.13
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Accordion#theming
*/
/* IE/Win - Fix animation bug - #4615 */
.ui-accordion { width: 100%; }
.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
.ui-accordion .ui-accordion-li-fix { display: inline; }
.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
.ui-accordion .ui-accordion-content-active { display: block; }
/*
* jQuery UI Autocomplete 1.8.13
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Autocomplete#theming
*/
.ui-autocomplete { position: absolute; cursor: default; }
/* workarounds */
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
/*
* jQuery UI Menu 1.8.13
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Menu#theming
*/
.ui-menu {
list-style:none;
padding: 2px;
margin: 0;
display:block;
float: left;
}
.ui-menu .ui-menu {
margin-top: -3px;
}
.ui-menu .ui-menu-item {
margin:0;
padding: 0;
zoom: 1;
float: left;
clear: left;
width: 100%;
}
.ui-menu .ui-menu-item a {
text-decoration:none;
display:block;
padding:.2em .4em;
line-height:1.5;
zoom:1;
}
.ui-menu .ui-menu-item a.ui-state-hover,
.ui-menu .ui-menu-item a.ui-state-active {
font-weight: normal;
margin: -1px;
}
/*
* jQuery UI Button 1.8.13
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Button#theming
*/
.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
.ui-button-icons-only { width: 3.4em; }
button.ui-button-icons-only { width: 3.7em; }
/*button text element */
.ui-button .ui-button-text { display: block; line-height: 1.4; }
.ui-button-text-only .ui-button-text { padding: .4em 1em; }
.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
/* no icon support for input elements, provide padding by default */
input.ui-button { padding: .4em 1em; }
/*button icon element(s) */
.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
/*button sets*/
.ui-buttonset { margin-right: 7px; }
.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
/* workarounds */
button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
/*
* jQuery UI Dialog 1.8.13
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Dialog#theming
*/
.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }
/*
* jQuery UI Slider 1.8.13
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Slider#theming
*/
.ui-slider { position: relative; text-align: left; }
.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
.ui-slider-horizontal { height: .8em; }
.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
.ui-slider-horizontal .ui-slider-range-min { left: 0; }
.ui-slider-horizontal .ui-slider-range-max { right: 0; }
.ui-slider-vertical { width: .8em; height: 100px; }
.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
.ui-slider-vertical .ui-slider-range-max { top: 0; }/*
* jQuery UI Tabs 1.8.13
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Tabs#theming
*/
.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
.ui-tabs .ui-tabs-hide { display: none !important; }
/*
* jQuery UI Datepicker 1.8.13
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Datepicker#theming
*/
.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
.ui-datepicker .ui-datepicker-prev { left:2px; }
.ui-datepicker .ui-datepicker-next { right:2px; }
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year { width: 49%;}
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
.ui-datepicker td { border: 0; padding: 1px; }
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi { width:auto; }
.ui-datepicker-multi .ui-datepicker-group { float:left; }
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
.ui-datepicker-row-break { clear:both; width:100%; }
/* RTL support */
.ui-datepicker-rtl { direction: rtl; }
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
.ui-datepicker-cover {
display: none; /*sorry for IE5*/
display/**/: block; /*sorry for IE5*/
position: absolute; /*must have*/
z-index: -1; /*must have*/
filter: mask(); /*must have*/
top: -4px; /*must have*/
left: -4px; /*must have*/
width: 200px; /*must have*/
height: 200px; /*must have*/
}/*
* jQuery UI Progressbar 1.8.13
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Progressbar#theming
*/
.ui-progressbar { height:2em; text-align: left; }
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }

File diff suppressed because one or more lines are too long

View File

@ -55,7 +55,7 @@ en:
:title_locked_by_user: "Locked by %{user}"
:title_locked_by_you: "Locked by you"
:title_waiting_for_approval: "Waiting for Approval"
:title_approved: "Approved"
:title_approved: "Approved"
:title_unlock_file: "Unlock to allow changes for other members"
:title_lock_file: "Lock to prevent changes for other members"
:submit_download: "Download"
@ -92,12 +92,10 @@ en:
:title_delete_revision: "Delete revision"
:label_created: "Created"
:label_changed: "Changed"
:info_changed_by_user: "%{changed} by %{user}"
:info_changed_by_user: "%{changed} by"
:label_filename: "Filename"
:label_version: "Version"
:label_workflow: "Workflow"
:option_workflow_waiting_for_approval: "Waiting for approval"
:option_workflow_approved: "Approved"
:label_workflow: "Workflow"
:option_workflow_none: "None"
:label_mime: "Mime"
:label_size: "Size"
@ -208,3 +206,36 @@ 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."
error_unable_delete_dmsf_workflow: 'Unable to delete the workflow'
error_empty_note: "The note can't be empty"
error_workflow_assign: 'An error occured while assigning'
label_dmsf_workflow_new: 'New approval workflow'
label_dmsf_workflow: 'Approval workflow'
label_dmsf_workflow_plural: 'Approval workflows'
label_dmsf_workflow_step: Step
label_dmsf_workflow_step_plural: Steps
label_dmsf_workflow_approval: Approval
label_dmsf_workflow_approval_plural: Approvals
label_dmsf: DMSF
label_dmsf_wokflow_action_approve: Approve
label_dmsf_wokflow_action_reject: Reject
label_dmsf_wokflow_action_delegate: 'Delegate to'
label_dmsf_wokflow_action_assign: 'Assign an approval workflow'
label_dmsf_wokflow_action_start: 'Start workflow'
label_action: Action
label_note: Note
title_draft: Draft
title_rejected: Rejected
title_delegated: Delegated
title_assigned: Assigned
title_started: Started
title_dmsf_workflow_log: 'Approval workflow log'
dmsf_and: AND
dmsf_or: OR
dmsf_new_step: New step
message_dmsf_wokflow_note: Your note...
info_revision: "r%{rev}"
link_workflow: Workflow

View File

@ -100,4 +100,21 @@ 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'
get 'assign'
get 'log'
post 'new_action'
post 'start'
post 'assignment'
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,44 @@
# 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 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
change_table :dmsf_file_revisions do |t|
t.references :dmsf_workflow
t.integer :dmsf_workflow_assigned_by
t.datetime :dmsf_workflow_assigned_at
t.integer :dmsf_workflow_started_by
t.datetime :dmsf_workflow_started_at
end
end
def self.down
remove_column :dmsf_file_revisions, :dmsf_workflow_id
remove_column :dmsf_file_revisions, :dmsf_workflow_assigned_by
remove_column :dmsf_file_revisions, :dmsf_workflow_assigned_at
remove_column :dmsf_file_revisions, :dmsf_workflow_started_by
remove_column :dmsf_file_revisions, :dmsf_workflow_started_at
drop_table :dmsf_workflows
end
end

View File

@ -0,0 +1,33 @@
# 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 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,35 @@
# 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 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,36 @@
# 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 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
t.integer :author_id, :null => false
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

21
init.rb
View File

@ -51,13 +51,26 @@ Redmine::Plugin.register :redmine_dmsf do
permission :view_dmsf_folders, {:dmsf => [:show], :dmsf_folders_copy => [:new, :copy_to, :move_to]}
permission :user_preferences, {:dmsf_state => [:user_pref_save]}
permission :view_dmsf_files, {:dmsf => [:entries_operation, :entries_email],
:dmsf_files => [:show], :dmsf_files_copy => [:new, :create, :move]}
:dmsf_files => [:show], :dmsf_files_copy => [:new, :create, :move]}
permission :folder_manipulation, {:dmsf => [:new, :create, :delete, :edit, :save, :edit_root, :save_root, :lock, :unlock]}
permission :file_manipulation, {:dmsf_files => [:create_revision, :delete, :lock, :unlock],
:dmsf_upload => [:upload_files, :upload_file, :commit_files]}
:dmsf_upload => [:upload_files, :upload_file, :commit_files]}
permission :file_approval, {:dmsf_files => [:delete_revision, :notify_activate, :notify_deactivate],
:dmsf => [:notify_activate, :notify_deactivate]}
permission :force_file_unlock, {}
:dmsf => [:notify_activate, :notify_deactivate],
:dmsf_workflows => [:new, :create, :destroy, :edit, :add_step, :remove_step, :reorder_steps, :update, :start, :assign, :assignment, :action, :new_action, :log]}
permission :force_file_unlock, {}
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 stylesheet tag
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