#48 Attaching DMS links to issues

This commit is contained in:
Karel Picman 2017-05-23 14:55:56 +02:00
parent 5940486c77
commit 10d786c6e4
27 changed files with 433 additions and 213 deletions

View File

@ -37,6 +37,7 @@ Features
* Documents and folders symbolic links
* Document tagging
* Trash bin
* Documents attachable to issues
* Compatible with Redmine 3.3.x
Dependencies
@ -235,9 +236,9 @@ Before installing ensure that the Redmine instance is stopped.
6. Restart the web server.
7. You should configure the plugin via Redmine interface: Administration -> Plugins -> DMSF -> Configure.
8. Assign DMSF permissions to appropriate roles.
9. There are two rake tasks:
9. There are a few rake tasks:
a) To convert documents from the standard Redmine document module
I) To convert documents from the standard Redmine document module
Available options:
@ -253,11 +254,42 @@ Before installing ensure that the Redmine instance is stopped.
chown -R www-data:www-data /redmine/files/dmsf
afterwards)
b) To alert all users who are expected to do an approval in the current approval steps
II) To alert all users who are expected to do an approval in the current approval steps
Example:
rake redmine:dmsf_alert_approvals RAILS_ENV="production"
rake redmine:dmsf_alert_approvals RAILS_ENV="production"
III) To clears the Webdav Cache
Example:
rake redmine:dmsf_clear_webdav_cache RAILS_ENV="production"
IV) To create missing MD5 digest for all file revisions
Available options:
*dry_run - test, no changes to the database
Example:
bundle exec rake redmine:dmsf_create_digests RAILS_ENV="production"
bundle exec rake redmine:dmsf_create_digests dry_run=1 RAILS_ENV="production"
V) To maintain DMSF
* Remove all files with no database record from the document directory
* Remove all links project_id = -1 (added links to an issue which hasn't been created)
Available options:
*dry_run - No physical deletion but to list of all unused files only
Example:
rake redmine:dmsf_maintenance RAILS_ENV="production"
rake redmine:dmsf_maintenance dry_run=1 RAILS_ENV="production"
### Fulltext search (optional)
If you want to use full-text search features, you must setup file content indexing.

View File

@ -728,19 +728,7 @@ class DmsfController < ApplicationController
end
end
# Remove system folders you are not allowed to see because you are not allowed to see the issue
@subfolders.delete_if{ |folder|
if folder.system
issue_id = folder.title.to_i
if issue_id > 0
issue = Issue.find_by_id issue_id
issue && !issue.visible?(User.current)
else
false
end
else
false
end
}
@subfolders = DmsfHelper.visible_folders(@subfolders)
end
@ajax_upload_size = Setting.plugin_redmine_dmsf['dmsf_max_ajax_upload_filesize'].present? ? Setting.plugin_redmine_dmsf['dmsf_max_ajax_upload_filesize'] : 100

View File

@ -26,6 +26,7 @@ class DmsfLinksController < ApplicationController
before_filter :find_link_project
before_filter :authorize
before_filter :permissions
protect_from_forgery except: :new
def permissions
if @dmsf_link
@ -43,109 +44,80 @@ class DmsfLinksController < ApplicationController
def new
@dmsf_link = DmsfLink.new
@dmsf_link.project_id = params[:project_id]
if params[:dmsf_link].present?
# Reload
@dmsf_link.dmsf_folder_id = params[:dmsf_link][:dmsf_folder_id]
@dmsf_file_id = params[:dmsf_link][:dmsf_file_id]
@type = params[:dmsf_link][:type]
@link_external = (@type == 'link_from') && (params[:external_link] == 'true')
@dmsf_link.target_project_id = params[:dmsf_link][:target_project_id]
@target_folder_id = params[:dmsf_link][:target_folder_id].to_i if params[:reload].blank? && DmsfLinksHelper.is_a_number?(params[:dmsf_link][:target_folder_id])
if @type == 'link_to'
if params[:dmsf_link][:dmsf_file_id].present?
file = DmsfFile.find_by_id params[:dmsf_link][:dmsf_file_id]
@dmsf_link.name = file.title if file
else
folder = DmsfFolder.find_by_id params[:dmsf_link][:dmsf_folder_id]
@dmsf_link.name = folder.title if folder
end
@dmsf_link.dmsf_folder_id = params[:dmsf_folder_id]
@dmsf_file_id = params[:dmsf_file_id]
@type = params[:type]
@dmsf_link.target_project_id = params[:project_id]
@dmsf_link.project_id = params[:project_id]
@target_folder_id = params[:dmsf_folder_id].to_i if params[:dmsf_folder_id].present?
if @type == 'link_to'
if @dmsf_file_id
file = DmsfFile.find_by_id @dmsf_file_id
@dmsf_link.name = file.title if file
else
if params[:dmsf_link][:target_file_id].present?
@target_file_id = params[:dmsf_link][:target_file_id]
file = DmsfFile.find_by_id @target_file_id
if file
folder = DmsfFolder.find_by_id params[:dmsf_link][:target_folder_id]
if (folder && (folder.project_id == @dmsf_link.target_project_id) && folder.dmsf_files.include?(file)) || folder.nil?
@dmsf_link.name = file.title
end
end
else
folder = DmsfFolder.find_by_id params[:dmsf_link][:target_folder_id]
if folder
if folder.project_id == @dmsf_link.target_project_id
@dmsf_link.name = folder.title if folder
end
end
end
end
else
# Link from/to
@dmsf_link.dmsf_folder_id = params[:dmsf_folder_id]
@dmsf_file_id = params[:dmsf_file_id]
@type = params[:type]
@link_external = false
@dmsf_link.target_project_id = params[:project_id]
@dmsf_link.project_id = params[:project_id]
@target_folder_id = params[:dmsf_folder_id].to_i if params[:dmsf_folder_id].present?
if @type == 'link_to'
if @dmsf_file_id
file = DmsfFile.find_by_id @dmsf_file_id
@dmsf_link.name = file.title if file
else
folder = DmsfFolder.find_by_id @target_folder_id
@dmsf_link.name = folder.title if folder
end
folder = DmsfFolder.find_by_id @target_folder_id
@dmsf_link.name = folder.title if folder
end
end
@container = params[:container]
respond_to do |format|
format.html
format.js
end
end
render :layout => !request.xhr?
def autocomplete_for_project
respond_to do |format|
format.js
end
end
def autocomplete_for_folder
respond_to do |format|
format.js
end
end
def create
@dmsf_link = DmsfLink.new
@dmsf_link.user = User.current
if params[:dmsf_link][:type] == 'link_from'
# Link from
@dmsf_link.project_id = params[:dmsf_link][:project_id]
@dmsf_link.dmsf_folder_id = params[:dmsf_link][:dmsf_folder_id]
if params[:dmsf_link][:container].blank?
@dmsf_link.project_id = params[:dmsf_link][:project_id]
@dmsf_link.dmsf_folder_id = params[:dmsf_link][:dmsf_folder_id]
else
# An issue link
@dmsf_link.project_id = -1
@dmsf_link.dmsf_folder_id = nil
end
@dmsf_link.target_project_id = params[:dmsf_link][:target_project_id]
@link_external = (params[:external_link] == 'true')
@dmsf_link.external_url = params[:dmsf_link][:external_url]
if (@link_external)
if (params[:external_link] == 'true')
@dmsf_link.external_url = params[:dmsf_link][:external_url]
@dmsf_link.target_type = 'DmsfUrl'
elsif params[:dmsf_link][:target_file_id].present?
@dmsf_link.target_id = params[:dmsf_link][:target_file_id]
@dmsf_link.target_type = DmsfFile.model_name.to_s
else
@dmsf_link.target_id = DmsfLinksHelper.is_a_number?(params[:dmsf_link][:target_folder_id]) ? params[:dmsf_link][:target_folder_id].to_i : nil
@dmsf_link.target_id = DmsfLinksHelper.is_a_number?(
params[:dmsf_link][:target_folder_id]) ? params[:dmsf_link][:target_folder_id].to_i : nil
@dmsf_link.target_id = nil if(@dmsf_link.target_id == 0)
@dmsf_link.target_type = DmsfFolder.model_name.to_s
end
@dmsf_link.name = params[:dmsf_link][:name]
if @dmsf_link.save
flash[:notice] = l(:notice_successful_create)
redirect_to dmsf_folder_path(:id => @project.id, :folder_id => @dmsf_link.dmsf_folder_id)
else
@dmsf_file_id = params[:dmsf_link][:dmsf_file_id]
@type = params[:dmsf_link][:type]
@target_folder_id = params[:dmsf_link][:target_folder_id].to_i if DmsfLinksHelper.is_a_number?(params[:dmsf_link][:target_folder_id])
@target_file_id = @dmsf_link.target_id if @dmsf_link.target_type == DmsfFile.model_name.to_s
render :action => 'new'
flash[:error] = @dmsf_link.errors.full_messages.to_sentence
end
else
# Link to
@dmsf_link.project_id = params[:dmsf_link][:target_project_id]
@dmsf_link.dmsf_folder_id = DmsfLinksHelper.is_a_number?(params[:dmsf_link][:target_folder_id]) ? params[:dmsf_link][:target_folder_id].to_i : nil
@dmsf_link.dmsf_folder_id = DmsfLinksHelper.is_a_number?(
params[:dmsf_link][:target_folder_id]) ? params[:dmsf_link][:target_folder_id].to_i : nil
@dmsf_link.dmsf_folder_id = nil if(@dmsf_link.dmsf_folder_id == 0)
@dmsf_link.target_project_id = params[:dmsf_link][:project_id]
@link_external = (params[:external_link] == 'true')
@dmsf_link.external_url = params[:dmsf_link][:external_url]
if (@link_external)
@dmsf_link.target_type = 'DmsfUrl'
elsif params[:dmsf_link][:dmsf_file_id].present?
if params[:dmsf_link][:dmsf_file_id].present?
@dmsf_link.target_id = params[:dmsf_link][:dmsf_file_id]
@dmsf_link.target_type = DmsfFile.model_name.to_s
else
@ -153,24 +125,26 @@ class DmsfLinksController < ApplicationController
@dmsf_link.target_type = DmsfFolder.model_name.to_s
end
@dmsf_link.name = params[:dmsf_link][:name]
if @dmsf_link.save
flash[:notice] = l(:notice_successful_create)
if params[:dmsf_link][:dmsf_file_id].present?
redirect_to dmsf_file_path(@dmsf_link.target_file)
else
redirect_to edit_dmsf_path(:id => params[:dmsf_link][:project_id], :folder_id => params[:dmsf_link][:dmsf_folder_id])
end
else
@dmsf_file_id = params[:dmsf_link][:dmsf_file_id]
@type = params[:dmsf_link][:type]
@target_folder_id = @dmsf_link.dmsf_folder_id
@dmsf_link.target_project_id = @dmsf_link.project.id
@dmsf_link.project_id = params[:dmsf_link][:project_id]
@dmsf_link.dmsf_folder_id = params[:dmsf_link][:dmsf_folder_id]
render :action => 'new'
flash[:error] = @dmsf_link.errors.full_messages.to_sentence
end
end
respond_to do |format|
format.html {
if params[:dmsf_link][:type] == 'link_from'
redirect_to dmsf_folder_path(:id => @project.id, :folder_id => @dmsf_link.dmsf_folder_id)
else
if params[:dmsf_link][:dmsf_file_id].present?
redirect_to dmsf_file_path(@dmsf_link.target_file)
else
redirect_to edit_dmsf_path(:id => params[:dmsf_link][:project_id], :folder_id => params[:dmsf_link][:dmsf_folder_id])
end
end
}
format.js
end
end
def destroy

View File

@ -89,9 +89,30 @@ module DmsfHelper
'plupload/js/i18n/en.js'
end
def self.visible_folders(folders)
allowed = Setting.plugin_redmine_dmsf['dmsf_act_as_attachable']
folders.reject{ |folder|
if folder.system
unless allowed
true
else
issue_id = folder.title.to_i
if issue_id > 0
issue = Issue.find_by_id issue_id
issue && !issue.visible?(User.current)
else
false
end
end
else
false
end
}
end
def self.all_children_sorted(parent, pos, ident)
# Folders && files && links
nodes = parent.dmsf_folders.visible + parent.dmsf_links.visible + parent.dmsf_files.visible
nodes = visible_folders(parent.dmsf_folders.visible.to_a) + parent.dmsf_links.visible + parent.dmsf_files.visible
# Alphabetical and type sort
nodes.sort! do |x, y|
if ((x.is_a?(DmsfFolder) || (x.is_a?(DmsfLink) && x.is_folder?)) &&

View File

@ -37,6 +37,18 @@ module DmsfLinksHelper
# An integer test
def self.is_a_number?(s)
s.to_s.match(/\A[+-]?\d+?(\.\d+)?\Z/) == nil ? false : true
end
end
def files_for_select(project_id, folder_id)
files = []
if folder_id && (folder_id != '0')
folder = DmsfFolder.find_by_id folder_id
files = folder.dmsf_files.visible.to_a if folder
elsif project_id
project = Project.find_by_id project_id
files = project.dmsf_files.visible.to_a if project
end
files
end
end

View File

@ -210,7 +210,10 @@ class DmsfFolder < ActiveRecord::Base
end
def self.directory_tree(project, current_folder = nil)
tree = [[l(:link_documents), nil]]
unless project.is_a? Project
project = Project.find_by_id project
end
tree = [[l(:link_documents), 0]]
project.dmsf_folders.visible(false).each do |folder|
unless folder == current_folder
tree.push(["...#{folder.title}", folder.id])

View File

@ -122,8 +122,17 @@ class DmsfLink < ActiveRecord::Base
link
end
def container
if self.folder && self.folder.system
Issue.where(:id => self.folder.title.to_i).first
end
end
def delete(commit = false)
if commit
if self.container.is_a?(Issue)
self.container.dmsf_file_removed(self.target_file)
end
self.destroy
else
self.deleted = STATUS_DELETED

View File

@ -59,8 +59,9 @@
<% end %>
<% if @file_manipulation_allowed && !@locked_for_user %>
<%= link_to(l(:label_link_from),
new_dmsf_link_path(:project_id => @project.id, :dmsf_folder_id => @folder ? @folder.id : @folder, :type => 'link_from'),
:title => l(:title_create_link), :class => 'icon icon-link') %>
new_dmsf_link_path(:project_id => @project.id, :dmsf_folder_id => @folder ? @folder.id : @folder,
:type => 'link_from'), :title => l(:title_create_link),
:class => 'icon icon-link') %>
<% end %>
<%= link_to(l(:link_create_folder),
new_dmsf_path(:id => @project, :parent_id => @folder),
@ -100,7 +101,6 @@
<% custom_value.custom_field.is_required = false %>
<% custom_value.value = params[:custom_value].present? ? params[:custom_value] : '' %>
<%= h(custom_value.custom_field.name) %>:
<%#= custom_field_tag(:dmsf_folder, custom_value, :style => 'width: auto') %>
<%= custom_value.custom_field.format.edit_tag(self,
custom_field_tag_id(:dmsf_folder, custom_value.custom_field),
custom_field_tag_name(:dmsf_folder, custom_value.custom_field),

View File

@ -78,7 +78,7 @@
<div class="box">
<p>
<%= label_tag('file_upload', l(:label_new_content)) %>
<%= render :partial => 'dmsf_upload/form', :locals => { :multiple => false } %>
<%= render :partial => 'dmsf_upload/form', :locals => { :multiple => false, :container => nil } %>
</p>
</div>
<p>

View File

@ -20,7 +20,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
%>
<% if dmsf_files.present? %>
<% if dmsf_files.present? || dmsf_links.present? %>
<hr/>
<div class="attachments dmsf_parent_container">
<% for dmsf_file in dmsf_files %>
@ -76,4 +76,58 @@
<% end %>
<% end %>
</div>
<% for dmsf_link in dmsf_links %>
<% dmsf_file = dmsf_link.target_file %>
<% if dmsf_file.last_revision %>
<p class = "dmsf_gray">
<% file_view_url = url_for({:controller => :dmsf_files, :action => 'view', :id => dmsf_file}) %>
<%= link_to(h(dmsf_link.title),
file_view_url,
:target => '_blank',
:class => "icon icon-file #{DmsfHelper.filetype_css(dmsf_file.name)}",
:title => h(dmsf_file.last_revision.try(:tooltip)),
'data-downloadurl' => "#{dmsf_file.last_revision.detect_content_type}:#{h(dmsf_file.name)}:#{file_view_url}") %>
<% if dmsf_file.text? || dmsf_file.image? %>
<%= link_to l(:button_view),
file_view_url,
:class => 'icon-only icon-magnifier',
:title => l(:button_view) %>
<% end %>
<%= " - #{dmsf_file.description}" unless dmsf_file.description.blank? %>
<span class="size">(<%= number_to_human_size dmsf_file.last_revision.size %>)</span>
<% if @issue.attributes_editable? && User.current.allowed_to?(:file_delete, dmsf_file.project) %>
<%= link_to('',
dmsf_link_path(dmsf_link, :commit => 'yes'),
:data => {:confirm => l(:text_are_you_sure)},
:method => :delete,
:title => l(:title_delete),
:class => 'icon icon-del') %>
<% end %>
<span class="author"><%= dmsf_file.last_revision.user %>, <%= format_time(dmsf_file.last_revision.updated_at) %></span>
<span class="dmsf_upload_select">
<% wf = DmsfWorkflow.find_by_id(dmsf_file.last_revision.dmsf_workflow_id) if dmsf_file.last_revision.dmsf_workflow_id %>
<%= render(:partial => 'dmsf_files/approval_workflow_button',
:locals => {:file => dmsf_file,
:file_approval_allowed => User.current.allowed_to?(:file_approval, dmsf_file.project),
:workflows_available => DmsfWorkflow.where(['project_id = ? OR project_id IS NULL', dmsf_file.project.id]).exists?,
:project => dmsf_file.project, :wf => wf }) %>
</span>
</p>
<% end %>
<% end %>
<% if defined?(thumbnails) && thumbnails %>
<% images = dmsf_links.map{ |link| link.target_file }.select(&:image?) %>
<% if images.any? %>
<div class="thumbnails">
<% images.each do |file| %>
<div>
<%= link_to image_tag(dmsf_thumbnail_path(file)),
url_for({:controller => :dmsf_files, :action => 'view', :id => file}),
:alt => dmsf_file.title %>
</div>
<% end %>
</div>
<% end %>
<% end %>
</div>
<% end %>

View File

@ -20,40 +20,41 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
%>
<% html_title l(:dmsf) %>
<h3 class="title"><%= l(:title_create_link) %></h3>
<% if @dmsf_file_id %>
<% file = DmsfFile.find_by_id @dmsf_file_id%>
<% title = file.title if file %>
<% end %>
<%= render(:partial => '/dmsf/path',
:locals => {:folder => @dmsf_link.folder, :filename => nil,
:title => (@type == 'link_from') ? l(:label_link_from) : l(:label_link_to) }) %>
<%= labelled_form_for @dmsf_link do |f| %>
<%= labelled_form_for @dmsf_link, :remote => modal do |f| %>
<%= error_messages_for @dmsf_link %>
<%= f.hidden_field :project_id, :value => @dmsf_link.project_id %>
<%= f.hidden_field :dmsf_folder_id, :value => @dmsf_link.dmsf_folder_id if @dmsf_link.dmsf_folder_id %>
<%= f.hidden_field :type, :value => @type %>
<%= f.hidden_field :dmsf_file_id, :value => @dmsf_file_id %>
<%= f.hidden_field(:container, :value => @container) if @container %>
<div class="box tabular">
<% if @type == 'link_from' %>
<% if (@type == 'link_from') && !@container %>
<p>
<%= radio_button_tag(:external_link, 'false', @link_external == false) %> <%= l(:label_internal) %><br/>
<%= radio_button_tag(:external_link, 'true', @link_external) %> <%= l(:label_external) %>
<%= radio_button_tag(:external_link, 'false', true) %> <%= l(:label_internal) %><br/>
<%= radio_button_tag(:external_link, 'true', false) %> <%= l(:label_external) %>
</p>
<% end %>
<div id="link_internal" style="<%= @link_external ? 'display:none' : '' %>">
<div id="link_internal">
<p>
<% if @type == 'link_from' %>
<label for="dmsf_link[target_project_id]"><%= l(:label_source_project) %><span class="required">*</span></label>
<label for="dmsf_link[target_project_id]"><%= l(:label_source_project) %></label>
<% else %>
<label for="dmsf_link[target_project_id]"><%= l(:label_target_project) %><span class="required">*</span></label>
<label for="dmsf_link[target_project_id]"><%= l(:label_target_project) %></label>
<% end %>
<%= select_tag('dmsf_link[target_project_id]',
project_tree_options_for_select(DmsfFile.allowed_target_projects_on_copy,
:selected => @dmsf_link.target_project)) %>
:selected => @dmsf_link.target_project), :style => "width=100%") %>
<%= javascript_tag do %>
$('#dmsf_link_target_project_id').change(function(){
$.ajax({
url: '<%= autocomplete_for_project_dmsf_link_path(@project, :format => 'js') %>',
type: 'get',
data: $('#new_dmsf_link').serialize()
});
});
<% end %>
</p>
<p>
<% if @type == 'link_from' %>
@ -64,51 +65,50 @@
<%= select_tag('dmsf_link[target_folder_id]',
folder_tree_options_for_select(DmsfFolder.directory_tree(@dmsf_link.target_project),
:selected => @target_folder_id)) %>
<%= javascript_tag do %>
$('#dmsf_link_target_folder_id').change(function(){
$.ajax({
url: '<%= autocomplete_for_folder_dmsf_link_path(@project, :format => 'js') %>',
type: 'get',
data: $('#new_dmsf_link').serialize()
});
});
<% end %>
</p>
<% if @type == 'link_from' %>
<p>
<% if @target_folder_id %>
<% folder = DmsfFolder.find_by_id @target_folder_id %>
<% files = folder.dmsf_files.visible if folder %>
<% else %>
<% files = @dmsf_link.target_project.dmsf_files.visible if @dmsf_link.target_project %>
<% end %>
<%= f.select(:target_file_id,
options_for_select(DmsfFolder.file_list(files), @target_file_id)) %>
<%= label_tag('dmsf_link[target_file_id]', l(:field_target_file)) %>
<% files = files_for_select(@dmsf_link.target_project.id, @target_folder_id) %>
<%= select_tag('dmsf_link[target_file_id]',
options_for_select(DmsfFolder.file_list(files))) %>
</p>
<% end %>
</div>
<div id="link_external" style="<%= @link_external ? '' : 'display:none' %>">
<div id="link_external" style="display: none">
<p>
<%= f.text_field :external_url, :label => l(:label_link_external_url) %>
<%= f.text_field :external_url, :required => true %>
</p>
</div>
<p>
<%= f.text_field :name, :label => l(:label_link_name) %>
<%= f.text_field :name, :required => true %>
</p>
</div>
<p><%= f.submit l(:button_create) %></p>
<p><%= f.submit l(:button_create), :onclick => 'hideModal(this);' %></p>
<% end %>
<script type="text/javascript">
$('#dmsf_link_target_project_id').change(function () {
$('#content').load("<%= url_for(:action => 'new', :project_id => @project.id, :reload => true) %>", $('#new_dmsf_link').serialize());
});
$('#dmsf_link_target_folder_id').change(function () {
$('#content').load("<%= url_for(:action => 'new', :project_id => @project.id) %>", $('#new_dmsf_link').serialize());
});
// Suggest a link name when a file is selected
$('#dmsf_link_target_file_id').change(function () {
$('#content').load("<%= url_for(:action => 'new', :project_id => @project.id) %>", $('#new_dmsf_link').serialize());
});
var linkName = $('#dmsf_link_name');
var name = linkName.val();
if(name == '') {
linkName.val($('#dmsf_link_target_file_id option:selected').text().replace(/\./g, ''));
}
});
// Internal/External link switch
$("input[name=external_link]:radio").change(function(){
$("#link_internal").toggle();
$("#link_external").toggle();
});
$('#dmsf_link_target_project_id').select2();
$('#dmsf_link_target_folder_id').select2();
$('#dmsf_link_target_file_id').select2();
</script>
</script>

