From 80dc2636466206de422a19ab6f579682b311e65d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Pi=C4=8Dman?= Date: Sun, 9 Aug 2015 07:52:29 +0200 Subject: [PATCH] approval process for 3 users 'user1 AND user2 AND user3' #432 --- app/controllers/dmsf_workflows_controller.rb | 19 +++--- app/models/dmsf_lock.rb | 18 +++--- app/models/dmsf_workflow_step.rb | 4 +- config/locales/cs.yml | 8 ++- config/locales/de.yml | 6 ++ config/locales/en.yml | 8 ++- config/locales/es.yml | 6 ++ config/locales/fr.yml | 6 ++ config/locales/ja.yml | 6 ++ config/locales/pl.yml | 6 ++ config/locales/pt-BR.yml | 6 ++ config/locales/ru.yml | 6 ++ config/locales/sl.yml | 6 ++ config/locales/zh-TW.yml | 6 ++ config/locales/zh.yml | 6 ++ lib/redmine_dmsf/lockable.rb | 67 ++++++++++---------- test/unit/dmsf_workflow_step_test.rb | 10 +-- 17 files changed, 134 insertions(+), 60 deletions(-) diff --git a/app/controllers/dmsf_workflows_controller.rb b/app/controllers/dmsf_workflows_controller.rb index e360e9d7..d2b10192 100644 --- a/app/controllers/dmsf_workflows_controller.rb +++ b/app/controllers/dmsf_workflows_controller.rb @@ -49,9 +49,10 @@ class DmsfWorkflowsController < ApplicationController file = DmsfFile.joins(:revisions).where(:dmsf_file_revisions => {:id => revision.id}).first if file begin - file.unlock! + file.unlock! true rescue DmsfLockError => e - logger.warn e.message + flash[:info] = e.message + #logger.warn e.message end end if revision.workflow == DmsfWorkflow::STATE_APPROVED @@ -287,18 +288,18 @@ class DmsfWorkflowsController < ApplicationController step = params[:step].to_i end operator = (params[:commit] == l(:dmsf_and)) ? DmsfWorkflowStep::OPERATOR_AND : DmsfWorkflowStep::OPERATOR_OR - users = User.where(:id => params[:user_ids]) + users = User.where(:id => params[:user_ids]).to_a if users.count > 0 users.each do |user| - ws = DmsfWorkflowStep.new( - :dmsf_workflow_id => @dmsf_workflow.id, - :step => step, - :user_id => user.id, - :operator => operator) + ws = DmsfWorkflowStep.new + ws.dmsf_workflow_id = @dmsf_workflow.id + ws.step = step + ws.user_id = user.id + ws.operator = operator if ws.save @dmsf_workflow.dmsf_workflow_steps << ws else - flash[:error] = @dmsf_workflow.errors.full_messages.to_sentence + flash[:error] = ws.errors.full_messages.to_sentence end end else diff --git a/app/models/dmsf_lock.rb b/app/models/dmsf_lock.rb index 2f427311..74d68372 100644 --- a/app/models/dmsf_lock.rb +++ b/app/models/dmsf_lock.rb @@ -1,8 +1,10 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-14 Karel Picman +# Copyright (C) 2011-15 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,20 +28,20 @@ class DmsfLock < ActiveRecord::Base belongs_to :folder, :class_name => 'DmsfFolder', :foreign_key => 'entity_id' belongs_to :user - #At the moment apparently we're only supporting a write lock? + # At the moment apparently we're only supporting a write lock? as_enum :lock_type, [:type_write, :type_other] as_enum :lock_scope, [:scope_exclusive, :scope_shared] - # We really loosly bind the value in the belongs_to above + # We really loosely bind the value in the belongs_to above # here we just ensure the data internal to the model is correct # to ensure everything lists fine - it's the same as a join - # just without runing the join in the first place + # just without running the join in the first place def file entity_type == 0 ? super : nil; end - # see file, exact same scenario + # See the file, exact same scenario def folder entity_type == 1 ? super : nil; end @@ -57,11 +59,11 @@ class DmsfLock < ActiveRecord::Base self.delete_all ["#{DmsfLock.table_name}.expires_at IS NOT NULL && #{DmsfLock.table_name}.expires_at < ?", Time.now] end - #Lets allow our UUID to be searchable + # Let's allow our UUID to be searchable def self.find(*args) if args.first && args.first.is_a?(String) && !args.first.match(/^\d*$/) lock = find_by_uuid(*args) - raise ActiveRecord::RecordNotFound, "Couldn't find lock with uuid=#{args.first}" if lock.nil? + raise ActiveRecord::RecordNotFound, "Couldn't find lock with uuid = #{args.first}" if lock.nil? lock else super @@ -72,4 +74,4 @@ class DmsfLock < ActiveRecord::Base self.find(*args) end -end +end \ No newline at end of file diff --git a/app/models/dmsf_workflow_step.rb b/app/models/dmsf_workflow_step.rb index 0374ac5b..8ce5c857 100644 --- a/app/models/dmsf_workflow_step.rb +++ b/app/models/dmsf_workflow_step.rb @@ -25,9 +25,7 @@ class DmsfWorkflowStep < ActiveRecord::Base validates :step, :presence => true validates :user_id, :presence => true validates :operator, :presence => true - validates_uniqueness_of :user_id, :scope => [:dmsf_workflow_id, :step] - - attr_accessible :dmsf_workflow_id, :step, :user_id, :operator + validates_uniqueness_of :user_id, :scope => [:dmsf_workflow_id, :step] OPERATOR_OR = 0 OPERATOR_AND = 1 diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 0ecd5e08..68f8f220 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -27,7 +27,7 @@ cs: label_dmsf_file_revision_access_plural: Přístupy k dokumentům warning_no_entries_selected: Není nic vybráno error_email_to_must_be_entered: Musí být zadán adresát - warning_file_already_locked: Soubor už je zamčen + warning_file_already_locked: Soubor je již zamčen notice_file_locked: Soubor byl zamčen warning_file_not_locked: Soubor není zamčen notice_file_unlocked: Soubor byl odemčen @@ -317,6 +317,12 @@ cs: notice_dmsf_link_restored: Odkaz byl úspěšně obnoven title_restore_checked: Obnov vybrané error_parent_folder: Nadřazený adresář neexistuje + + error_resource_or_parent_locked: Nelze zamknout - zdrojový nebo nadřazený objekt je zamčený + error_parent_locked: Nelze zamknout - nadřazený objekt je zamčený + error_resource_locked: Nelze zamknout - zdrojový objekt je zamčený + error_lock_exclusively: Nelze zamknout již zamčený objekt + error_unlock_parent_locked: Nelze odemknout - nadřazený objekt je zamčený my: blocks: diff --git a/config/locales/de.yml b/config/locales/de.yml index 340b5e3f..89e4b6f0 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -318,6 +318,12 @@ de: title_restore_checked: Restore checked error_parent_folder: "The parent folder doesn't exist" + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked + error_parent_locked: Unable to complete lock - resource parent is locked + error_resource_locked: Unable to complete lock - resource is locked + error_lock_exclusively: unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked + my: blocks: locked_documents: Gesperrte Dateien diff --git a/config/locales/en.yml b/config/locales/en.yml index 36aa7b7f..d9555d78 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -31,7 +31,7 @@ en: notice_file_locked: File locked warning_file_not_locked: File not locked notice_file_unlocked: File unlocked - error_only_user_that_locked_file_can_unlock_it: Only user that locked file can unlock it + error_only_user_that_locked_file_can_unlock_it: Only user that locked the file can unlock it error_max_files_exceeded: "Limit for %{number} simultaneously downloaded files exceeded" error_entry_project_does_not_match_current_project: "Entry project doesn't match current project" notice_folder_created: Folder created @@ -318,6 +318,12 @@ en: title_restore_checked: Restore checked error_parent_folder: "The parent folder doesn't exist" + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked + error_parent_locked: Unable to complete lock - resource parent is locked + error_resource_locked: Unable to complete lock - resource is locked + error_lock_exclusively: unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked + my: blocks: locked_documents: Locked documents diff --git a/config/locales/es.yml b/config/locales/es.yml index 2586673d..c183d11e 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -318,6 +318,12 @@ es: title_restore_checked: Restaurar Seleccionados error_parent_folder: "El directorio padre no existe" + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked + error_parent_locked: Unable to complete lock - resource parent is locked + error_resource_locked: Unable to complete lock - resource is locked + error_lock_exclusively: unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked + my: blocks: locked_documents: Documentos bloqueados diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 65819018..03a5e1a4 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -317,6 +317,12 @@ fr: notice_dmsf_link_restored: Le lien a été récupéré avec succès title_restore_checked: Restauration vérifiée error_parent_folder: "Le dossier parent n'existe pas" + + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked + error_parent_locked: Unable to complete lock - resource parent is locked + error_resource_locked: Unable to complete lock - resource is locked + error_lock_exclusively: unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked my: blocks: diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 85154051..b1cb870e 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -318,6 +318,12 @@ ja: title_restore_checked: チェックしたものを復元します error_parent_folder: "親フォルダが存在しません" + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked + error_parent_locked: Unable to complete lock - resource parent is locked + error_resource_locked: Unable to complete lock - resource is locked + error_lock_exclusively: unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked + my: blocks: locked_documents: ロック中 diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 10d043f8..dbea1855 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -318,6 +318,12 @@ pl: title_restore_checked: Restore checked error_parent_folder: "Folder nadrzędny nie istnieje" + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked + error_parent_locked: Unable to complete lock - resource parent is locked + error_resource_locked: Unable to complete lock - resource is locked + error_lock_exclusively: unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked + my: blocks: locked_documents: Dokumenty zablokowane diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index ce1cbdc4..54186729 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -316,6 +316,12 @@ en: notice_dmsf_link_restored: The link has been successfully restored title_restore_checked: Restore checked error_parent_folder: "The parent folder doesn't exist" + + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked + error_parent_locked: Unable to complete lock - resource parent is locked + error_resource_locked: Unable to complete lock - resource is locked + error_lock_exclusively: unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked my: blocks: diff --git a/config/locales/ru.yml b/config/locales/ru.yml index ab3ecf4f..ddb9b129 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -318,6 +318,12 @@ ru: title_restore_checked: Restore checked error_parent_folder: "The parent folder doesn't exist" + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked + error_parent_locked: Unable to complete lock - resource parent is locked + error_resource_locked: Unable to complete lock - resource is locked + error_lock_exclusively: unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked + my: blocks: locked_documents: Locked documents diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 2d26d65f..1e7b84e4 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -318,6 +318,12 @@ sl: title_restore_checked: Restore checked error_parent_folder: "The parent folder doesn't exist" + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked + error_parent_locked: Unable to complete lock - resource parent is locked + error_resource_locked: Unable to complete lock - resource is locked + error_lock_exclusively: unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked + my: blocks: locked_documents: Locked documents diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 9fdfa073..0813357c 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -317,6 +317,12 @@ zh-TW: notice_dmsf_link_restored: The link has been successfully restored title_restore_checked: Restore checked error_parent_folder: "The parent folder doesn't exist" + + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked + error_parent_locked: Unable to complete lock - resource parent is locked + error_resource_locked: Unable to complete lock - resource is locked + error_lock_exclusively: unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked my: blocks: diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 9ffbaee9..61363220 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -318,6 +318,12 @@ zh: title_restore_checked: Restore checked error_parent_folder: "The parent folder doesn't exist" + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked + error_parent_locked: Unable to complete lock - resource parent is locked + error_resource_locked: Unable to complete lock - resource is locked + error_lock_exclusively: unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked + my: blocks: locked_documents: Locked documents diff --git a/lib/redmine_dmsf/lockable.rb b/lib/redmine_dmsf/lockable.rb index 8055fe12..1c47d0f0 100644 --- a/lib/redmine_dmsf/lockable.rb +++ b/lib/redmine_dmsf/lockable.rb @@ -1,7 +1,10 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-15 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -30,9 +33,9 @@ module RedmineDmsf def lock(tree = true) ret = [] unless locks.empty? - locks.each {|lock| + locks.each do |lock| ret << lock unless lock.expired? - } + end end if tree ret = ret | (folder.locks.empty? ? folder.lock : folder.locks) unless folder.nil? @@ -43,20 +46,20 @@ module RedmineDmsf def lock! scope = :scope_exclusive, type = :type_write, expire = nil # Raise a lock error if entity is locked, but its not at resource level existing = locks(false) - raise DmsfLockError.new("Unable to complete lock - resource (or parent) is locked") if self.locked? && existing.empty? + raise DmsfLockError.new(l(:error_resource_or_parent_locked)) if self.locked? && existing.empty? unless existing.empty? if existing[0].lock_scope == :scope_shared && scope == :scope_shared # RFC states if an item is exclusively locked and another lock is attempted we reject # if the item is shared locked however, we can always add another lock to it if self.folder.locked? - raise DmsfLockError.new("Unable to complete lock - resource parent is locked") + raise DmsfLockError.new(l(:error_parent_locked)) else - existing.each {|l| - raise DmsfLockError.new("Unable to complete lock - resource is locked") if l.user.id == User.current.id - } + existing.each do |l| + raise DmsfLockError.new(l(:error_resource_locked)) if l.user.id == User.current.id + end end else - raise DmsfLockError.new("unable to lock exclusively an already-locked resource") if scope == :scope_exclusive + raise DmsfLockError.new(l(:error_lock_exclusively)) if scope == :scope_exclusive end end l = DmsfLock.new @@ -75,69 +78,67 @@ module RedmineDmsf def unlockable? return false unless self.locked? existing = self.lock(true) - return false if existing.empty? || (!self.folder.nil? && self.folder.locked?) #If its empty its a folder thats locked (not root) - true + # If its empty its a folder that's locked (not root) + (existing.empty? || (!self.folder.nil? && self.folder.locked?)) ? false : true end # # By using the path upwards, surely this would be quicker? - def locked_for_user?(tree = true) + def locked_for_user? return false unless locked? b_shared = nil heirarchy = self.dmsf_path - heirarchy.each {|folder| + heirarchy.each do |folder| locks = folder.locks || folder.lock(false) next if locks.empty? - locks.each {|lock| - next if lock.expired? #Incase we're inbetween updates + locks.each do |lock| + next if lock.expired? # In case we're in between updates if (lock.lock_scope == :scope_exclusive && b_shared.nil?) return true if (!lock.user) || (lock.user.id != User.current.id) else b_shared = true if b_shared.nil? b_shared = false if lock.user.id == User.current.id end - } + end return true if b_shared - } + end false end - - def unlock! - raise DmsfLockError.new("Unable to complete unlock - requested resource is not reported locked") unless self.locked? + + def unlock!(force_file_unlock_allowed = false) + raise DmsfLockError.new(l(:warning_file_not_locked)) unless self.locked? existing = self.lock(true) if existing.empty? || (!self.folder.nil? && self.folder.locked?) #If its empty its a folder thats locked (not root) - raise DmsfLockError.new("Unlock failed - resource parent is locked") + raise DmsfLockError.new(l(:error_unlock_parent_locked)) else - # If entity is locked to you, you arent the lock originator (or named in a shared lock) so deny action + # If entity is locked to you, you aren't the lock originator (or named in a shared lock) so deny action # Unless of course you have the rights to force an unlock - raise DmsfLockError.new("Unlock failed - resource is locked by another user") if ( - self.locked_for_user?(false) && - !User.current.allowed_to?(:force_file_unlock, self.project)) + raise DmsfLockError.new(l(:error_only_user_that_locked_file_can_unlock_it)) if ( + self.locked_for_user? && !User.current.allowed_to?(:force_file_unlock, self.project) && !force_file_unlock_allowed) - #Now we need to determine lock type and do the needful + # Now we need to determine lock type and do the needful if (existing.count == 1 && existing[0].lock_scope == :exclusive) existing[0].destroy else b_destroyed = false - existing.each {|lock| + existing.each do |lock| if (lock.user && (lock.user.id == User.current.id)) || User.current.admin? lock.destroy b_destroyed = true break end - } + end # At first it was going to be allowed for someone with force_file_unlock to delete all shared by default - # Instead, they by default remove themselves from sahred lock, and everyone from shared lock if they're not + # Instead, they by default remove themselves from shared lock, and everyone from shared lock if they're not # on said lock - if (!b_destroyed && User.current.allowed_to?(:force_file_unlock, self.project)) + if (!b_destroyed && (User.current.allowed_to?(:force_file_unlock, self.project) || force_file_unlock_allowed)) locks.delete_all end end end - reload locks.reload end true end -end +end \ No newline at end of file diff --git a/test/unit/dmsf_workflow_step_test.rb b/test/unit/dmsf_workflow_step_test.rb index 6f0271d9..a9eba1a6 100644 --- a/test/unit/dmsf_workflow_step_test.rb +++ b/test/unit/dmsf_workflow_step_test.rb @@ -35,11 +35,11 @@ class DmsfWorkflowStepTest < RedmineDmsf::Test::UnitTest end def test_create - wfs = DmsfWorkflowStep.new( - :dmsf_workflow_id => 1, - :step => 2, - :user_id => 3, - :operator => 1) + wfs = DmsfWorkflowStep.new + wfs.dmsf_workflow_id = 1 + wfs.step = 2 + wfs.user_id = 3 + wfs.operator = 1 assert wfs.save end