diff --git a/CHANGELOG.md b/CHANGELOG.md index 123c91fb..95bdc3c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,14 @@ Changelog for Redmine DMSF ========================== +2.4.11 *????-??-??* +------------------- + + REST API + Copy and move of files and folders + 2.4.10 *2021-10-20* ------------------- +------------------- German and English localisation improvement @@ -20,7 +26,8 @@ Changelog for Redmine DMSF 2.4.8 *2021-10-08* ------------------ - REST API - Create a revision, updating custom fields + REST API + Create a revision, updating custom fields Bug fixes * Bug: #1290 - Column is not shown. GUI is not changed @@ -46,8 +53,8 @@ Changelog for Redmine DMSF Bug fixes -* Bug: #1251 DMSF Security Vulnerability -* Bug: 1249 Fix Version field interpreting int as BigDecimal +* Bug: #1251 - DMSF Security Vulnerability +* Bug: #1249 - Bug Version field interpreting int as BigDecimal 2.4.6 *2021-04-30* ------------------ @@ -72,7 +79,7 @@ Changelog for Redmine DMSF * New: #1209 - Added some translations // Fixed typo * New: #1207 - Sub-projects as sub-folders * New: #1206 - Support for .xlsm files in Edit content -* New - #1204: Empty trash bin function is missing +* New: #1204 - Empty trash bin function is missing * New: #1201 - Givable roles for folder's permissions * New: #1200 - Improvement of german translations * New: #1199 - Breakdown structure of folders if a filter is set @@ -101,7 +108,7 @@ Changelog for Redmine DMSF * Bug: #1163 - Folder visible via webdav but not via UI * Bug: #1159 - Approval workflow log not available for non-admin users * Bug: #1156 - Editing a document also changes its title -* Bug: #1155 - Fix easy context menu +* Bug: #1155 - Bug easy context menu * Bug: #1150 - Uploading big files causes no memory exception * New: #1145 - Folder can not be deleted if the folder contains files or folders * New: #1136 - WebDAV tree structure including sub-projects duplicate @@ -151,7 +158,7 @@ Changelog for Redmine DMSF Compatibility with Redmine 4.1 Chinese localisation updated -* New: #1072 - Fix deprecation multiple gemfile sources +* New: #1072 - Bug deprecation multiple gemfile sources * New: #1069 - Minor version is limited to 99 max - I recommend to change the limit to 999 * New: #1068 - [travis] test redmine 4.1.0 * New: #1067 - update redmine extensions @@ -169,7 +176,7 @@ Changelog for Redmine DMSF * Bug: #1038 - Webdav not open file * Bug: #932 - Undefined method 'to_prepare' for ActionDispatch::Reloader:Class (Redmine 4.0 / Rails 5) * Bug: #913 - ActionController::RoutingError (No route matches [PROPFIND] "/") -* New #908 - Wrapping problem in Issue view +* New: #908 - Wrapping problem in Issue view 2.4.1 *2019-09-13* ------------------ @@ -493,7 +500,7 @@ IMPORTANT * Bug: #465 - Install using debian 8 (jessie) * Bug: #459 - WebDav Windows * Bug: #458 - Cannot upload big files -* New #44 - Append File Revision on filename when downloading file +* New: #44 - Append File Revision on filename when downloading file Maintenance release II. @@ -515,13 +522,13 @@ IMPORTANT File name updating when a new content is uploaded System files filtering when working with WebDAV - * Bug: #442 - Can't move directories - * Bug: #441 - Drag'n'drop save only the picture thumbnail - * New: #438 - The file name update when a new content is uploaded - * Bug: #418 - Documents details from in IE - * Bug: #417 - Selected column is not highlighted under mouse pointer in IE - * New: #352 - DMSF Macro to display inline Pictures in wiki - * New: #54 - Webdav: Filter Mac OS X "resource forks" files +* Bug: #442 - Can't move directories +* Bug: #441 - Drag'n'drop save only the picture thumbnail +* New: #438 - The file name update when a new content is uploaded +* Bug: #418 - Documents details from in IE +* Bug: #417 - Selected column is not highlighted under mouse pointer in IE +* New: #352 - DMSF Macro to display inline Pictures in wiki +* New: #54 - Webdav: Filter Mac OS X "resource forks" files 1.5.3 *2015-08-10* ------------------ @@ -595,188 +602,188 @@ IMPORTANT * Bug: #372 - Can't move file via WebDav 1.4.9: *2014-10-17* ------------------------ +------------------- Trash bin Standard Redmine's upload form with progress bar for files > 100 MB WebDAV library upgrade - * New: #130 - redmine_dmsf: last update of the folders - * Fix: #131 - Wiki link shows filename for all users type - * New: #136 - `File Manipulation` permissions - * New: #218 - Feature request: Recycle bin - * Fix: #226 - Undefined method `custom_fields_tabs` for module `CustomFieldsHelper` - * New: #238 - DMSF document update shows up in issue referred to in comment - * New: #249 - Storage path for DMSF files ignores global storage path for attachments - * New: #255 - Debian - Readme install procedure update - * Fix: #258 - Jquery conflict with Redmine - * Fix: #267 - Custom fields tabs not work with last custom_fields_helper_patch.rb - * Fix: #269 - Workflow OR not working for second reviewer - * Fix: #270 - 500 Internal Server Error, redmine 2.5.1, MS SQL Server 2012, dmsf 1.4.8-master, dmsf_link.rb - * Fix: #275 - Typo in readme file type - * Fix: #288 - ubuntu migrate failed - * Fix: #290 - error installing plugin - * Fix: #293 - Locking of inexistent files fails - * Fix: #298 - The same approver in one approval step - * Update: #301 - Database normalization +* New: #130 - redmine_dmsf: last update of the folders +* Bug: #131 - Wiki link shows filename for all users type +* New: #136 - `File Manipulation` permissions +* New: #218 - Feature request: Recycle bin +* Bug: #226 - Undefined method `custom_fields_tabs` for module `CustomFieldsHelper` +* New: #238 - DMSF document update shows up in issue referred to in comment +* New: #249 - Storage path for DMSF files ignores global storage path for attachments +* New: #255 - Debian - Readme install procedure update +* Bug: #258 - Jquery conflict with Redmine +* Bug: #267 - Custom fields tabs not work with last custom_fields_helper_patch.rb +* Bug: #269 - Workflow OR not working for second reviewer +* Bug: #270 - 500 Internal Server Error, redmine 2.5.1, MS SQL Server 2012, dmsf 1.4.8-master, dmsf_link.rb +* Bug: #275 - Typo in readme file type +* Bug: #288 - ubuntu migrate failed +* Bug: #290 - error installing plugin +* Bug: #293 - Locking of inexistent files fails +* Bug: #298 - The same approver in one approval step +* Update: #301 - Database normalization 1.4.8: *2014-04-17* ------------------------ +------------------- Symbolic links Document tagging Localization of email notifications An option to send document links by email - * New: Issue #19 - Documentation? - * Update: Issue #106 - [Feature Request] Save files in folder structure defined via DMSF - * Fix: Issue #107 - Problems upgrading redmine 1.3 to 2.23 regarding DMFS - * Fix: Issue #111 - Cannot sort files in folders by date, size, etc - * Update: Issue #139 - Error 500 on click on "details" icon - * New: Issue #183 - Create document links - * New: Issue #201 - Download link by email - * Fix: Issue #205 - Ampersand shows up in displayed filenames as "&" instead of "&" - * Fix: Issue #212 - Incorrect revision information in email notification - * Fix: Issue #214 - Required DMSF custom field prevents documents to be saved - * Update: Issue #216 - Enhancement : having notification emails translated - * Update: Issue #224 - Setup/Upgrade documentation - * Fix: Issue #226 - undefined method `custom_fields_tabs` for module `CustomFieldsHelper` - * Fix: Issue #233 - Failed Travis builds - * Update: Issue #235 - "You are not member of the project" when changing project notification. - * New: Issue #236 - Documents tagging - * Fix: Issue #240 - Internal server error, redmine 2.5.1-devel.13064, PostgreSQL, dmsf 1.4.8-devel - * Fix: Issue #242 - dsmf 1.4.8 minor ... "link form" tab - * Fix: Issue #246 - "File storage directory" does not default properly when setting is empty +* New: #19 - Documentation? +* New: #106 - [Feature Request] Save files in folder structure defined via DMSF +* Bug: #107 - Problems upgrading redmine 1.3 to 2.23 regarding DMFS +* Bug: #111 - Cannot sort files in folders by date, size, etc +* Bug: #139 - Error 500 on click on "details" icon +* New: #183 - Create document links +* New: #201 - Download link by email +* Bug: #205 - Ampersand shows up in displayed filenames as "&" instead of "&" +* Bug: #212 - Incorrect revision information in email notification +* Bug: #214 - Required DMSF custom field prevents documents to be saved +* New: #216 - Enhancement : having notification emails translated +* New: #224 - Setup/Upgrade documentation +* Bug: #226 - undefined method `custom_fields_tabs` for module `CustomFieldsHelper` +* Bug: #233 - Failed Travis builds +* New: #235 - "You are not member of the project" when changing project notification. +* New: #236 - Documents tagging +* Bug: #240 - Internal server error, redmine 2.5.1-devel.13064, PostgreSQL, dmsf 1.4.8-devel +* Bug: #242 - dsmf 1.4.8 minor ... "link form" tab +* Bug: #246 - "File storage directory" does not default properly when setting is empty 1.4.7: *2014-01-02* ------------------------ +------------------- Open approvals in My page Custom fields Speeding up Code revision -* New: Issue #38 - A few questions about the plugin (possible improvements) -* New: Issue #49 - Make the 100 MB ajax upload limit an option -* Fix: Issue #52 - Error : undefined method `size' for nil:NilClass -* Fix: Issue #90 - Missing redmine_dmsf / assets / javascripts / plupload / i18n /en.js file? -* Fix: Issue #94 - Files not deleted with project -* Fix: Issue #95 - DMSF tab missing on closed projects -* Fix: Issue #104 - Custom fields do not work -* Fix: Issue #141 - Error 500 uploading file with DMSF custom fields -* Fix: Issue #159 - Broken links caused by plugin_asset_path implementation -* New: Issue #173 - Open approvals in My page -* Fix: Issue #174 - Workflow error when more than one approver -* Fix: Issue #175 - Error 500 on performing search -* Fix: Issue #176 - 500 internal error when approving workflow - dmsf_workflows/4/new_action -* Fix: Issue #177 - 1.4.7-devel unable to upload files -* Fix: Issue #178 - Error 500 cannot access Administration -> Custom Fields page -* New: Issue #179 - Workflow Log History in Detailed View -* Fix: Issue #187 - Approval workflow permissions -* New: Issue #190 - Very slow in directories containing many files -* Fix: Issue #191 - Move/Copy gives undefined method for File:Class type: bug -* New: Issue #193 - French translation -* Fix: Issue #194 - Workflow name link in workflow log window -* Fix: Issue #195 - Workflow log not displaying all the steps -* New: Issue #196 - Update French Language -* Fix: Issue #197 - Multi upload not loading the translation -* New: Issue #198 - When editing a workflow, only show current project's users -* Fix: Issue #199 - Small error in plugin_asset_path function -* New: Issue #200 - Update the french translation for the multi upload module -* Fix: Issue #202 - unable to create Custom Field when DMSF plugin installed -* Fix: Issue #203 - Little typing error in french translation -* Fix: Issue #206 - "Select All" checkbox not functioning -* Fix: Issue #207 - locks by deleted users producte internal server error 500 +* New: #38 - A few questions about the plugin (possible improvements) +* New: #49 - Make the 100 MB ajax upload limit an option +* Bug: #52 - Error : undefined method `size' for nil:NilClass +* Bug: #90 - Missing redmine_dmsf / assets / javascripts / plupload / i18n /en.js file? +* Bug: #94 - Files not deleted with project +* Bug: #95 - DMSF tab missing on closed projects +* Bug: #104 - Custom fields do not work +* Bug: #141 - Error 500 uploading file with DMSF custom fields +* Bug: #159 - Broken links caused by plugin_asset_path implementation +* New: #173 - Open approvals in My page +* Bug: #174 - Workflow error when more than one approver +* Bug: #175 - Error 500 on performing search +* Bug: #176 - 500 internal error when approving workflow - dmsf_workflows/4/new_action +* Bug: #177 - 1.4.7-devel unable to upload files +* Bug: #178 - Error 500 cannot access Administration -> Custom Fields page +* New: #179 - Workflow Log History in Detailed View +* Bug: #187 - Approval workflow permissions +* New: #190 - Very slow in directories containing many files +* Bug: #191 - Move/Copy gives undefined method for File:Class type: bug +* New: #193 - French translation +* Bug: #194 - Workflow name link in workflow log window +* Bug: #195 - Workflow log not displaying all the steps +* New: #196 - Update French Language +* Bug: #197 - Multi upload not loading the translation +* New: #198 - When editing a workflow, only show current project's users +* Bug: #199 - Small error in plugin_asset_path function +* New: #200 - Update the french translation for the multi upload module +* Bug: #202 - unable to create Custom Field when DMSF plugin installed +* Bug: #203 - Little typing error in french translation +* Bug: #206 - "Select All" checkbox not functioning +* Bug: #207 - locks by deleted users producte internal server error 500 1.4.6: *2013-10-18* ------------------------ +------------------- * New: Document approval workflow * New: Slovene language translation -* Update: German language translation -* Revisit: Issue #34 - fix does not function as expected on Rails < 3.2.6, Redmine 2.0.3 dependency added. -* Fix: Issue #75 - Wrong filename encoding in emailed zip file -* Fix: Issue #87 - RoutingError (No route matches [GET] "/javascripts/jstoolbar/lang/jstoolbar-en-IS.js"): -* Fix: Issue #103 - Multiple DMSF tabs in Administration->Custom fields & localization -* Fix: Issue #110 - 'zip' gem conflicts with 'rubyzip' on Redmine XLS Export Plugin. -* Fix: Issue #112 - Uninstall command -* Fix: Issue #116 - Translation missing for DMSF custom field tabs -* Fix: Issue #146 - Problem with Russian file names in zip -* Fix: Issue #143 - Error on missing template - has to have to_s if adding to string -* Fix: Issue #148 - I don't have a notification sent out when I upload a file -* Fix: Issue #157 - Copying files/folders from one project to another project +* New: German language translation +* Bug: #34 - fix does not function as expected on Rails < 3.2.6, Redmine 2.0.3 dependency added. +* Bug: #75 - Wrong filename encoding in emailed zip file +* Bug: #87 - RoutingError (No route matches [GET] "/javascripts/jstoolbar/lang/jstoolbar-en-IS.js"): +* Bug: #103 - Multiple DMSF tabs in Administration->Custom fields & localization +* Bug: #110 - 'zip' gem conflicts with 'rubyzip' on Redmine XLS Export Plugin. +* Bug: #112 - Uninstall command +* Bug: #116 - Translation missing for DMSF custom field tabs +* Bug: #146 - Problem with Russian file names in zip +* Bug: #143 - Error on missing template - has to have to_s if adding to string +* Bug: #148 - I don't have a notification sent out when I upload a file +* Bug: #157 - Copying files/folders from one project to another project 1.4.5: *2012-07-20* ------------------ +------------------- * New: Settings introduced to enable read-only or read-write stance to be taken with webdav -* Fix: Issue #27 - incorrect call to display column information from database (redmine 1.x fragment). -* Fix: Issue #28 - incompatible SQL in db migration script for postgresql -* Fix: Issue #23 - Incorrect call to to_s for displaying time in certain views -* Fix: Issue #24 - Incorrect times shown on revision history / documents -* Fix: Issue #25 - Character in init.rb stops execution -* Fix: Issue #34 - Incorrect scope when accessing deleted files prevented notification. +* Bug: #27 - incorrect call to display column information from database (redmine 1.x fragment). +* Bug: #28 - incompatible SQL in db migration script for postgresql +* Bug: #23 - Incorrect call to to_s for displaying time in certain views +* Bug: #24 - Incorrect times shown on revision history / documents +* Bug: #25 - Character in init.rb stops execution +* Bug: #34 - Incorrect scope when accessing deleted files prevented notification. 1.4.4p2: *2012-07-08* -------------------- +--------------------- -* Fix: Issue #22 - Webdav upload with passenger/nginx fails with server error (passenger class for request.body does not contain length method. -* Fix: Additional check implemented before reading settings to prevent server error when setting is not set and default does not apply. +* Bug: #22 - Webdav upload with passenger/nginx fails with server error (passenger class for request.body does not contain length method. +* Bug: Additional check implemented before reading settings to prevent server error when setting is not set and default does not apply. 1.4.4p1: *2012-07-07* -------------------- +--------------------- -* Fix: Issue #20 - Listing not functional when using sqlite adapter -* Fix: Issue #21 - Webdav not functional under bitnami (or sub directory) -* Fix: Testcase failed to cleanup after itself -* Fix: Webdav index object identified itself as having parent under prefix'ed path (in error) -* Fix: Addition of a path_prefix routine for webdav to be able to correct redirects +* Bug: #20 - Listing not functional when using sqlite adapter +* Bug: #21 - Webdav not functional under bitnami (or sub directory) +* Bug: Testcase failed to cleanup after itself +* Bug: Webdav index object identified itself as having parent under prefix'ed path (in error) +* Bug: Addition of a path_prefix routine for webdav to be able to correct redirects 1.4.4: *2012-07-01* ------------------ +------------------- * New: Locking model updated to support shared and exclusive write locks. [At present UI and Webdav only support exclusive locking however] * New: Folders are now write lockable (shared and exclusively) [UI upgraded to support folder locking, however only exclusively] * New: Locks can now have a time-limit [Not yet supported from UI] * New: Inereted lock support (locked folders child entries are now flagged as locked) -* Fix: Some testcases erroniously passed when files are locked, when they should be unlocked -* Update: Webdav locks files for 1 hour at a time (requested time is ignored) +* Bug: Some testcases erroniously passed when files are locked, when they should be unlocked +* New: Webdav locks files for 1 hour at a time (requested time is ignored) * New: Files are now stored in project relevent folder * New: Implementation of lockdiscovery and supportedlock property requests * New: Locks store a timestamp based UUID string enabling better interaction with webservices -* Fix: Issue #16 - unable to add new project when plugin enabled due to bug in UI -* Fix: Issue #17 - dav4rack not installable on some systems - it is now vendored -* Fix: Issue #18 - Warnings thrown due to space between function and parentheses +* Bug: #16 - unable to add new project when plugin enabled due to bug in UI +* Bug: #17 - dav4rack not installable on some systems - it is now vendored +* Bug: #18 - Warnings thrown due to space between function and parentheses 1.4.3: *2012-06-26* ------------------ +------------------- * New: Hook into project copy functionality to permit (although not attractively) functionality for DMSF to be duplicated accross projects -* Update: Project patch defines linkage between DMSF files and DMSF folders. -* Update: Data linkage allowing dependent items to be deleted (project deletion for example) +* New: Project patch defines linkage between DMSF files and DMSF folders. +* New: Data linkage allowing dependent items to be deleted (project deletion for example) this needs to be revised as files marked deleted are not affected by this at present -* Update: README.md updated with Bundler requirement (Issue #13) -* Fix: Error in entity details page UI prevented revision management. +* New: README.md updated with Bundler requirement (Issue #13) +* Bug: Error in entity details page UI prevented revision management. 1.4.2: *2012-06-21* ------------------ +------------------- * New: Integration test cases for webdav functionality -* Update: Documentation has been converted from Simpletext to Markdown -* Update: Features listed in documentation -* Fix: Issue #3 - "webdav broken until set in Administrator -> Settings" -* Fix: Issue #5 - "Webdav incorrectly provides empty listing for non-DMSF enabled projects" -* Fix: Issues identified by test cases +* New: Documentation has been converted from Simpletext to Markdown +* New: Features listed in documentation +* Bug: #3 - "webdav broken until set in Administrator -> Settings" +* Bug: #5 - "Webdav incorrectly provides empty listing for non-DMSF enabled projects" +* Bug: Issues identified by test cases 1.4.1: *2012-06-15* ------------------ +------------------- * New: DAV4Rack requirement added (Gemfile makes reference to github repository for latest release). * New: Webdav functionality included, additional administrative settings added -* Fixed: Issue #2 - extended xapian search fixed with Rails 3 compatible code. +* Bug: #2 - extended xapian search fixed with Rails 3 compatible code. 1.4.0: *2012-06-06* ------------------ +------------------- * New: Redmine 2.0 or higher is required \ No newline at end of file diff --git a/README.md b/README.md index 9d4aff92..89a389d9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Redmine DMSF Plugin =================== -The current version of Redmine DMSF is **2.4.10** +The current version of Redmine DMSF is **2.4.11 devel** Redmine DMSF is Document Management System Features plugin for Redmine issue tracking system; It is aimed to replace current Redmine's Documents module. diff --git a/app/controllers/dmsf_files_copy_controller.rb b/app/controllers/dmsf_files_copy_controller.rb index dda16acc..25016906 100644 --- a/app/controllers/dmsf_files_copy_controller.rb +++ b/app/controllers/dmsf_files_copy_controller.rb @@ -29,31 +29,56 @@ class DmsfFilesCopyController < ApplicationController before_action :find_target_folder before_action :check_target_folder, only: [:copy, :move] + accept_api_auth :copy, :move + def new @projects = DmsfFile.allowed_target_projects_on_copy @folders = DmsfFolder.directory_tree(@target_project, @folder) - @target_folder = DmsfFolder.visible.find(params[:target_folder_id]) unless params[:target_folder_id].blank? @back_url = params[:back_url] render layout: !request.xhr? end def copy new_file = @file.copy_to(@target_project, @target_folder) - if new_file.nil? || new_file.errors.present? + failure = new_file.nil? || new_file.errors.present? + if failure flash[:error] = new_file ? new_file.errors.full_messages.to_sentence : @file.errors.full_messages.to_sentence else flash[:notice] = l(:notice_successful_update) end - redirect_back_or_default dmsf_folder_path(id: @file.project, folder_id: @file.dmsf_folder) + respond_to do |format| + format.html do + redirect_back_or_default dmsf_folder_path(id: @file.project, folder_id: @file.dmsf_folder) + end + format.api do + if failure + render_validation_errors(new_file ? new_file : @file) + else + render_api_ok + end + end + end end def move - if @file.move_to(@target_project, @target_folder) + success = @file.move_to(@target_project, @target_folder) + if success flash[:notice] = l(:notice_successful_update) else flash[:error] = @file.errors.full_messages.to_sentence end - redirect_back_or_default dmsf_folder_path(id: @file.project, folder_id: @file.dmsf_folder) + respond_to do |format| + format.html do + redirect_back_or_default dmsf_folder_path(id: @file.project, folder_id: @file.dmsf_folder) + end + format.api do + if success + render_api_ok + else + render_validation_errors @file + end + end + end end private @@ -70,15 +95,18 @@ private end def find_target_folder - if params[:target_project_id].present? - @target_project = Project.find params[:target_project_id] - else - @target_project = @project - end if params[:target_folder_id].present? - @target_folder = DmsfFolder.visible.find(params[:target_folder_id]) - raise ActiveRecord::RecordNotFound unless DmsfFolder.visible.where(id: params[:target_folder_id]).exists? + target_folder_id = params[:target_folder_id] + elsif params[:dmsf_file_or_folder] && params[:dmsf_file_or_folder][:target_folder_id].present? + target_folder_id = params[:dmsf_file_or_folder][:target_folder_id] end + @target_folder = DmsfFolder.visible.find(target_folder_id) if target_folder_id + if params[:target_project_id].present? + target_project_id = params[:target_project_id] + elsif params[:dmsf_file_or_folder] && params[:dmsf_file_or_folder][:target_project_id].present? + target_project_id = params[:dmsf_file_or_folder][:target_project_id] + end + @target_project = target_project_id ? Project.visible.find(target_project_id) : @project rescue ActiveRecord::RecordNotFound render_404 end @@ -87,11 +115,11 @@ private if (@target_folder && @target_folder == @file.dmsf_folder) || (@target_folder.nil? && @file.dmsf_folder.nil? && @target_project == @file.project) flash[:error] = l(:error_target_folder_same) - redirect_to action: :new, id: @file, target_project_id: @target_project.id, target_folder_id: @target_folder + redirect_to action: :new, id: @file, target_project_id: @target_project&.id, target_folder_id: @target_folder return end - if (@target_folder && (@target_folder.locked_for_user? || !DmsfFolder.permissions?(@target_folder, false))) || - !@target_project.allows_to?(:file_manipulation) + if (@target_folder && (@target_folder.locked_for_user? || !DmsfFolder.permissions?(@target_folder, + false))) || !@target_project.allows_to?(:file_manipulation) raise DmsfAccessError end rescue DmsfAccessError diff --git a/app/controllers/dmsf_folders_copy_controller.rb b/app/controllers/dmsf_folders_copy_controller.rb index 30db49ad..61ad5948 100644 --- a/app/controllers/dmsf_folders_copy_controller.rb +++ b/app/controllers/dmsf_folders_copy_controller.rb @@ -29,6 +29,8 @@ class DmsfFoldersCopyController < ApplicationController before_action :find_target_folder before_action :check_target_folder, only: [:copy, :move] + accept_api_auth :copy, :move + def new @projects = DmsfFolder.allowed_target_projects_on_copy @folders = DmsfFolder.directory_tree(@target_project, @folder) @@ -39,21 +41,45 @@ class DmsfFoldersCopyController < ApplicationController def copy new_folder = @folder.copy_to(@target_project, @target_folder) - if new_folder.errors.empty? + success = new_folder.errors.empty? + if success flash[:notice] = l(:notice_successful_update) else flash[:error] = new_folder.errors.full_messages.to_sentence end - redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder.dmsf_folder) + respond_to do |format| + format.html do + redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder.dmsf_folder) + end + format.api do + if success + render_api_ok + else + render_validation_errors new_folder + end + end + end end def move - if @folder.move_to(@target_project, @target_folder) + success = @folder.move_to(@target_project, @target_folder) + if success flash[:notice] = l(:notice_successful_update) else flash[:error] = @folder.errors.full_messages.to_sentence end - redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder.dmsf_folder) + respond_to do |format| + format.html do + redirect_back_or_default dmsf_folder_path(id: @project, folder_id: @folder.dmsf_folder) + end + format.api do + if success + render_api_ok + else + render_validation_errors @folder + end + end + end end private @@ -70,14 +96,15 @@ class DmsfFoldersCopyController < ApplicationController end def find_target_folder - if params[:target_project_id].present? - @target_project = Project.find params[:target_project_id] + if params[:dmsf_file_or_folder] && params[:dmsf_file_or_folder][:target_project_id].present? + @target_project = Project.find params[:dmsf_file_or_folder][:target_project_id] else @target_project = @project end - if params[:target_folder_id].present? - @target_folder = DmsfFolder.find(params[:target_folder_id]) - raise ActiveRecord::RecordNotFound unless DmsfFolder.visible.where(id: params[:target_folder_id]).exists? + if params[:dmsf_file_or_folder] && params[:dmsf_file_or_folder][:target_folder_id].present? + target_folder_id = params[:dmsf_file_or_folder][:target_folder_id] + @target_folder = DmsfFolder.find(target_folder_id) + raise ActiveRecord::RecordNotFound unless DmsfFolder.visible.where(id: target_folder_id).exists? end rescue ActiveRecord::RecordNotFound render_404 diff --git a/app/views/dmsf_folders_copy/_form.html.erb b/app/views/dmsf_folders_copy/_form.html.erb index 0749f676..bd3c51f9 100644 --- a/app/views/dmsf_folders_copy/_form.html.erb +++ b/app/views/dmsf_folders_copy/_form.html.erb @@ -25,12 +25,14 @@ <%= hidden_field_tag 'back_url', back_url %>

