Dmitry Yu Okunev лет назад: 8
Сommit
e5d9275d4a

+ 6 - 0
app/views/js/_update_issue_edit_form_on_attachments_change.html.erb

@@ -0,0 +1,6 @@
+<script>
+	$(function(){
+		var file_selectors = $('#issue-form .file_selector');
+		file_selectors.change(function(){updateIssueFrom('<%=escape_javascript project_issue_form_path(@project, :id => @issue, :format => 'js')%>'+'&attachments_count='+file_selectors[0].files.length)});
+	});
+</script>

+ 4 - 0
config/locales/en.yml

@@ -0,0 +1,4 @@
+# English strings go here for Rails i18n
+en:
+  project_module_requireattachment: "Require an attachment to be able to close an issue"
+  issue_requires_attachment: "An attachment is required to be able to close the issue"

+ 4 - 0
config/locales/ru.yml

@@ -0,0 +1,4 @@
+# Russian strings go here for Rails i18n
+ru:
+  project_module_requireattachment: "Требовать вложение для закрытия задачи"
+  issue_requires_attachment: "Перед закрытием необходимо добавить хотя бы одно вложение"

+ 9 - 0
db/migrate/0100_add_is_public_project_to_project.rb

@@ -0,0 +1,9 @@
+class AddIsPublicProjectToProject < ActiveRecord::Migration
+  def self.up
+    add_column :projects, :is_public_project, :boolean
+  end
+
+  def self.down
+    remove_column :projects, :is_public_project
+  end
+end

+ 16 - 0
init.rb

@@ -0,0 +1,16 @@
+require 'redmine'
+
+require 'requireattachment/patches/issues_controller'
+require 'requireattachment/patches/issue'
+require 'requireattachment/hooks/view_issues_edit_notes_bottom'
+
+Redmine::Plugin.register :redmine_requireattachment do
+  name 'Require attachment to close'
+  author 'Dmitry Yu Okunev'
+  description "Plugin adds role-based permission to control who can close issues without any attachment. Yes, it's very specific use case."
+  version '0.1'
+  project_module :requireattachment do
+    permission :close_without_attachment, :projects => :close_without_attachment
+  end
+end
+

+ 5 - 0
lib/requireattachment/hooks/view_issues_edit_notes_bottom.rb

@@ -0,0 +1,5 @@
+module Requireattachment
+	class Hooks < Redmine::Hook::ViewListener
+		render_on :view_issues_edit_notes_bottom, :partial => 'js/update_issue_edit_form_on_attachments_change'
+	end
+end

+ 48 - 0
lib/requireattachment/patches/issue.rb

@@ -0,0 +1,48 @@
+require 'redmine'
+
+module Requireattachment
+	module IssuePatch
+		def self.included(base)
+			base.extend(ClassMethods)
+			base.send(:include, InstanceMethods)
+			base.class_eval do
+				unloadable
+
+				alias_method_chain :new_statuses_allowed_to, :requireattachment
+			end
+		end
+
+		module ClassMethods
+		end
+
+		module InstanceMethods
+			def can_close_check_attachment(user = User.current, force_has_attachments=false)
+				# Don't do anything if the module is disabled
+				return true unless project.module_enabled?("requireattachment")
+
+				# If already closed then there's nothing to do, just run the old method
+				return true if status.is_closed
+
+				# If current user is the author of the issue then he CAN close the issue
+				return true if user.id == author_id
+
+				# Checking if the user has right to close the issue without attachments.
+				return true if user.allowed_to?(:close_without_attachment, @project)
+
+				# If attachments are already uploaded then don't do anything.
+				return true if attachments.count != 0 or force_has_attachments
+
+				# Seems cannot
+				return false
+			end
+
+			def new_statuses_allowed_to_with_requireattachment(user=User.current, include_default=false, force_has_attachments=false)
+				@statuses_allowed_to = new_statuses_allowed_to_without_requireattachment(user, include_default)
+				return @statuses_allowed_to if can_close_check_attachment(user, force_has_attachments)
+				return @statuses_allowed_to.reject {|s| s.is_closed}
+			end
+		end
+	end
+end
+
+Issue.send(:include, Requireattachment::IssuePatch)

+ 59 - 0
lib/requireattachment/patches/issues_controller.rb

@@ -0,0 +1,59 @@
+module Requireattachment
+	module IssuesControllerPatch
+		def self.included(base)
+			base.send(:include, InstanceMethods)
+
+			base.class_eval do
+				unloadable
+
+				alias_method_chain :update,      :requireattachment
+				alias_method_chain :update_form, :requireattachment
+			end
+		end
+
+		module InstanceMethods
+			def can_close_check_attachment
+				# Model's can_close_check_attachment checks
+				return true if @issue.can_close_check_attachment(User.current)
+				# Additional checks:
+
+				# If attachments are already provided then just run the old method.
+				return true if not params[:attachments].nil?
+
+				# If all the above failed then seems the user cannot close the issue
+				return false
+			end
+
+			def update_with_requireattachment
+				# If can close without attachment then close
+				return update_without_requireattachment if can_close_check_attachment
+
+				# Checking if it is issues closing event. Otherwise just run the old method
+				@oldstatus = @issue.status
+				@newstatus = IssueStatus.find(params[:issue][:status_id])
+				return update_without_requireattachment unless @oldstatus.is_closed == false and @newstatus.is_closed = true
+
+				# If not then it's time to disappoint the user and tell him to make an attachment
+				#@issue.errors.add :attachments, :issue_requires_attachment
+				#respond_to do |format|
+				#	format.html { render :action => 'edit' }
+				#	format.api  { render_validation_errors(@issue) }
+				#end 
+				render_403
+			end
+
+			def update_form_with_requireattachment
+				update_form_without_requireattachment
+				return unless @project.module_enabled?("requireattachment")
+				return if params[:attachments_count].nil? or params[:attachments_count] == '0'
+
+				puts "params[:attachments_count] == #{params[:attachments_count]}"
+
+				@allowed_statuses = @issue.new_statuses_allowed_to_with_requireattachment(User.current, false, true)
+				#puts "@allowed_statuses == [#{@issue.attachments.count}] #{@allowed_statuses.to_yaml}"
+			end
+		end
+	end
+end
+
+IssuesController.send(:include, Requireattachment::IssuesControllerPatch)