diff --git a/after_init.rb b/after_init.rb index 97643d10..1e45f9b0 100644 --- a/after_init.rb +++ b/after_init.rb @@ -66,6 +66,27 @@ unless Redmine::Plugin.installed?(:easy_extensions) else ActiveSupport.on_load(:easyproject, yield: true) do init + + require File.expand_path('../app/models/easy_page_modules/easy_dms/epm_dmsf_locked_documents', __FILE__) + require File.expand_path('../app/models/easy_page_modules/easy_dms/epm_dmsf_open_approvals', __FILE__) + + EpmDmsfLockedDocuments.register_to_all(:plugin => :easy_dms) + EpmDmsfOpenApprovals.register_to_all(:plugin => :easy_dms) + + EasyExtensions::AfterInstallScripts.add do + page = EasyPage.where(:page_name => 'project-overview').first + page_template = page.default_template if page + + unless page_template + page_template = EasyPageTemplate.create(:easy_pages_id => page.id, :template_name => 'Default template', + :description => 'Default template', :is_default => true) + EasyPageTemplateModule.create_template_module(page, page_template, EpmDmsfLockedDocuments.first, + 'top-left', HashWithIndifferentAccess.new, 1) + EasyPageTemplateModule.create_template_module(page, page_template, EpmDmsfOpenApprovals.first, + 'top-left', HashWithIndifferentAccess.new, 1) + end + end + end end diff --git a/app/models/easy_page_modules/easy_dms/epm_dmsf_locked_documents.rb b/app/models/easy_page_modules/easy_dms/epm_dmsf_locked_documents.rb new file mode 100644 index 00000000..383e07f0 --- /dev/null +++ b/app/models/easy_page_modules/easy_dms/epm_dmsf_locked_documents.rb @@ -0,0 +1,35 @@ +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-17 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 +# 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 EpmDmsfLockedDocuments < EasyPageModule + + def category_name + @category_name ||= 'easy_dms' + end + + def get_show_data(settings, user, page_context = {}) + {} + end + + def get_edit_data(settings, user, page_context = {}) + {} + end + +end \ No newline at end of file diff --git a/app/models/easy_page_modules/easy_dms/epm_dmsf_open_approvals.rb b/app/models/easy_page_modules/easy_dms/epm_dmsf_open_approvals.rb new file mode 100644 index 00000000..16e97cf7 --- /dev/null +++ b/app/models/easy_page_modules/easy_dms/epm_dmsf_open_approvals.rb @@ -0,0 +1,31 @@ +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-17 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 +# 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 EpmDmsfOpenApprovals < EasyPageModule + + def category_name + @category_name ||= 'easy_dms' + end + + def get_show_data(settings, user, page_context = {}) + {} + end + +end \ No newline at end of file diff --git a/app/views/dmsf_links/_form.html.erb b/app/views/dmsf_links/_form.html.erb index 7d71fe13..5d9df1e1 100644 --- a/app/views/dmsf_links/_form.html.erb +++ b/app/views/dmsf_links/_form.html.erb @@ -87,7 +87,7 @@ <% if (@type == 'link_from') && !@container %> <% end %> @@ -124,5 +124,11 @@ $("input[name=external_link]:radio").change(function(){ $("#link_internal").toggle(); $("#link_external").toggle(); + $("#dmsf_link_external_url").toggleClass('required', $(this).val()); + var labelUrl = $('label[for="dmsf_link_external_url"]'); + labelUrl.toggleClass('required', $(this).val()); + if(labelUrl.find(".required").length == 0){ + labelUrl.append(' *'); + } }); diff --git a/app/views/easy_page_modules/easy_dms/_dmsf_locked_documents_edit.html.erb b/app/views/easy_page_modules/easy_dms/_dmsf_locked_documents_edit.html.erb new file mode 100644 index 00000000..1c27cf6b --- /dev/null +++ b/app/views/easy_page_modules/easy_dms/_dmsf_locked_documents_edit.html.erb @@ -0,0 +1,23 @@ +<% + # encoding: utf-8 + # + # Redmine plugin for Document Management System "Features" + # + # Copyright (C) 2011-17 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 + # 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. +%> + +<% 'Nothing to show' %> diff --git a/app/views/easy_page_modules/easy_dms/_dmsf_locked_documents_show.html.erb b/app/views/easy_page_modules/easy_dms/_dmsf_locked_documents_show.html.erb new file mode 100644 index 00000000..bb7680fb --- /dev/null +++ b/app/views/easy_page_modules/easy_dms/_dmsf_locked_documents_show.html.erb @@ -0,0 +1,23 @@ +<% + # encoding: utf-8 + # + # Redmine plugin for Document Management System "Features" + # + # Copyright (C) 2011-17 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 + # 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. +%> + +<%= render :partial => 'my/blocks/open_approvals' %> diff --git a/app/views/easy_page_modules/easy_dms/_dmsf_open_approvals_edit.html.erb b/app/views/easy_page_modules/easy_dms/_dmsf_open_approvals_edit.html.erb new file mode 100644 index 00000000..1c27cf6b --- /dev/null +++ b/app/views/easy_page_modules/easy_dms/_dmsf_open_approvals_edit.html.erb @@ -0,0 +1,23 @@ +<% + # encoding: utf-8 + # + # Redmine plugin for Document Management System "Features" + # + # Copyright (C) 2011-17 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 + # 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. +%> + +<% 'Nothing to show' %> diff --git a/app/views/easy_page_modules/easy_dms/_dmsf_open_approvals_show.html.erb b/app/views/easy_page_modules/easy_dms/_dmsf_open_approvals_show.html.erb new file mode 100644 index 00000000..2bb38f20 --- /dev/null +++ b/app/views/easy_page_modules/easy_dms/_dmsf_open_approvals_show.html.erb @@ -0,0 +1,23 @@ +<% + # encoding: utf-8 + # + # Redmine plugin for Document Management System "Features" + # + # Copyright (C) 2011-17 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 + # 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. +%> + +<%= render :partial => 'my/blocks/locked_documents' %> diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 8d363808..91e3406e 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -360,4 +360,11 @@ cs: label_user_search_add: Vyhledej uživatele pro přidání label_dmsf_attachments: DMS přílohy - label_basic_attachments: Souborové přílohy \ No newline at end of file + label_basic_attachments: Souborové přílohy + + easy_pages: + modules: + dmsf_locked_documents: My locked documents + dmsf_open_approvals: My open approvals + module_category: + easy_dms: Easy DMS \ No newline at end of file diff --git a/config/locales/de.yml b/config/locales/de.yml index ba5a3d63..d7b5eec4 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -357,4 +357,11 @@ de: label_user_search_add: Search for user to add label_dmsf_attachments: DMS Anhänge - label_basic_attachments: Dateianhängen \ No newline at end of file + label_basic_attachments: Dateianhängen + + easy_pages: + modules: + dmsf_locked_documents: My locked documents + dmsf_open_approvals: My open approvals + module_category: + easy_dms: Easy DMS \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index 755b0eee..46e8ef2e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -360,4 +360,11 @@ en: label_user_search_add: Search for user to add label_dmsf_attachments: DMS Attachments - label_basic_attachments: Basic Attachments \ No newline at end of file + label_basic_attachments: Basic Attachments + + easy_pages: + modules: + dmsf_locked_documents: My locked documents + dmsf_open_approvals: My open approvals + module_category: + easy_dms: Easy DMS \ No newline at end of file diff --git a/config/locales/es.yml b/config/locales/es.yml index 93b5ac47..cda12057 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -360,4 +360,11 @@ es: label_user_search_add: Search for user to add label_dmsf_attachments: DMS Attachments - label_basic_attachments: Basic Attachments \ No newline at end of file + label_basic_attachments: Basic Attachments + + easy_pages: + modules: + dmsf_locked_documents: My locked documents + dmsf_open_approvals: My open approvals + module_category: + easy_dms: Easy DMS \ No newline at end of file diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 58b7cbc6..e1c167e0 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -361,3 +361,10 @@ fr: label_dmsf_attachments: Pièces-jointes DMS label_basic_attachments: Pièces-jointes standards + + easy_pages: + modules: + dmsf_locked_documents: My locked documents + dmsf_open_approvals: My open approvals + module_category: + easy_dms: Easy DMS \ No newline at end of file diff --git a/config/locales/hu.yml b/config/locales/hu.yml index eb740299..fc7bc836 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -360,4 +360,11 @@ hu: label_user_search_add: Search for user to add label_dmsf_attachments: DMS Attachments - label_basic_attachments: Basic Attachments \ No newline at end of file + label_basic_attachments: Basic Attachments + + easy_pages: + modules: + dmsf_locked_documents: My locked documents + dmsf_open_approvals: My open approvals + module_category: + easy_dms: Easy DMS \ No newline at end of file diff --git a/config/locales/it.yml b/config/locales/it.yml index a06b598d..1ee6a79c 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -360,4 +360,11 @@ it: # Italian strings thx 2 Matteo Arceci! label_user_search_add: Search for user to add label_dmsf_attachments: DMS Attachments - label_basic_attachments: Basic Attachments \ No newline at end of file + label_basic_attachments: Basic Attachments + + easy_pages: + modules: + dmsf_locked_documents: My locked documents + dmsf_open_approvals: My open approvals + module_category: + easy_dms: Easy DMS \ No newline at end of file diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 9f513dc4..2da772b4 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -360,4 +360,11 @@ ja: label_user_search_add: Search for user to add label_dmsf_attachments: DMS Attachments - label_basic_attachments: Basic Attachments \ No newline at end of file + label_basic_attachments: Basic Attachments + + easy_pages: + modules: + dmsf_locked_documents: My locked documents + dmsf_open_approvals: My open approvals + module_category: + easy_dms: Easy DMS \ No newline at end of file diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 0bbd6c73..ad24edc2 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -360,4 +360,11 @@ pl: label_user_search_add: Search for user to add label_dmsf_attachments: DMS Attachments - label_basic_attachments: Basic Attachments \ No newline at end of file + label_basic_attachments: Basic Attachments + + easy_pages: + modules: + dmsf_locked_documents: My locked documents + dmsf_open_approvals: My open approvals + module_category: + easy_dms: Easy DMS \ No newline at end of file diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 07bdc292..b9e48fb8 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -361,3 +361,10 @@ pt-BR: label_dmsf_attachments: DMS Attachments label_basic_attachments: Basic Attachments + + easy_pages: + modules: + dmsf_locked_documents: My locked documents + dmsf_open_approvals: My open approvals + module_category: + easy_dms: Easy DMS \ No newline at end of file diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 14ee162d..007a25b8 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -360,4 +360,11 @@ ru: label_user_search_add: Search for user to add label_dmsf_attachments: DMS Attachments - label_basic_attachments: Basic Attachments \ No newline at end of file + label_basic_attachments: Basic Attachments + + easy_pages: + modules: + dmsf_locked_documents: My locked documents + dmsf_open_approvals: My open approvals + module_category: + easy_dms: Easy DMS \ No newline at end of file diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 5a2beaf0..36b399d7 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -360,4 +360,11 @@ sl: label_user_search_add: Search for user to add label_dmsf_attachments: DMS Attachments - label_basic_attachments: Basic Attachments \ No newline at end of file + label_basic_attachments: Basic Attachments + + easy_pages: + modules: + dmsf_locked_documents: My locked documents + dmsf_open_approvals: My open approvals + module_category: + easy_dms: Easy DMS \ No newline at end of file diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index d09a42d1..7e9ae417 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -360,4 +360,11 @@ zh-TW: label_user_search_add: Search for user to add label_dmsf_attachments: DMS Attachments - label_basic_attachments: Basic Attachments \ No newline at end of file + label_basic_attachments: Basic Attachments + + easy_pages: + modules: + dmsf_locked_documents: My locked documents + dmsf_open_approvals: My open approvals + module_category: + easy_dms: Easy DMS \ No newline at end of file diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 11c0119c..eff1e4cd 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -360,4 +360,11 @@ zh: label_user_search_add: Search for user to add label_dmsf_attachments: DMS Attachments - label_basic_attachments: Basic Attachments \ No newline at end of file + label_basic_attachments: Basic Attachments + + easy_pages: + modules: + dmsf_locked_documents: My locked documents + dmsf_open_approvals: My open approvals + module_category: + easy_dms: Easy DMS \ No newline at end of file diff --git a/lib/redmine_dmsf/patches/easy_crm_cases_controller_patch.rb b/lib/redmine_dmsf/patches/easy_crm_cases_controller_patch.rb index 90d95c49..5222bd38 100644 --- a/lib/redmine_dmsf/patches/easy_crm_cases_controller_patch.rb +++ b/lib/redmine_dmsf/patches/easy_crm_cases_controller_patch.rb @@ -3,99 +3,98 @@ module RedmineDmsf def self.included(base) base.send(:include, InstanceMethods) - base.class_eval do - + unloadable before_action :controller_easy_crm_cases_before_save, only: [:create, :update, :bulk_update] - after_action :controller_easy_crm_cases_after_save, only: [:create, :update, :bulk_update] - - - private - - def controller_easy_crm_cases_before_save - easy_crm_cases = @easy_crm_cases - easy_crm_cases ||= [@easy_crm_case] - easy_crm_cases.each do |easy_crm_case| - easy_crm_case.save_dmsf_attachments(params[:dmsf_attachments]) - easy_crm_case.save_dmsf_links(params[:dmsf_links]) - easy_crm_case.save_dmsf_attachments_wfs(params[:dmsf_attachments_wfs], params[:dmsf_attachments]) - easy_crm_case.save_dmsf_links_wfs(params[:dmsf_links_wfs]) - end - end - - def controller_easy_crm_cases_after_save - easy_crm_cases = @easy_crm_cases - easy_crm_cases ||= [@easy_crm_case] - easy_crm_cases.each do |easy_crm_case| - # Attach DMS documents - uploaded_files = params[:dmsf_attachments] - if uploaded_files && uploaded_files.is_a?(Hash) - system_folder = easy_crm_case.system_folder(true) - uploaded_files.each do |key, uploaded_file| - upload = DmsfUpload.create_from_uploaded_attachment(easy_crm_case.project, system_folder, uploaded_file) - if upload - uploaded_file[:disk_filename] = upload.disk_filename - uploaded_file[:name] = upload.name - uploaded_file[:title] = upload.title - uploaded_file[:version] = 1 - uploaded_file[:size] = upload.size - uploaded_file[:mime_type] = upload.mime_type - uploaded_file[:tempfile_path] = upload.tempfile_path - if params[:dmsf_attachments_wfs].present? && params[:dmsf_attachments_wfs][key].present? - uploaded_file[:workflow_id] = params[:dmsf_attachments_wfs][key].to_i - end - end - end - DmsfUploadHelper.commit_files_internal uploaded_files, easy_crm_case.project, system_folder, self - end - # Attach DMS links - easy_crm_case.saved_dmsf_links.each do |l| - file = l.target_file - revision = file.last_revision - system_folder = easy_crm_case.system_folder(true) - if system_folder - l.project_id = system_folder.project_id - l.dmsf_folder_id = system_folder.id - if l.save - easy_crm_case.dmsf_file_added file - end - wf = easy_crm_case.saved_dmsf_links_wfs[l.id] - if wf - # Assign the workflow - revision.set_workflow(wf.id, 'assign') - revision.assign_workflow(wf.id) - # Start the workflow - revision.set_workflow(wf.id, 'start') - if revision.save - wf.notify_users(easy_crm_case.project, revision, self) - begin - file.lock! - rescue DmsfLockError => e - Rails.logger.warn e.message - end - else - Rails.logger.error l(:error_workflow_assign) - end - end - end - end - copied_from = EasyCrmCase.find_by_id(params[:copy_from]) if params[:copy_from].present? - # Save documents - if copied_from - copied_from.dmsf_files.each do |dmsf_file| - dmsf_file.copy_to(easy_crm_case.project, easy_crm_cases.system_folder(true)) - end - end - end - end - + alias_method_chain :easy_crm_after_save, :dmsf end end module InstanceMethods + + def controller_easy_crm_cases_before_save + easy_crm_cases = @easy_crm_cases + easy_crm_cases ||= [@easy_crm_case] + easy_crm_cases.each do |easy_crm_case| + easy_crm_case.save_dmsf_attachments(params[:dmsf_attachments]) + easy_crm_case.save_dmsf_links(params[:dmsf_links]) + easy_crm_case.save_dmsf_attachments_wfs(params[:dmsf_attachments_wfs], params[:dmsf_attachments]) + easy_crm_case.save_dmsf_links_wfs(params[:dmsf_links_wfs]) + end + end + + def easy_crm_after_save_with_dmsf + easy_crm_after_save_without_dmsf + easy_crm_cases = @easy_crm_cases + easy_crm_cases ||= [@easy_crm_case] + easy_crm_cases.each do |easy_crm_case| + # Attach DMS documents + uploaded_files = params[:dmsf_attachments] + if uploaded_files && uploaded_files.is_a?(Hash) + system_folder = easy_crm_case.system_folder(true) + uploaded_files.each do |key, uploaded_file| + upload = DmsfUpload.create_from_uploaded_attachment(easy_crm_case.project, system_folder, uploaded_file) + if upload + uploaded_file[:disk_filename] = upload.disk_filename + uploaded_file[:name] = upload.name + uploaded_file[:title] = upload.title + uploaded_file[:version] = 1 + uploaded_file[:size] = upload.size + uploaded_file[:mime_type] = upload.mime_type + uploaded_file[:tempfile_path] = upload.tempfile_path + if params[:dmsf_attachments_wfs].present? && params[:dmsf_attachments_wfs][key].present? + uploaded_file[:workflow_id] = params[:dmsf_attachments_wfs][key].to_i + end + end + end + DmsfUploadHelper.commit_files_internal uploaded_files, easy_crm_case.project, system_folder, self + end + # Attach DMS links + easy_crm_case.saved_dmsf_links.each do |l| + file = l.target_file + revision = file.last_revision + system_folder = easy_crm_case.system_folder(true) + if system_folder + l.project_id = system_folder.project_id + l.dmsf_folder_id = system_folder.id + if l.save + easy_crm_case.dmsf_file_added file + end + wf = easy_crm_case.saved_dmsf_links_wfs[l.id] + if wf + # Assign the workflow + revision.set_workflow(wf.id, 'assign') + revision.assign_workflow(wf.id) + # Start the workflow + revision.set_workflow(wf.id, 'start') + if revision.save + wf.notify_users(easy_crm_case.project, revision, self) + begin + file.lock! + rescue DmsfLockError => e + Rails.logger.warn e.message + end + else + Rails.logger.error l(:error_workflow_assign) + end + end + end + end + copied_from = EasyCrmCase.find_by_id(params[:copy_from]) if params[:copy_from].present? + # Save documents + if copied_from + copied_from.dmsf_files.each do |dmsf_file| + dmsf_file.copy_to(easy_crm_case.project, easy_crm_cases.system_folder(true)) + end + end + end + end + end + end end + Rails.configuration.to_prepare do unless EasyCrmCasesController.included_modules.include?(RedmineDmsf::EasyCrmCasesControllerPatch) EasyCrmCasesController.send(:include, RedmineDmsf::EasyCrmCasesControllerPatch)