File Storage Directory does not change #522

This commit is contained in:
Karel Picman 2016-04-12 12:07:16 +02:00
parent 68c68cdf79
commit 3a4d3e8b2b
4 changed files with 132 additions and 123 deletions

12
Gemfile
View File

@ -1,5 +1,5 @@
# encoding: utf-8
#
#
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2011 Vít Jonáš <vit.jonas@gmail.com>
@ -22,12 +22,12 @@
source 'https://rubygems.org'
gem 'rubyzip', '>= 1.0.0'
gem 'zip-zip' # Just to avoid 'cannot load such file -- zip/zip' error
gem 'rubyzip'
gem 'zip-zip'
gem 'simple_enum'
gem 'uuidtools', '~> 2.1.1'
gem 'dav4rack', '~> 0.3.0'
gem 'uuidtools'
gem 'dav4rack'
group :xapian do
group :xapian do
gem 'xapian-full-alaveteli', :require => false
end

View File

@ -1,5 +1,5 @@
# encoding: utf-8
#
#
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2011 Vít Jonáš <vit.jonas@gmail.com>
@ -21,16 +21,16 @@
class DmsfUploadController < ApplicationController
unloadable
menu_item :dmsf
before_filter :find_project
before_filter :authorize
before_filter :find_folder, :except => [:upload_file, :upload, :commit]
helper :all
helper :dmsf_workflows
accept_api_auth :upload, :commit
def upload_files
@ -38,7 +38,7 @@ class DmsfUploadController < ApplicationController
@uploads = []
if uploaded_files && uploaded_files.is_a?(Hash)
# standard file input uploads
uploaded_files.each_value do |uploaded_file|
uploaded_files.each_value do |uploaded_file|
upload = DmsfUpload.create_from_uploaded_attachment(@project, @folder, uploaded_file)
@uploads.push(upload) if upload
end
@ -53,9 +53,9 @@ class DmsfUploadController < ApplicationController
end
end
# async single file upload handling
def upload_file
@tempfile = params[:file]
# async single file upload handling
def upload_file
@tempfile = params[:file]
unless @tempfile.original_filename
render_404
return
@ -72,21 +72,21 @@ class DmsfUploadController < ApplicationController
rescue Exception => e
Rails.logger.error e.message
end
render :layout => nil, :json => { :jsonrpc => '2.0',
:error => {
:code => 103,
:message => l(:header_minimum_filesize),
:details => l(:error_minimum_filesize,
:file => @tempfile.original_filename.to_s)
render :layout => nil, :json => { :jsonrpc => '2.0',
:error => {
:code => 103,
:message => l(:header_minimum_filesize),
:details => l(:error_minimum_filesize,
:file => @tempfile.original_filename.to_s)
}
}
else
render :layout => false
end
end
# REST API document upload
def upload
def upload
unless request.content_type == 'application/octet-stream'
render :nothing => true, :status => 406
return
@ -109,69 +109,69 @@ class DmsfUploadController < ApplicationController
}
end
end
def commit_files
def commit_files
commit_files_internal params[:commited_files]
end
end
# REST API file commit
def commit
attachments = params[:attachments]
attachments = params[:attachments]
if attachments && attachments.is_a?(Hash)
@folder = DmsfFolder.visible.find_by_id attachments[:folder_id].to_i if attachments[:folder_id].present?
# standard file input uploads
uploaded_files = attachments.select { |key, value| key == 'uploaded_file'}
uploaded_files.each_value do |uploaded_file|
uploaded_files.each_value do |uploaded_file|
upload = DmsfUpload.create_from_uploaded_attachment(@project, @folder, uploaded_file)
uploaded_file[:disk_filename] = upload.disk_filename
end
end
commit_files_internal uploaded_files
commit_files_internal uploaded_files
end
private
def commit_files_internal(commited_files)
if commited_files && commited_files.is_a?(Hash)
@files = []
failed_uploads = []
commited_files.each_value do |commited_file|
name = commited_file[:name]
new_revision = DmsfFileRevision.new
file = DmsfFile.visible.find_file_by_name(@project, @folder, name)
unless file
link = DmsfLink.find_link_by_file_name(@project, @folder, name)
file = link.target_file if link
end
unless file
file = DmsfFile.new
file.project = @project
file.name = name
file.folder = @folder
file.notification = Setting.plugin_redmine_dmsf[:dmsf_default_notifications].present?
file.notification = Setting.plugin_redmine_dmsf[:dmsf_default_notifications].present?
new_revision.minor_version = 0
new_revision.major_version = 0
else
else
if file.last_revision
last_revision = file.last_revision
new_revision.source_revision = last_revision
new_revision.major_version = last_revision.major_version
new_revision.minor_version = last_revision.minor_version
new_revision.minor_version = last_revision.minor_version
else
new_revision.minor_version = 0
new_revision.major_version = 0
end
end
if file.locked_for_user?
failed_uploads.push(commited_file)
next
end
commited_disk_filepath = "#{DmsfHelper.temp_dir}/#{commited_file[:disk_filename].gsub(/[\/\\]/,'')}"
new_revision.file = file
new_revision.user = User.current
new_revision.name = name
@ -183,43 +183,49 @@ class DmsfUploadController < ApplicationController
new_revision.major_version = commited_file[:custom_version_major].to_i
new_revision.minor_version = commited_file[:custom_version_minor].to_i
else
new_revision.increase_version(version, true)
new_revision.increase_version(version, true)
end
new_revision.mime_type = Redmine::MimeType.of(new_revision.name)
new_revision.size = File.size(commited_disk_filepath)
# Need to save file first to generate id for it in case of creation.
# File id is needed to properly generate revision disk filename
# Need to save file first to generate id for it in case of creation.
# File id is needed to properly generate revision disk filename
if commited_file[:dmsf_file_revision].present?
commited_file[:dmsf_file_revision][:custom_field_values].each_with_index do |v, i|
new_revision.custom_field_values[i].value = v[1]
end
end
if new_revision.valid? && file.save
new_revision.disk_filename = new_revision.new_storage_filename
else
failed_uploads.push(commited_file)
next
end
if new_revision.save
new_revision.assign_workflow(commited_file[:dmsf_workflow_id])
FileUtils.mv(commited_disk_filepath, new_revision.disk_file)
file.set_last_revision new_revision
@files.push(file)
new_revision.assign_workflow(commited_file[:dmsf_workflow_id])
begin
FileUtils.mv(commited_disk_filepath, new_revision.disk_file)
file.set_last_revision new_revision
@files.push(file)
rescue Exception => e
Rails.logger.error e.message
flash[:error] = e.message
failed_uploads.push(file)
end
else
failed_uploads.push(commited_file)
end
end
unless @files.empty?
@files.each { |file| log_activity(file, 'uploaded') if file }
unless @files.empty?
@files.each { |file| log_activity(file, 'uploaded') if file }
if (@folder && @folder.notification?) || (!@folder && @project.dmsf_notification?)
begin
recipients = DmsfMailer.get_notify_users(@project, @files)
recipients.each do |u|
DmsfMailer.files_updated(u, @project, @files).deliver
end
end
if Setting.plugin_redmine_dmsf[:dmsf_display_notified_recipients] == '1'
unless recipients.empty?
to = recipients.collect{ |r| r.name }.first(DMSF_MAX_NOTIFICATION_RECEIVERS_INFO).join(', ')
@ -238,22 +244,22 @@ class DmsfUploadController < ApplicationController
end
respond_to do |format|
format.js
format.api {
render_validation_errors(failed_uploads) unless failed_uploads.empty?
format.api {
render_validation_errors(failed_uploads) unless failed_uploads.empty?
}
format.html { redirect_to dmsf_folder_path(:id => @project, :folder_id => @folder) }
end
end
def log_activity(file, action)
Rails.logger.info "#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} #{User.current.login}@#{request.remote_ip}/#{request.env['HTTP_X_FORWARDED_FOR']}: #{action} dmsf://#{file.project.identifier}/#{file.id}/#{file.last_revision.id}"
end
def find_folder
@folder = DmsfFolder.visible.find(params[:folder_id]) if params.keys.include?('folder_id')
@folder = DmsfFolder.visible.find(params[:folder_id]) if params.keys.include?('folder_id')
rescue DmsfAccessError
render_403
end
end

View File

@ -29,13 +29,13 @@ end
class DmsfFile < ActiveRecord::Base
unloadable
include RedmineDmsf::Lockable
include RedmineDmsf::Lockable
belongs_to :project
belongs_to :folder, :class_name => 'DmsfFolder', :foreign_key => 'dmsf_folder_id'
belongs_to :deleted_by_user, :class_name => 'User', :foreign_key => 'deleted_by_user_id'
has_many :revisions, -> { order("#{DmsfFileRevision.table_name}.major_version DESC, #{DmsfFileRevision.table_name}.minor_version DESC, #{DmsfFileRevision.table_name}.updated_at DESC") },
:class_name => 'DmsfFileRevision', :foreign_key => 'dmsf_file_id',
:dependent => :destroy
@ -44,10 +44,10 @@ class DmsfFile < ActiveRecord::Base
has_many :referenced_links, -> { where target_type: DmsfFile.model_name.to_s},
:class_name => 'DmsfLink', :foreign_key => 'target_id', :dependent => :destroy
accepts_nested_attributes_for :revisions, :locks, :referenced_links, :project
STATUS_DELETED = 1
STATUS_ACTIVE = 0
scope :visible, -> { where(:deleted => STATUS_ACTIVE) }
scope :deleted, -> { where(:deleted => STATUS_DELETED) }
@ -61,27 +61,27 @@ class DmsfFile < ActiveRecord::Base
existing_file = DmsfFile.visible.find_file_by_name(self.project, self.folder, self.name)
errors.add(:name, l('activerecord.errors.messages.taken')) unless
existing_file.nil? || existing_file.id == self.id
end
end
acts_as_event :title => Proc.new { |o| o.name },
:description => Proc.new { |o|
desc = Redmine::Search.cache_store.fetch("DmsfFile-#{o.id}")
:description => Proc.new { |o|
desc = Redmine::Search.cache_store.fetch("DmsfFile-#{o.id}")
if desc
Redmine::Search.cache_store.delete("DmsfFile-#{o.id}")
Redmine::Search.cache_store.delete("DmsfFile-#{o.id}")
else
desc = o.description
desc += ' / ' if o.description.present? && o.last_revision.comment.present?
desc += o.last_revision.comment if o.last_revision.comment.present?
end
end
desc
},
:url => Proc.new { |o| {:controller => 'dmsf_files', :action => 'show', :id => o} },
:datetime => Proc.new { |o| o.updated_at },
:author => Proc.new { |o| o.last_revision.user }
acts_as_searchable :columns => ["#{table_name}.name", "#{DmsfFileRevision.table_name}.title", "#{DmsfFileRevision.table_name}.description", "#{DmsfFileRevision.table_name}.comment"],
:project_key => 'project_id',
:date_column => "#{table_name}.updated_at"
:date_column => "#{table_name}.updated_at"
before_create :default_values
def default_values
@ -96,19 +96,22 @@ class DmsfFile < ActiveRecord::Base
@@storage_path = nil
def self.storage_path
unless @@storage_path.present?
@@storage_path = Setting.plugin_redmine_dmsf['dmsf_storage_directory'].strip if Setting.plugin_redmine_dmsf['dmsf_storage_directory'].present?
@@storage_path = Pathname(Redmine::Configuration['attachments_storage_path']).join('dmsf') if @@storage_path.blank? && Redmine::Configuration['attachments_storage_path'].present?
@@storage_path = Rails.root.join('files/dmsf').to_s if @@storage_path.blank?
Dir.mkdir(@@storage_path) unless File.exists?(@@storage_path)
end
path = Setting.plugin_redmine_dmsf['dmsf_storage_directory'].strip if Setting.plugin_redmine_dmsf['dmsf_storage_directory'].present?
path = Pathname(Redmine::Configuration['attachments_storage_path']).join('dmsf') if path.blank? && Redmine::Configuration['attachments_storage_path'].present?
path = Rails.root.join('files/dmsf').to_s if path.blank?
DmsfFile.storage_path = path if path != @@storage_path
@@storage_path
end
# Lets introduce a write for storage path, that way we can also
# better interact from test-cases etc
def self.storage_path=(obj)
@@storage_path = obj
def self.storage_path=(path)
begin
FileUtils.mkdir_p(path) unless File.exists?(path)
rescue Exception => e
Rails.logger.error e.message
end
@@storage_path = path
end
def self.find_file_by_name(project, folder, name)
@ -128,7 +131,7 @@ class DmsfFile < ActiveRecord::Base
def set_last_revision(new_revision)
@last_revision = new_revision
end
def deleted?
self.deleted == STATUS_DELETED
end
@ -141,7 +144,7 @@ class DmsfFile < ActiveRecord::Base
end
begin
# Revisions and links of a deleted file SHOULD be deleted too
self.revisions.each { |r| r.delete(commit, true) }
self.revisions.each { |r| r.delete(commit, true) }
if commit
self.destroy
else
@ -161,7 +164,7 @@ class DmsfFile < ActiveRecord::Base
errors[:base] << l(:error_parent_folder)
return false
end
self.revisions.each { |r| r.restore }
self.revisions.each { |r| r.restore }
self.deleted = STATUS_ACTIVE
self.deleted_by_user = nil
save
@ -172,8 +175,8 @@ class DmsfFile < ActiveRecord::Base
end
def description
self.last_revision ? self.last_revision.description : ''
end
self.last_revision ? self.last_revision.description : ''
end
def version
self.last_revision ? self.last_revision.version : '0'
@ -291,33 +294,33 @@ class DmsfFile < ActiveRecord::Base
def self.search(tokens, projects = nil, options = {}, user = User.current)
tokens = [] << tokens unless tokens.is_a?(Array)
projects = [] << projects if projects.is_a?(Project)
project_ids = projects.collect(&:id) if projects
if options[:offset]
project_ids = projects.collect(&:id) if projects
if options[:offset]
limit_options = ["dmsf_files.updated_at #{options[:before] ? '<' : '>'} ?", options[:offset]]
end
if options[:titles_only]
columns = [searchable_options[:columns][1]]
else
else
columns = searchable_options[:columns]
end
token_clauses = columns.collect{ |column| "(LOWER(#{column}) LIKE ?)" }
sql = (['(' + token_clauses.join(' OR ') + ')'] * tokens.size).join(options[:all_words] ? ' AND ' : ' OR ')
sql = (['(' + token_clauses.join(' OR ') + ')'] * tokens.size).join(options[:all_words] ? ' AND ' : ' OR ')
find_options = [sql, * (tokens.collect {|w| "%#{w.downcase}%"} * token_clauses.size).sort]
project_conditions = []
project_conditions << Project.allowed_to_condition(user, :view_dmsf_files)
project_conditions << Project.allowed_to_condition(user, :view_dmsf_files)
project_conditions << "#{DmsfFile.table_name}.project_id IN (#{project_ids.join(',')})" if project_ids.present?
results = []
results = []
scope = self.visible.joins(:project, :revisions)
scope = scope.limit(options[:limit]) unless options[:limit].blank?
scope = scope.limit(options[:limit]) unless options[:limit].blank?
scope = scope.where(limit_options) unless limit_options.blank?
scope = scope.where(project_conditions.join(' AND '))
scope = scope.where(project_conditions.join(' AND '))
results = scope.where(find_options).uniq.to_a
if !options[:titles_only] && $xapian_bindings_available
@ -361,7 +364,7 @@ class DmsfFile < ActiveRecord::Base
enquire.query = query
matchset = enquire.mset(0, 1000)
if matchset
if matchset
matchset.matches.each { |m|
docdata = m.document.data{url}
dochash = Hash[*docdata.scan(/(url|sample|modtime|author|type|size)=\/?([^\n\]]+)/).flatten]
@ -372,14 +375,14 @@ class DmsfFile < ActiveRecord::Base
id_attribute = dmsf_attrs[0][1] if dmsf_attrs.length > 0
next if dmsf_attrs.length == 0 || id_attribute == 0
next unless results.select{|f| f.id.to_s == id_attribute}.empty?
dmsf_file = DmsfFile.visible.where(limit_options).where(:id => id_attribute).first
if dmsf_file
if user.allowed_to?(:view_dmsf_files, dmsf_file.project) &&
(project_ids.blank? || (project_ids.include?(dmsf_file.project.id)))
Redmine::Search.cache_store.write("DmsfFile-#{dmsf_file.id}",
dochash['sample'].force_encoding('UTF-8')) if dochash['sample']
if user.allowed_to?(:view_dmsf_files, dmsf_file.project) &&
(project_ids.blank? || (project_ids.include?(dmsf_file.project.id)))
Redmine::Search.cache_store.write("DmsfFile-#{dmsf_file.id}",
dochash['sample'].force_encoding('UTF-8')) if dochash['sample']
break if(!options[:limit].blank? && results.count >= options[:limit])
results << dmsf_file
end
@ -389,14 +392,14 @@ class DmsfFile < ActiveRecord::Base
end
end
end
[results, results.count]
end
def self.search_result_ranks_and_ids(tokens, user = User.current, projects = nil, options = {})
r = self.search(tokens, projects, options, user)[0]
r.map{ |f| [f.updated_at.to_i, f.id]}
end
end
def display_name
if self.name.length > 50
@ -404,33 +407,33 @@ class DmsfFile < ActiveRecord::Base
end
self.name
end
def image?
self.last_revision && !!(self.last_revision.disk_filename =~ /\.(bmp|gif|jpg|jpe|jpeg|png|svg)$/i)
end
def preview(limit)
result = 'No preview available'
if (self.last_revision.disk_filename =~ /\.(txt|ini|diff|c|cpp|php|csv|rb|h|erb|html|css|py)$/i)
begin
f = File.new(self.last_revision.disk_file)
f.each_line do |line|
f.each_line do |line|
case f.lineno
when 1
result = line
when limit.to_i + 1
break
else
result << line
end
result << line
end
end
rescue Exception => e
result = e.message
end
end
end
result
end
def formatted_name(format)
if self.last_revision
self.last_revision.formatted_name(format)
@ -438,5 +441,5 @@ class DmsfFile < ActiveRecord::Base
self.name
end
end
end

View File

@ -27,12 +27,12 @@ class DmsfFileRevision < ActiveRecord::Base
belongs_to :folder, :class_name => 'DmsfFolder', :foreign_key => 'dmsf_folder_id'
belongs_to :deleted_by_user, :class_name => 'User', :foreign_key => 'deleted_by_user_id'
has_many :access, :class_name => 'DmsfFileRevisionAccess', :foreign_key => 'dmsf_file_revision_id', :dependent => :destroy
has_many :dmsf_workflow_step_assignment, :dependent => :destroy
accepts_nested_attributes_for :access, :dmsf_workflow_step_assignment, :file, :user
has_many :dmsf_workflow_step_assignment, :dependent => :destroy
accepts_nested_attributes_for :access, :dmsf_workflow_step_assignment, :file, :user
STATUS_DELETED = 1
STATUS_ACTIVE = 0
scope :visible, -> { where(:deleted => STATUS_ACTIVE) }
scope :deleted, -> { where(:deleted => STATUS_DELETED) }
@ -42,7 +42,7 @@ class DmsfFileRevision < ActiveRecord::Base
:datetime => Proc.new {|o| o.updated_at },
:description => Proc.new {|o| o.comment },
:author => Proc.new {|o| o.user }
acts_as_activity_provider :type => 'dmsf_file_revisions',
:timestamp => "#{DmsfFileRevision.table_name}.updated_at",
:author_key => "#{DmsfFileRevision.table_name}.user_id",
@ -51,11 +51,11 @@ class DmsfFileRevision < ActiveRecord::Base
joins(
"INNER JOIN #{DmsfFile.table_name} ON #{DmsfFileRevision.table_name}.dmsf_file_id = #{DmsfFile.table_name}.id " +
"INNER JOIN #{Project.table_name} ON #{DmsfFile.table_name}.project_id = #{Project.table_name}.id").
where("#{DmsfFile.table_name}.deleted = ?", STATUS_ACTIVE)
where("#{DmsfFile.table_name}.deleted = ?", STATUS_ACTIVE)
validates :title, :presence => true
validates :title, :presence => true
validates_format_of :name, :with => DmsfFolder.invalid_characters,
:message => l(:error_contains_invalid_character)
:message => l(:error_contains_invalid_character)
def project
self.file.project if self.file
@ -136,7 +136,7 @@ class DmsfFileRevision < ActiveRecord::Base
project_base = project.identifier.gsub(/[^\w\.\-]/,'_')
storage_base << "/p_#{project_base}"
end
Dir.mkdir(storage_base) unless File.exists?(storage_base)
FileUtils.mkdir_p(storage_base) unless File.exists?(storage_base)
"#{storage_base}/#{self.disk_filename}"
end
@ -249,7 +249,7 @@ class DmsfFileRevision < ActiveRecord::Base
parts = self.version.split '.'
parts.size == 2 ? parts[0].to_i * 1000 + parts[1].to_i : 0
end
def formatted_name(format)
return self.name if format.blank?
if self.name =~ /(.*)(\..*)$/
@ -257,7 +257,7 @@ class DmsfFileRevision < ActiveRecord::Base
ext = $2
else
filename = self.name
end
end
format.sub!('%t', self.title)
format.sub!('%f', filename)
format.sub!('%d', self.updated_at.strftime('%Y%m%d%H%M%S'))