Merge branch 'devel-2.0.0' of github.com:danmunn/redmine_dmsf into devel-2.0.0

This commit is contained in:
Karel Pičman 2019-01-03 10:13:06 +01:00
commit 56cd51b359
19 changed files with 112 additions and 63 deletions

View File

@ -378,9 +378,10 @@ class DmsfController < ApplicationController
zipped_content = DmsfHelper.temp_dir.join(DmsfHelper.temp_filename('dmsf_email_sent_documents.zip')) zipped_content = DmsfHelper.temp_dir.join(DmsfHelper.temp_filename('dmsf_email_sent_documents.zip'))
File.open(zipped_content, 'wb') do |f| File.open(zipped_content, 'wb') do |f|
zip_file = File.open(zip.finish, 'rb') File.open(zip.finish, 'rb') do |zip_file|
while (buffer = zip_file.read(8192)) while (buffer = zip_file.read(8192))
f.write(buffer) f.write(buffer)
end
end end
end end
@ -402,8 +403,8 @@ class DmsfController < ApplicationController
:folders => selected_folders, :folders => selected_folders,
:files => selected_files, :files => selected_files,
:subject => "#{@project.name} #{l(:label_dmsf_file_plural).downcase}", :subject => "#{@project.name} #{l(:label_dmsf_file_plural).downcase}",
:from => Setting.plugin_redmine_dmsf['dmsf_documents_email_from'].blank? ? :from => Setting.plugin_redmine_dmsf['dmsf_documents_email_from'].presence ||
"#{User.current.name} <#{User.current.mail}>" : Setting.plugin_redmine_dmsf['dmsf_documents_email_from'], "#{User.current.name} <#{User.current.mail}>",
:reply_to => Setting.plugin_redmine_dmsf['dmsf_documents_email_reply_to'] :reply_to => Setting.plugin_redmine_dmsf['dmsf_documents_email_reply_to']
} }
render :action => 'email_entries' render :action => 'email_entries'
@ -434,7 +435,7 @@ class DmsfController < ApplicationController
end end
def zip_entries(zip, selected_folders, selected_files) def zip_entries(zip, selected_folders, selected_files)
member = Member.where(user_id: User.current.id, project_id: @project.id).first member = Member.find_by(user_id: User.current.id, project_id: @project.id)
selected_folders.each do |selected_folder_id| selected_folders.each do |selected_folder_id|
folder = DmsfFolder.visible.find_by(id: selected_folder_id) folder = DmsfFolder.visible.find_by(id: selected_folder_id)
if folder if folder
@ -684,7 +685,7 @@ class DmsfController < ApplicationController
@subfolders = DmsfHelper.visible_folders(@subfolders, @project) @subfolders = DmsfHelper.visible_folders(@subfolders, @project)
end end
@ajax_upload_size = Setting.plugin_redmine_dmsf['dmsf_max_ajax_upload_filesize'].present? ? Setting.plugin_redmine_dmsf['dmsf_max_ajax_upload_filesize'] : 100 @ajax_upload_size = Setting.plugin_redmine_dmsf['dmsf_max_ajax_upload_filesize'].presence || 100
# Trash # Trash
@trash_visible = @folder_manipulation_allowed && @file_manipulation_allowed && @trash_visible = @folder_manipulation_allowed && @file_manipulation_allowed &&

View File

@ -57,7 +57,7 @@ class DmsfFilesController < ApplicationController
access.dmsf_file_revision = @revision access.dmsf_file_revision = @revision
access.action = DmsfFileRevisionAccess::DownloadAction access.action = DmsfFileRevisionAccess::DownloadAction
access.save! access.save!
member = Member.where(user_id: User.current.id, project_id: @file.project.id).first member = Member.find_by(user_id: User.current.id, project_id: @file.project.id)
if member && !member.dmsf_title_format.nil? && !member.dmsf_title_format.empty? if member && !member.dmsf_title_format.nil? && !member.dmsf_title_format.empty?
title_format = member.dmsf_title_format title_format = member.dmsf_title_format
else else

View File