View File

@ -0,0 +1,4 @@
$('#dmsf_link_target_file_id').html('<%= escape_javascript(
select_tag('dmsf_link[target_file_id]',
options_for_select(DmsfFolder.file_list(files_for_select(params[:dmsf_link][:target_project_id],
params[:dmsf_link][:target_folder_id]))))) %>');

View File

@ -0,0 +1,4 @@
$('#dmsf_link_target_folder_id').html('<%= escape_javascript(
select_tag('dmsf_link[target_folder_id]',
folder_tree_options_for_select(DmsfFolder.directory_tree(params[:dmsf_link][:target_project_id])))) %>');
$('#dmsf_link_target_folder_id').change();

View File

@ -0,0 +1,27 @@
<%
# encoding: utf-8
#
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2011-17 Karel Pičman <karel.picman@kontron.com>
#
# 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.
%>
var linksSpan = $("#dmsf_links_fields");
var linkId = "<%= @dmsf_link.id %>";
var linkName = "<%= @dmsf_link.name %>";
dmsfAddLink(linksSpan, linkId, linkName);

View File

@ -20,4 +20,4 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
%>
<%= render 'form' %>
<%= render :partial => 'form', :locals => { :modal => false } %>

View File

@ -0,0 +1,24 @@
<%
# encoding: utf-8
#
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2011-17 Karel Pičman <karel.picman@kontron.com>
#
# 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.
%>
$('#ajax-modal').html('<%= escape_javascript(render :partial => 'form', :locals => { :modal => true }) %>');
showModal('ajax-modal', '40%');