- <%= label_tag 'target_project_id', l(:field_target_project) %> - <%= select_tag 'target_project_id', project_tree_options_for_select(projects, selected: target_project) %> + <%= label_tag 'dmsf_file_or_folder[target_project_id]', l(:field_target_project) %> + <%= select_tag 'dmsf_file_or_folder[target_project_id]', + project_tree_options_for_select(projects, selected: target_project) %>

- <%= label_tag 'target_folder_id', l(:field_target_folder) %> - <%= select_tag 'target_folder_id', options_for_select(folders, selected: (target_folder.id if target_folder)) %> + <%= label_tag 'dmsf_file_or_folder[target_folder_id]', l(:field_target_folder) %> + <%= select_tag 'dmsf_file_or_folder[target_folder_id]', + options_for_select(folders, selected: (target_folder.id if target_folder)) %>

diff --git a/extra/api/api_client.sh b/extra/api/api_client.sh index 43a348b1..7de7dfc7 100644 --- a/extra/api/api_client.sh +++ b/extra/api/api_client.sh @@ -37,20 +37,32 @@ #curl -v -H "Content-Type: application/xml" -X GET -u ${1}:${2} http://localhost:3000/dmsf/files/17216.xml #curl -v -H "Content-Type: application/octet-stream" -X GET -u ${1}:${2} http://localhost:3000/dmsf/files/41532/download > file.txt -# 3. Create a folder -#curl -v -H "Content-Type: application/xml" -X POST --data "@folder.xml" -u ${1}:${2} http://localhost:3000/projects/12/dmsf/create.xml - -# 4. Upload a document into a given folder or the root folder +# 3. Upload a document into a given folder or the root folder #curl --data-binary "@cat.gif" -H "Content-Type: application/octet-stream" -X POST -u ${1}:${2} http://localhost:3000/projects/12/dmsf/upload.xml?filename=cat.gif #curl -v -H "Content-Type: application/xml" -X POST --data "@file.xml" -u ${1}:${2} http://localhost:3000/projects/12/dmsf/commit.xml -# 5. Create a new revision +# 4. Create a new revision #curl -v -H "Content-Type: application/xml" -X POST --data "@revision.xml" -u ${1}:${2} http://localhost:3000/dmsf/files/232565/revision/create.xml -# 6. List folder content & check folder existence (by folder title) +# 5. Copy a document +#curl -v -H "Content-Type: application/xml" -X POST --data "@file_or_folder_copy_move.xml" -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000/dmsf/files/233313/copy/copy.xml + +# 6. Move a document +#curl -v -H "Content-Type: application/xml" -X POST --data "@file_or_folder_copy_move.xml" -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000/dmsf/files/233313/copy/move.xml + +# 7. Delete a document +# a) Move to trash only +# curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/dmsf/files/196118.xml +# b) Delete permanently +# curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/dmsf/files/196118.xml?commit=yes" + +# 8. Create a folder +#curl -v -H "Content-Type: application/xml" -X POST --data "@folder.xml" -u ${1}:${2} http://localhost:3000/projects/12/dmsf/create.xml + +# 9. List folder content & check folder existence (by folder title) # curl -v -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000/projects/1/dmsf.json?folder_title=Updated%20title -# 7. List folder content & check folder existence (by folder id) +# 10. List folder content & check folder existence (by folder id) # curl -v -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000/projects/1/dmsf.json?folder_id=3 # both returns 404 not found, or json with following structure: # { @@ -62,7 +74,7 @@ # } #} -# 8. Update a folder +# 11. Update a folder # curl -v -H "Content-Type: application/json" -X POST --data "@update-folder-payload.json" -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000//projects/#{project_id}/dmsf/save.json?folder_id=#{folder_id} # update-folder-payload.json @@ -73,17 +85,17 @@ # }, # } -# 9. Delete a folder +# 12. Copy a folder +#curl -v -H "Content-Type: application/xml" -X POST --data "@file_or_folder_copy_move.xml" -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000/dmsf/folders/53075/copy/copy.xml + +# 13. Move a folder +#curl -v -H "Content-Type: application/xml" -X POST --data "@file_or_folder_copy_move.xml" -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000/dmsf/folders/53075/copy/move.xml + +# 14. Delete a folder # a) Move to trash only # curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/projects/2387/dmsf/delete.xml?folder_id=#{folder_id} # b) Delete permanently # curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} "http://localhost:3000/projects/2387/dmsf/delete.xml?folder_id=#{folder_id}&commit=yes" -# 10. Delete a file -# a) Move to trash only -# curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/dmsf/files/196118.xml -# b) Delete permanently -# curl -v -H "Content-Type: application/xml" -X DELETE -u ${1}:${2} http://localhost:3000/dmsf/files/196118.xml?commit=yes" - -# 11. Create a symbolic link +# 15. Create a symbolic link # curl -v -H "Content-Type: application/xml" -X POST --data "@link.xml" -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000/dmsf_links.xml \ No newline at end of file diff --git a/extra/api/file_or_folder_copy_move.xml b/extra/api/file_or_folder_copy_move.xml new file mode 100644 index 00000000..32ab0806 --- /dev/null +++ b/extra/api/file_or_folder_copy_move.xml @@ -0,0 +1,5 @@ + + + 3057 + 53064 + \ No newline at end of file diff --git a/init.rb b/init.rb index 7f6f283f..31e6ca92 100644 --- a/init.rb +++ b/init.rb @@ -33,7 +33,7 @@ Redmine::Plugin.register :redmine_dmsf do end author 'Vít Jonáš / Daniel Munn / Karel Pičman' description 'Document Management System Features' - version '2.4.10' + version '2.4.11 devel' requires_redmine version_or_higher: '4.2.0' diff --git a/test/functional/dmsf_files_copy_controller_test.rb b/test/functional/dmsf_files_copy_controller_test.rb index d6d3a713..c573ed18 100644 --- a/test/functional/dmsf_files_copy_controller_test.rb +++ b/test/functional/dmsf_files_copy_controller_test.rb @@ -57,14 +57,14 @@ class DmsfFilesCopyControllerTest < RedmineDmsf::Test::TestCase end def test_target_folder - get :new, params: { id: @file1.id, target_folder_id: @folder1.id } + get :new, params: { id: @file1.id, dmsf_file_or_folder: { target_folder_id: @folder1.id } } assert_response :success assert_template 'new' end def test_target_folder_forbidden @role_manager.remove_permission! :view_dmsf_folders - get :new, params: { id: @file1.id, target_folder_id: @folder1.id } + get :new, params: { id: @file1.id, dmsf_file_or_folder: { target_folder_id: @folder1.id } } assert_response :not_found end @@ -81,79 +81,86 @@ class DmsfFilesCopyControllerTest < RedmineDmsf::Test::TestCase end def test_copy - post :copy, params: { id: @file1.id, target_project_id: @folder1.project.id, target_folder_id: @folder1.id } + post :copy, params: { id: @file1.id, target_project_id: @folder1.project.id, + dmsf_file_or_folder: { target_folder_id: @folder1.id } } assert_response :redirect assert_nil flash[:error] end def test_copy_the_same_target - post :copy, params: { id: @file1.id, target_project_id: @file1.project.id, target_folder_id: @file1.dmsf_folder } + post :copy, params: { id: @file1.id, + dmsf_file_or_folder: { target_project_id: @file1.project.id, target_folder_id: @file1.dmsf_folder } } assert_equal flash[:error], l(:error_target_folder_same) assert_redirected_to action: :new, target_project_id: @file1.project.id, target_folder_id: @file1.dmsf_folder end def test_copy_to_locked_folder @request.session[:user_id] = @admin.id - post :copy, params: { id: @file1.id, target_project_id: @folder2.project.id, target_folder_id: @folder2.id } + post :copy, params: { id: @file1.id, + dmsf_file_or_folder: { target_project_id: @folder2.project.id, target_folder_id: @folder2.id } } assert_response :forbidden end def test_copy_to_dmsf_not_enabled @project2.disable_module! :dmsf - post :copy, params: { id: @file1.id, target_project_id: @project2.id } + post :copy, params: { id: @file1.id, dmsf_file_or_folder: { target_project_id: @project2.id } } assert_response :forbidden end def test_copy_to_dmsf_enabled - post :copy, params: { id: @file1.id, target_project_id: @project2.id } + post :copy, params: { id: @file1.id, dmsf_file_or_folder: { target_project_id: @project2.id } } assert_response :redirect assert flash[:error].blank? end def test_copy_to_as_non_member @request.session[:user_id] = @someone.id - post :copy, params: { id: @file1.id, target_project_id: @project2.id } + post :copy, params: { id: @file1.id, dmsf_file_or_folder: { target_project_id: @project2.id } } assert_response :forbidden end def test_move - post :move, params: { id: @file1.id, target_project_id: @folder1.project.id, target_folder_id: @folder1.id } + post :move, params: { id: @file1.id, + dmsf_file_or_folder: { target_project_id: @folder1.project.id, target_folder_id: @folder1.id } } assert_response :redirect assert_nil flash[:error] end def test_move_the_same_target - post :move, params: { id: @file1.id, target_project_id: @file1.project.id, target_folder_id: @file1.dmsf_folder } + post :move, params: { id: @file1.id, + dmsf_file_or_folder: { target_project_id: @file1.project.id, target_folder_id: @file1.dmsf_folder } } assert_equal flash[:error], l(:error_target_folder_same) assert_redirected_to action: :new, target_project_id: @file1.project.id, target_folder_id: @file1.dmsf_folder end def test_move_locked_file - post :move, params: { id: @file2.id, target_project_id: @folder1.project.id, target_folder_id: @folder1.id } + post :move, params: { id: @file2.id, + dmsf_file_or_folder: { target_project_id: @folder1.project.id, target_folder_id: @folder1.id } } assert_response :forbidden end def test_move_to_locked_folder @request.session[:user_id] = @admin.id - post :move, params: { id: @file1.id, target_project_id: @folder2.project.id, target_folder_id: @folder2.id } + post :move, params: { id: @file1.id, + dmsf_file_or_folder: { target_project_id: @folder2.project.id, target_folder_id: @folder2.id } } assert_response :forbidden end def test_move_to_dmsf_not_enabled @project2.disable_module! :dmsf - post :move, params: { id: @file9.id, target_project_id: @project2.id } + post :move, params: { id: @file9.id, dmsf_file_or_folder: { target_project_id: @project2.id } } assert_response :forbidden end def test_move_to_dmsf_enabled - post :move, params: { id: @file9.id, target_project_id: @project2.id } + post :move, params: { id: @file9.id, dmsf_file_or_folder: { target_project_id: @project2.id } } assert_response :redirect assert_nil flash[:error] end def test_move_to_as_non_member @request.session[:user_id] = @someone.id - post :move, params: { id: @file9.id, target_project_id: @project2.id } + post :move, params: { id: @file9.id, dmsf_file_or_folder: { target_project_id: @project2.id } } assert_response :forbidden end diff --git a/test/functional/dmsf_folders_copy_controller_test.rb b/test/functional/dmsf_folders_copy_controller_test.rb index 26f6ceae..e56e8c6b 100644 --- a/test/functional/dmsf_folders_copy_controller_test.rb +++ b/test/functional/dmsf_folders_copy_controller_test.rb @@ -81,7 +81,8 @@ class DmsfFoldersCopyControllerTest < RedmineDmsf::Test::TestCase end def test_copy - post :copy, params: { id: @folder1.id, target_project_id: @project1.id, target_folder_id: @folder6.id } + post :copy, params: { id: @folder1.id, + dmsf_file_or_folder: { target_project_id: @project1.id, target_folder_id: @folder6.id } } assert_response :redirect assert_nil flash[:error] end @@ -89,86 +90,93 @@ class DmsfFoldersCopyControllerTest < RedmineDmsf::Test::TestCase def test_copy_to_another_project @request.session[:user_id] = @admin.id assert_equal @project1.id, @folder1.project_id - post :copy, params: { id: @folder1.id, target_project_id: @project2.id } + post :copy, params: { id: @folder1.id, dmsf_file_or_folder: { target_project_id: @project2.id } } assert_response :redirect assert_nil flash[:error] end def test_copy_the_same_target - post :copy, params: { id: @folder6.id, target_project_id: @folder6.project.id } + post :copy, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @folder6.project.id } } assert_equal flash[:error], l(:error_target_folder_same) assert_redirected_to action: :new, target_project_id: @folder6.project.id end def test_copy_to_locked_folder @request.session[:user_id] = @admin.id - post :copy, params: { id: @folder6.id, target_project_id: @folder2.project.id, target_folder_id: @folder2.id } + post :copy, + params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @folder2.project.id, + target_folder_id: @folder2.id } } assert_response :forbidden end def test_copy_to_dmsf_not_enabled @project2.disable_module! :dmsf - post :copy, params: { id: @folder6.id, target_project_id: @project2.id } + post :copy, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @project2.id } } assert_response :forbidden end def test_copy_to_dmsf_enabled - post :copy, params: { id: @folder6.id, target_project_id: @project2.id } + post :copy, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @project2.id } } assert_response :redirect assert_nil flash[:error] end def test_copy_to_as_non_member @request.session[:user_id] = @someone.id - post :copy, params: { id: @folder6.id, target_project_id: @folder1.project.id, target_folder_id: @folder1.id } + post :copy, params: { id: @folder6.id, + dmsf_file_or_folder: { target_project_id: @folder1.project.id, target_folder_id: @folder1.id } } assert_response :not_found end def test_move - post :move, params: { id: @folder6.id, target_project_id: @folder1.project.id, target_folder_id: @folder1.id } + post :move, params: { id: @folder6.id, + dmsf_file_or_folder: { target_project_id: @folder1.project.id, target_folder_id: @folder1.id } } assert_response :redirect assert_nil flash[:error] end def test_move_to_another_project - post :move, params: { id: @folder6.id, target_project_id: @project2.id } + post :move, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @project2.id } } assert_response :redirect assert_nil flash[:error] end def test_move_the_same_target - post :move, params: { id: @folder6.id, target_project_id: @folder6.project.id } + post :move, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @folder6.project.id } } assert_equal flash[:error], l(:error_target_folder_same) assert_redirected_to action: :new, target_project_id: @folder6.project.id end def test_move_locked_folder @request.session[:user_id] = @admin.id - post :move, params: { id: @folder2.id, target_project_id: @folder6.project.id, target_folder_id: @folder6.id } + post :move, params: { id: @folder2.id, + dmsf_file_or_folder: { target_project_id: @folder6.project.id, target_folder_id: @folder6.id } } assert_response :forbidden end def test_move_to_locked_folder @request.session[:user_id] = @admin.id - post :move, params: { id: @folder6.id, target_project_id: @folder2.project.id, target_folder_id: @folder2.id } + post :move, params: { id: @folder6.id, + dmsf_file_or_folder: { target_project_id: @folder2.project.id, target_folder_id: @folder2.id } } assert_response :forbidden end def test_move_to_dmsf_not_enabled @project2.disable_module! :dmsf - post :move, params: { id: @folder6.id, target_project_id: @project2.id } + post :move, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @project2.id } } assert_response :forbidden end def test_move_to_dmsf_enabled - post :move, params: { id: @folder6.id, target_project_id: @project2.id } + post :move, params: { id: @folder6.id, dmsf_file_or_folder: { target_project_id: @project2.id } } assert_response :redirect assert_nil flash[:error] end def test_move_to_as_non_member @request.session[:user_id] = @someone.id - post :move, params: { id: @folder6.id, target_project_id: @folder1.project.id, target_folder_id: @folder1.id } + post :move, params: { id: @folder6.id, + dmsf_file_or_folder: { target_project_id: @folder1.project.id, target_folder_id: @folder1.id } } assert_response :not_found end diff --git a/test/integration/rest_api/dmsf_file_api_test.rb b/test/integration/rest_api/dmsf_file_api_test.rb index 5a3bb3ef..321c0980 100644 --- a/test/integration/rest_api/dmsf_file_api_test.rb +++ b/test/integration/rest_api/dmsf_file_api_test.rb @@ -224,4 +224,36 @@ class DmsfFileApiTest < RedmineDmsf::Test::IntegrationTest assert_select 'dmsf_file_revision > id', text: @file1.last_revision.id.to_s end + def test_copy_document + #curl -v -H "Content-Type: application/xml" -X POST --data "@file_or_folder_copy_move.xml" -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000/dmsf/files/1/copy/copy.xml + payload = %{ + + + #{@project1.id} + #{@folder1.id} + + } + assert_difference('@folder1.dmsf_files.count', 1) do + post "/dmsf/files/#{@file1.id}/copy/copy.xml?key=#{@token.value}", params: payload, + headers: { 'CONTENT_TYPE' => 'application/xml' } + end + assert_response :success + end + + def test_move_document + #curl -v -H "Content-Type: application/xml" -X POST --data "@file_or_folder_copy_move.xml" -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000/dmsf/files/1/copy/move.xml + payload = %{ + + + #{@project1.id} + #{@folder1.id} + + } + assert_difference('@folder1.dmsf_files.count', 1) do + post "/dmsf/files/#{@file1.id}/copy/move.xml?key=#{@token.value}", params: payload, + headers: { 'CONTENT_TYPE' => 'application/xml' } + end + assert_response :success + end + end \ No newline at end of file diff --git a/test/integration/rest_api/dmsf_folder_api_test.rb b/test/integration/rest_api/dmsf_folder_api_test.rb index 17339bfa..cd1649c1 100644 --- a/test/integration/rest_api/dmsf_folder_api_test.rb +++ b/test/integration/rest_api/dmsf_folder_api_test.rb @@ -263,4 +263,36 @@ class DmsfFolderApiTest < RedmineDmsf::Test::IntegrationTest assert_equal DmsfFolder::STATUS_ACTIVE, @folder2.deleted end + def test_copy_folder + #curl -v -H "Content-Type: application/xml" -X POST --data "@file_or_folder_copy_move.xml" -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000/dmsf/folders/6/copy/copy.xml + payload = %{ + + + #{@project1.id} + #{@folder1.id} + + } + assert_difference('@folder1.dmsf_folders.count', 1) do + post "/dmsf/folders/#{@folder6.id}/copy/copy.xml?key=#{@token.value}", params: payload, + headers: { 'CONTENT_TYPE' => 'application/xml' } + end + assert_response :success + end + + def test_move_document + #curl -v -H "Content-Type: application/xml" -X POST --data "@file_or_folder_copy_move.xml" -H "X-Redmine-API-Key: USERS_API_KEY" http://localhost:3000/dmsf/folders/6/copy/move.xml + payload = %{ + + + #{@project1.id} + #{@folder1.id} + + } + assert_difference('@folder1.dmsf_folders.count', 1) do + post "/dmsf/folders/#{@folder6.id}/copy/move.xml?key=#{@token.value}", params: payload, + headers: { 'CONTENT_TYPE' => 'application/xml' } + end + assert_response :success + end + end \ No newline at end of file