@ -27,7 +27,7 @@ class DmsfStateController < ApplicationController
before_action :authorize before_action :authorize
def user_pref_save def user_pref_save
member = @project.members.where(user_id: User.current.id).first member = @project.members.find_by(user_id: User.current.id)
if member if member
member.dmsf_mail_notification = params[:email_notify] member.dmsf_mail_notification = params[:email_notify]
member.dmsf_title_format = params[:title_format] member.dmsf_title_format = params[:title_format]
@ -49,7 +49,7 @@ class DmsfStateController < ApplicationController
private private
def format_valid?(format) def format_valid?(format)
format.blank? || ((format =~ /%(t|d|v|i|r)/) && format.length < 256) format.blank? || ((/%(t|d|v|i|r)/.match?(format)) && format.length < 256)
end end
end end

View File

@ -339,7 +339,7 @@ class DmsfWorkflowsController < ApplicationController
end end
operator = (params[:commit] == l(:dmsf_and)) ? DmsfWorkflowStep::OPERATOR_AND : DmsfWorkflowStep::OPERATOR_OR operator = (params[:commit] == l(:dmsf_and)) ? DmsfWorkflowStep::OPERATOR_AND : DmsfWorkflowStep::OPERATOR_OR
user_ids = User.where(id: params[:user_ids]).ids user_ids = User.where(id: params[:user_ids]).ids
if user_ids.count > 0 if user_ids.any?
user_ids.each do |user_id| user_ids.each do |user_id|
ws = DmsfWorkflowStep.new ws = DmsfWorkflowStep.new
ws.dmsf_workflow_id = @dmsf_workflow.id ws.dmsf_workflow_id = @dmsf_workflow.id

View File

@ -48,11 +48,13 @@ module DmsfHelper
# get only the filename, not the whole path # get only the filename, not the whole path
just_filename = File.basename(filename.gsub('\\\\', '/')) just_filename = File.basename(filename.gsub('\\\\', '/'))
# replace all non alphanumeric, hyphens or periods with underscore # replace all non alphanumeric, hyphens or periods with underscore
just_filename = just_filename.gsub(/[^\w\.\-]/,'_') just_filename.gsub!(/[^\w\.\-]/, '_')
unless just_filename =~ %r{^[a-zA-Z0-9_\.\-]*$} unless %r{^[a-zA-Z0-9_\.\-]*$}.match?(just_filename)
# keep the extension if any # keep the extension if any
extension = $1 if just_filename =~ %r{(\.[a-zA-Z0-9]+)$} if just_filename =~ %r{(\.[a-zA-Z0-9]+)$}
just_filename = Digest::SHA256.hexdigest(just_filename) << extension extension = $1
just_filename = Digest::SHA256.hexdigest(just_filename) << extension
end
end end
just_filename just_filename
end end

View File

@ -51,7 +51,7 @@ module DmsfWorkflowsHelper
options = Array.new options = Array.new
options << [l(:dmsf_new_step), 0] options << [l(:dmsf_new_step), 0]
steps.each do |step| steps.each do |step|
options << [step.name.present? ? step.name : step.step.to_s, step.step] options << [step.name.presence || step.step.to_s, step.step]
end end
options_for_select(options, 0) options_for_select(options, 0)
end end

View File

@ -391,7 +391,7 @@ class DmsfFile < ActiveRecord::Base
next if dmsf_attrs.length == 0 || id_attribute == 0 next if dmsf_attrs.length == 0 || id_attribute == 0
next unless results.select{|f| f.id.to_s == id_attribute}.empty? next unless results.select{|f| f.id.to_s == id_attribute}.empty?
dmsf_file = DmsfFile.visible.where(limit_options).where(id: id_attribute).first dmsf_file = DmsfFile.visible.where(limit_options).find_by(id: id_attribute)
if dmsf_file && DmsfFolder.permissions?(dmsf_file.dmsf_folder) if dmsf_file && DmsfFolder.permissions?(dmsf_file.dmsf_folder)
if user.allowed_to?(:view_dmsf_files, dmsf_file.project) && if user.allowed_to?(:view_dmsf_files, dmsf_file.project) &&
@ -417,7 +417,7 @@ class DmsfFile < ActiveRecord::Base
end end
def display_name def display_name
member = Member.where(user_id: User.current.id, project_id: project_id).first member = Member.find_by(user_id: User.current.id, project_id: project_id)
if member && !member.dmsf_title_format.nil? && !member.dmsf_title_format.empty? if member && !member.dmsf_title_format.nil? && !member.dmsf_title_format.empty?
title_format = member.dmsf_title_format title_format = member.dmsf_title_format
else else