View File

@ -21,7 +21,7 @@
%>
<span id="dmsf_attachments_fields">
<% if defined?(container) && container && container.saved_dmsf_attachments %>
<% if defined?(container) && container && container.saved_dmsf_attachments.present? %>
<% container.saved_dmsf_attachments.each_with_index do |attachment, i| %>
<span id="dmsf_attachments_p<%= i %>">
<%= text_field_tag("dmsf_attachments[p#{i}][filename]", attachment.filename, :class => 'filename') +
@ -47,3 +47,24 @@
} %>
(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)
</span>
<% if container %>
<br/>
<span id="dmsf_links_fields">
<% if container.saved_dmsf_links.present? %>
<% container.saved_dmsf_links.each_with_index do |dmsf_link, index| %>
<span id="dmsf_links_<%= index %>">
<input name="dmsf_links[<%= index %>]" value="<%= dmsf_link.id %>" type="hidden">
<input type="text" class='filename readonly' value="<%= dmsf_link.name %>">
<a href="#" class="remove-upload icon icon-del" onclick="$(this).parent('span').remove();return false;">&nbsp;</a>
<br/>
</span>
<% end %>
<% end %>
</span>
<span class="dmsf_add_link">
<%= link_to l(:label_link_from),
new_dmsf_link_path(:project_id => container.project.id, :type => 'link_from', :container => 'issue'),
:title => l(:title_create_link), :class => 'icon icon-add', :remote => true %>
</span>
<% end %>

View File

@ -40,7 +40,7 @@
<div class="dmsf_uploader">
<p>
<label><%= l(:label_document_plural) %></label>
<%= render :partial => 'dmsf_upload/form', :locals => { :multiple => true } %>
<%= render :partial => 'dmsf_upload/form', :locals => { :multiple => true, :container => nil } %>
</p>
</div>
<%= submit_tag l(:label_upload) %>

View File

@ -1,4 +1,5 @@
<%# Redmine plugin for Document Management System "Features"
<%
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2011-15 Karel Pičman <karel.picman@kontron.com>
#
@ -14,7 +15,8 @@
#
# 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.%>
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
%>
$('#ajax-modal').html('<%= escape_javascript(render :partial => 'assign', :locals => {:workflow => @dmsf_workflow}) %>');
showModal('ajax-modal', '30%');

View File

@ -19,6 +19,24 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
function dmsfAddLink(linksSpan, linkId, linkName) {
if (linksSpan.children().length < 10) {
var nextLinkId = dmsfAddLink.nextLinkId++;
var linkSpan = $('<span>', { id: 'dmsf_links_' + nextLinkId });
linkSpan.append(
"<input name='dmsf_links[" + nextLinkId + "]' value='" + linkId + "' type='hidden'>",
"<input type='text' class='filename readonly' value='" + linkName + "'>",
$('<a>&nbsp;</a>').attr({href: "#", 'class': 'remove-upload icon icon-del'}).click(dmsfRemoveFile),
"<br/>"
).appendTo(linksSpan);
}
}
dmsfAddLink.nextLinkId = 1000;
function dmsfAddFile(inputEl, file, eagerUpload) {
if ($('#dmsf_attachments_fields').children().length < 10) {

View File

@ -319,3 +319,5 @@ div.dmsf_revision_inner_box .attribute .label {
#dmsf_attachments_fields .ajax-loading input.filename {background:url(../../../images/loading.gif) no-repeat 0px 50%;}
#dmsf_attachments_fields div.ui-progressbar { width: 100px; height:14px; margin: 2px 0 -5px 8px; display: inline-block; }
#dmsf_links_fields span {display:block; white-space:nowrap;}
#dmsf_links_fields input.filename {border:0; height:1.8em; width:250px; color:#555; background-color:inherit; background:url(../../../images/link.png) no-repeat 1px 50%; padding-left:18px;}

View File

@ -151,6 +151,8 @@ if Redmine::Plugin.installed? :redmine_dmsf
resources :dmsf_links do
member do
get 'restore'
get 'autocomplete_for_project'
get 'autocomplete_for_folder'
end
end

View File

@ -89,7 +89,7 @@ Redmine::Plugin.register :redmine_dmsf do
permission :file_manipulation,
{ :dmsf_files => [:create_revision, :lock, :unlock, :delete_revision, :notify_activate, :notify_deactivate, :restore],
:dmsf_upload => [:upload_files, :upload_file, :upload, :commit_files, :commit, :delete_dmsf_attachment],
:dmsf_links => [:new, :create, :destroy, :restore] }
:dmsf_links => [:new, :create, :destroy, :restore, :autocomplete_for_project, :autocomplete_for_folder] }
permission :file_delete,
{ :dmsf => [:trash, :delete_entries],
:dmsf_files => [:delete] }

View File

@ -59,6 +59,7 @@ module RedmineDmsf
issue = context[:issue]
params = context[:params]
issue.save_dmsf_attachments(params[:dmsf_attachments])
issue.save_dmsf_links(params[:dmsf_links])
end
end
@ -85,6 +86,21 @@ module RedmineDmsf
DmsfUploadHelper.commit_files_internal uploaded_files, issue.project, system_folder,
context[:controller]
end
dmsf_links = params[:dmsf_links]
if dmsf_links && dmsf_links.is_a?(Hash)
system_folder = issue.system_folder(true)
ids = dmsf_links.map(&:last)
ids.each do |id|
l = DmsfLink.find_by_id(id)
if l
l.project_id = system_folder.project_id
l.dmsf_folder_id = system_folder.id
if l.save
issue.dmsf_file_added l.target_file
end
end
end
end
end
end

View File

@ -49,7 +49,8 @@ module RedmineDmsf
if User.current.allowed_to?(:view_dmsf_files, issue.project) &&
Setting.plugin_redmine_dmsf['dmsf_act_as_attachable']
context[:controller].send(:render_to_string, {:partial => 'dmsf_files/links',
:locals => { :dmsf_files => issue.dmsf_files.to_a, :thumbnails => Setting.thumbnails_enabled? }})
:locals => { :dmsf_files => issue.dmsf_files, :dmsf_links => issue.dmsf_links,
:thumbnails => Setting.thumbnails_enabled? }})
end
end
end