View File

@ -130,9 +130,9 @@ class DmsfFolder < ActiveRecord::Base
def self.find_by_title(project, folder, title) def self.find_by_title(project, folder, title)
if folder if folder
visible.where(project_id: project.id, dmsf_folder_id: nil, title: title).first visible.find_by(project_id: project.id, dmsf_folder_id: nil, title: title)
else else
visible.where(project_id: project.id, dmsf_folder_id: folder.id, title: title).first visible.find_by(project_id: project.id, dmsf_folder_id: folder.id, title: title)
end end
end end

View File

@ -52,7 +52,7 @@ class DmsfMailer < Mailer
end end
def files_deleted(user, project, files) def files_deleted(user, project, files)
if user && files.count > 0 if user && files.any?
redmine_headers 'Project' => project.identifier if project redmine_headers 'Project' => project.identifier if project
@files = files @files = files
@project = project @project = project

View File

@ -22,7 +22,7 @@
class DmsfFileNameValidator < ActiveModel::EachValidator class DmsfFileNameValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value) def validate_each(record, attribute, value)
unless value =~ /\A[^#{DmsfFolder::INVALID_CHARACTERS}]*\z/ unless /\A[^#{DmsfFolder::INVALID_CHARACTERS}]*\z/.match?(value)
record.errors.add attribute, :error_contains_invalid_character record.errors.add attribute, :error_contains_invalid_character
end end
end end

View File

@ -23,7 +23,7 @@
<% parent = @folder ? @folder : @project %> <% parent = @folder ? @folder : @project %>
<% DmsfHelper.all_children_sorted(parent, @pos, @idnt).each do |obj, position| %> <% DmsfHelper.all_children_sorted(parent, @pos, @idnt).each do |obj, position| %>
<% classes = "dmsf_tree idnt-#{@idnt} hascontextmenu" %> <% classes = "dmsf_tree idnt-#{@idnt} hascontextmenu" %>
<% if obj.is_a?(DmsfFolder) && ((obj.dmsf_folders.visible.count > 0) || (obj.dmsf_files.visible.count > 0) || (obj.dmsf_links.visible.count > 0)) %> <% if obj.is_a?(DmsfFolder) && ((obj.dmsf_folders.visible.any?) || (obj.dmsf_files.visible.any?) || (obj.dmsf_links.visible.any?)) %>
<% classes += ' idnt dmsf_collapsed dmsf-not-loaded' %> <% classes += ' idnt dmsf_collapsed dmsf-not-loaded' %>
<% id = "id='#{obj.id}span'".html_safe %> <% id = "id='#{obj.id}span'".html_safe %>
<% onclick = "onclick=\"dmsfToggle('#{obj.id}','#{obj.id}span','#{escape_javascript(expand_folder_dmsf_path)}')\"" %> <% onclick = "onclick=\"dmsfToggle('#{obj.id}','#{obj.id}span','#{escape_javascript(expand_folder_dmsf_path)}')\"" %>

View File

@ -78,11 +78,11 @@
<% end %> <% end %>
<% testfilename = DmsfFile.storage_path.join('test.test') %> <% testfilename = DmsfFile.storage_path.join('test.test') %>
<% if File.exist?(storage_dir) %> <% if File.exist?(storage_dir) %>
<% begin %> <% begin %>
<% File.open(testfilename, 'wb') %> <% File.open(testfilename, 'wb') {} %>
<% rescue %> <% rescue %>
<p class="warning"><%= l(:error_file_can_not_be_created) %></p> <p class="warning"><%= l(:error_file_can_not_be_created) %></p>
<% ensure %> <% ensure %>
<% FileUtils.rm_f(testfilename) %> <% FileUtils.rm_f(testfilename) %>
<% end %> <% end %>
<% end %> <% end %>
@ -108,12 +108,12 @@
<% path = Pathname.new(tmpdir) %> <% path = Pathname.new(tmpdir) %>
<% testfilename = path.join('test.test') %> <% testfilename = path.join('test.test') %>
<% if File.exist?(tmpdir) %> <% if File.exist?(tmpdir) %>
<% begin %> <% begin %>
<% File.open(testfilename, 'wb') %> <% File.open(testfilename, 'wb') {} %>
<% rescue %> <% rescue %>
<p class="warning"><%= l(:error_tmpfile_can_not_be_created) %></p> <p class="warning"><%= l(:error_tmpfile_can_not_be_created) %></p>
<% ensure %> <% ensure %>
<% File.delete(testfilename) if File.exist?(testfilename) %> <% FileUtils.rm_f(testfilename) %>
<% end %> <% end %>
<% end %> <% end %>

View File

@ -102,12 +102,14 @@ module DAV4Rack
# Return response to HEAD # Return response to HEAD
def head def head
if(resource.exist?) if(resource.exist?)
response['Etag'] = resource.etag res = resource.head(request, response)
response['Content-Type'] = resource.content_type if(res == OK)
response['Content-Length'] = resource.content_length.to_s response['Etag'] ||= resource.etag
response['Last-Modified'] = resource.last_modified.httpdate response['Content-Type'] ||= resource.content_type
resource.head(request, response) response['Content-Length'] ||= resource.content_length.to_s
OK response['Last-Modified'] ||= resource.last_modified.httpdate
end
res
else else
NotFound NotFound
end end
@ -197,7 +199,8 @@ module DAV4Rack
return BadRequest unless request.depth == :infinity return BadRequest unless request.depth == :infinity
return BadRequest unless dest = request.destination return BadRequest unless dest = request.destination
if status = dest.validate if status = dest.validate(host: request.host,
resource_path: resource.path)
return status return status
end end

View File

@ -5,24 +5,13 @@ module DAV4Rack
attr_reader :host, :path, :path_info attr_reader :host, :path, :path_info
def initialize(value, script_name: nil) # uri is expected to be a DAV4Rack::Uri instance
@script_name = script_name.to_s def initialize(uri)
@value = value.to_s.strip
parse
end
def parse
uri = Addressable::URI.parse @value
@host = uri.host @host = uri.host
@path = Addressable::URI.unencode uri.path @path = uri.path
unless @path_info = uri.path_info
if @script_name # nil path info means path is outside the realm of script_name
if @path =~ /\A(?<path>#{Regexp.escape @script_name}(?<path_info>\/.*))\z/ raise ArgumentError, "invalid destination header value: #{uri.to_s}"
@path_info = $~[:path_info]
else
raise ArgumentError, 'invalid destination header value'
end
end end
end end
@ -30,7 +19,7 @@ module DAV4Rack
def validate(host: nil, resource_path: nil) def validate(host: nil, resource_path: nil)
if host and self.host and self.host != host if host and self.host and self.host != host
DAV4Rack::HTTPStatus::BadGateway DAV4Rack::HTTPStatus::BadGateway
elsif self.path == resource_path elsif resource_path and self.path_info == resource_path
DAV4Rack::HTTPStatus::Forbidden DAV4Rack::HTTPStatus::Forbidden
end end
end end

View File

@ -3,6 +3,7 @@
require 'uri' require 'uri'
require 'addressable/uri' require 'addressable/uri'
require 'dav4rack/logger' require 'dav4rack/logger'
require 'dav4rack/uri'
module DAV4Rack module DAV4Rack
class Request < Rack::Request class Request < Rack::Request
@ -84,7 +85,7 @@ module DAV4Rack
# Destination header # Destination header
def destination def destination
@destination ||= if h = get_header('HTTP_DESTINATION') @destination ||= if h = get_header('HTTP_DESTINATION')
DestinationHeader.new h, script_name: script_name DestinationHeader.new DAV4Rack::Uri.new(h, script_name: script_name)
end end
end end
@ -123,6 +124,13 @@ module DAV4Rack
"#{script_name}#{expand_path path}" "#{script_name}#{expand_path path}"
end end
# returns the given path, but with the leading script_name removed. Will
# return nil if the path does not begin with the script_name
def path_info_for(full_path, script_name: self.script_name)
uri = DAV4Rack::Uri.new full_path, script_name: script_name
return uri.path_info
end
# expands '/foo/../bar' to '/bar' # expands '/foo/../bar' to '/bar'
def expand_path(path) def expand_path(path)
path.squeeze! '/' path.squeeze! '/'

View File

@ -208,8 +208,12 @@ module DAV4Rack
NotImplemented NotImplemented
end end
# HTTP HEAD request.
#
# Like GET, but without content. Override if you set custom headers in GET
# to set them here as well.
def head(request, response) def head(request, response)
#no-op, but called by the controller OK
end end
# HTTP PUT request. # HTTP PUT request.

42
lib/dav4rack/uri.rb Normal file
View File

@ -0,0 +1,42 @@
require 'addressable/uri'
module DAV4Rack
# adds a bit of parsing logic around a header URI or path value
class Uri
attr_reader :host, :path, :path_info, :script_name
def initialize(uri_or_path, script_name: nil)
# more than one leading slash confuses Addressable::URI, resulting e.g.
# with //remote.php/dav/files in a path of /dav/files with a host
# remote.php.
@uri_or_path = uri_or_path.to_s.strip.sub %r{\A/+}, '/'
@script_name = script_name
parse
end
def to_s
@uri_or_path
end
private
def parse
uri = Addressable::URI.parse @uri_or_path
@host = uri.host
@path = Addressable::URI.unencode uri.path
if @script_name
if @path =~ /\A(?<path>#{Regexp.escape @script_name}(?<path_info>\/.*))\z/
@path_info = $~[:path_info]
end
else
@path_info = @path
end
end
end
end

View File

@ -13,5 +13,5 @@ module DAV4Rack
end end
end end
VERSION = Version.new('1.0.0') VERSION = Version.new('1.1.0')
end end

View File

@ -72,25 +72,25 @@ class DmsfWorkflowStepTest < RedmineDmsf::Test::UnitTest
def test_validate_workflow_id_presence def test_validate_workflow_id_presence
@wfs1.dmsf_workflow_id = nil @wfs1.dmsf_workflow_id = nil
assert !@wfs1.save assert !@wfs1.save
assert@wfs1.errors.count > 0 assert @wfs1.errors.any?
end end
def test_validate_step_presence def test_validate_step_presence
@wfs1.step = nil @wfs1.step = nil
assert !@wfs1.save assert !@wfs1.save
assert @wfs1.errors.count > 0 assert @wfs1.errors.any?
end end
def test_validate_user_id_presence def test_validate_user_id_presence
@wfs1.user_id = nil @wfs1.user_id = nil
assert !@wfs1.save assert !@wfs1.save
assert@wfs1.errors.count > 0 assert @wfs1.errors.any?
end end
def test_validate_operator_presence def test_validate_operator_presence
@wfs1.operator = nil @wfs1.operator = nil
assert !@wfs1.save assert !@wfs1.save
assert @wfs1.errors.count > 0 assert @wfs1.errors.any?
end end
def test_validate_user_id_uniqueness def test_validate_user_id_uniqueness
@ -98,7 +98,7 @@ class DmsfWorkflowStepTest < RedmineDmsf::Test::UnitTest
@wfs2.dmsf_workflow_id = @wfs1.dmsf_workflow_id @wfs2.dmsf_workflow_id = @wfs1.dmsf_workflow_id
@wfs2.step = @wfs1.step @wfs2.step = @wfs1.step
assert !@wfs2.save assert !@wfs2.save
assert @wfs2.errors.count > 0 assert @wfs2.errors.any?
end end
def test_validate_name_length def test_validate_name_length