View File

@ -34,10 +34,12 @@ module RedmineDmsf
def save_dmsf_attachments(dmsf_attachments)
@saved_dmsf_attachments = []
dmsf_attachments = dmsf_attachments.map(&:last)
dmsf_attachments.each do |dmsf_attachment|
a = Attachment.find_by_token(dmsf_attachment[:token])
@saved_dmsf_attachments << a if a
if dmsf_attachments
dmsf_attachments = dmsf_attachments.map(&:last)
dmsf_attachments.each do |dmsf_attachment|
a = Attachment.find_by_token(dmsf_attachment[:token])
@saved_dmsf_attachments << a if a
end
end
end
@ -45,6 +47,21 @@ module RedmineDmsf
@saved_dmsf_attachments || []
end
def save_dmsf_links(dmsf_links)
@saved_dmsf_links = []
if dmsf_links
ids = dmsf_links.map(&:last)
ids.each do |id|
l = DmsfLink.find_by_id(id)
@saved_dmsf_links << l if l
end
end
end
def saved_dmsf_links
@saved_dmsf_links || []
end
def system_folder(create = false)
parent = DmsfFolder.system.where(:project_id => self.project_id, :title => '.Issues').first
if create && !parent
@ -73,8 +90,21 @@ module RedmineDmsf
end
def dmsf_files
files = []
folder = self.system_folder
folder.dmsf_files if folder
if folder
files = folder.dmsf_files.to_a
end
files
end
def dmsf_links
links = []
folder = self.system_folder
if folder
links = folder.dmsf_links
end
links
end
def delete_system_folder
@ -105,6 +135,7 @@ module RedmineDmsf
)
current_journal.save
end
end
end

View File

@ -18,10 +18,11 @@
desc <<-END_DESC
DMSF maintenance task
* Remove all files and folders with no database record from the document directory
* Remove all files with no database record from the document directory
* Remove all links project_id = -1 (added links to an issue which hasn't been created)
Available options:
*dry_run - No physical deletion but to list of all unused files and folders only
*dry_run - No physical deletion but to list of all unused files only
Example:
rake redmine:dmsf_maintenance RAILS_ENV="production"
@ -34,7 +35,7 @@ namespace :redmine do
begin
STDERR.puts "\n"
Dir.chdir(DmsfFile.storage_path)
m.files
m.files
if m.dry_run
m.result
else
@ -54,54 +55,41 @@ class DmsfMaintenance
def initialize
@dry_run = ENV['dry_run']
@folders_to_delete = Array.new
@files_to_delete = Array.new
end
def files
Dir.glob("**/*").each do |f|
if Dir.exist?(f)
check_dir f
else
unless Dir.exist?(f)
check_file f
end
end
end
def result
if (@files_to_delete.count == 0) && (@folders_to_delete.count == 0)
puts "\nNo orphens!\n\n"
return
end
def result
# Files
size = 0
@files_to_delete.each{ |f| size += File.size(f) }
puts "\n#{@files_to_delete.count} files havn't got a coresponding revision and can be deleted"
puts "#{number_to_human_size(size)} can be released\n\n"
# Projects
puts "\n#{@folders_to_delete.count} directories havn't got coresponding projects and can be deleted\n\n" if(@folders_to_delete.count > 0)
puts "\n#{@files_to_delete.count} files havn't got a coresponding revision and can be deleted."
puts "#{number_to_human_size(size)} can be released.\n\n"
# Links
size = DmsfLink.where(:project_id => -1).count
puts "#{size} links can be deleted.\n\n"
end
def clean
if (@files_to_delete.count == 0) && (@folders_to_delete.count == 0)
puts "\nNo orphens!\n\n"
return
end
def clean
# Files
size = 0
@files_to_delete.each do |f|
size += File.size(f)
File.delete f
end
puts "\n#{@files_to_delete.count} files hadn't got a coresponding revision and have been be deleted" if(@files_to_delete.count > 0)
puts "#{number_to_human_size(size)} has been released\n\n" if(@files_to_delete.count > 0)
# Projects
@folders_to_delete.each do |d|
Dir.delete d
end
puts "\n#{@folders_to_delete.count} directories hadn't got a coresponding projects and have been deleted\n\n" if(@folders_to_delete.count > 0)
puts "\n#{@files_to_delete.count} files hadn't got a coresponding revision and have been be deleted."
puts "#{number_to_human_size(size)} has been released\n\n"
# Links
size = DmsfLink.where(:project_id => -1).count
DmsfLink.delete_all(:project_id => -1)
puts "#{size} links have been deleted.\n\n"
end
private
@ -118,18 +106,5 @@ class DmsfMaintenance
STDERR.puts "\t#{file} doesn't seem to be a DMSF file!"
end
end
def check_dir(directory)
name = Pathname.new(directory).basename.to_s
if name =~ /^p_(.*)/
p = Project.find_by_identifier $1
unless p
@folders_to_delete << name
puts "\t#{name}"
end
else
STDERR.puts "\t#{directory} doesn't seem to be a DMSF folder!"
end
end
end