diff --git a/README.md b/README.md index 2e6b3133..3e8bba90 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/app/controllers/dmsf_controller.rb b/app/controllers/dmsf_controller.rb index 5fa504eb..feeced3a 100644 --- a/app/controllers/dmsf_controller.rb +++ b/app/controllers/dmsf_controller.rb @@ -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 diff --git a/app/controllers/dmsf_links_controller.rb b/app/controllers/dmsf_links_controller.rb index 08b0fce3..7d8eb1a2 100644 --- a/app/controllers/dmsf_links_controller.rb +++ b/app/controllers/dmsf_links_controller.rb @@ -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 diff --git a/app/helpers/dmsf_helper.rb b/app/helpers/dmsf_helper.rb index 808aaf8e..844b3593 100644 --- a/app/helpers/dmsf_helper.rb +++ b/app/helpers/dmsf_helper.rb @@ -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?)) && diff --git a/app/helpers/dmsf_links_helper.rb b/app/helpers/dmsf_links_helper.rb index 758e5ce3..aa6d1a2d 100644 --- a/app/helpers/dmsf_links_helper.rb +++ b/app/helpers/dmsf_links_helper.rb @@ -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 diff --git a/app/models/dmsf_folder.rb b/app/models/dmsf_folder.rb index a0787be6..6248d6f7 100644 --- a/app/models/dmsf_folder.rb +++ b/app/models/dmsf_folder.rb @@ -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]) diff --git a/app/models/dmsf_link.rb b/app/models/dmsf_link.rb index 26b837a7..08285c18 100644 --- a/app/models/dmsf_link.rb +++ b/app/models/dmsf_link.rb @@ -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 diff --git a/app/views/dmsf/show.html.erb b/app/views/dmsf/show.html.erb index fb930ba7..7d265216 100644 --- a/app/views/dmsf/show.html.erb +++ b/app/views/dmsf/show.html.erb @@ -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), diff --git a/app/views/dmsf_files/_file_new_revision.html.erb b/app/views/dmsf_files/_file_new_revision.html.erb index a0a2c9c9..b80e0cdf 100644 --- a/app/views/dmsf_files/_file_new_revision.html.erb +++ b/app/views/dmsf_files/_file_new_revision.html.erb @@ -78,7 +78,7 @@

<%= 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 } %>

diff --git a/app/views/dmsf_files/_links.html.erb b/app/views/dmsf_files/_links.html.erb index 1b928310..d32d3fd1 100644 --- a/app/views/dmsf_files/_links.html.erb +++ b/app/views/dmsf_files/_links.html.erb @@ -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? %>


<% for dmsf_file in dmsf_files %> @@ -76,4 +76,58 @@ <% end %> <% end %>
+ <% for dmsf_link in dmsf_links %> + <% dmsf_file = dmsf_link.target_file %> + <% if dmsf_file.last_revision %> +

+ <% 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? %> + (<%= number_to_human_size dmsf_file.last_revision.size %>) + <% 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 %> + <%= dmsf_file.last_revision.user %>, <%= format_time(dmsf_file.last_revision.updated_at) %> + + <% 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 }) %> + +

+ <% end %> + <% end %> + <% if defined?(thumbnails) && thumbnails %> + <% images = dmsf_links.map{ |link| link.target_file }.select(&:image?) %> + <% if images.any? %> +
+ <% images.each do |file| %> +
+ <%= link_to image_tag(dmsf_thumbnail_path(file)), + url_for({:controller => :dmsf_files, :action => 'view', :id => file}), + :alt => dmsf_file.title %> +
+ <% end %> +
+ <% end %> + <% end %> + <% end %> diff --git a/app/views/dmsf_links/_form.html.erb b/app/views/dmsf_links/_form.html.erb index ddbed231..608176db 100644 --- a/app/views/dmsf_links/_form.html.erb +++ b/app/views/dmsf_links/_form.html.erb @@ -20,40 +20,41 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. %> -<% html_title l(:dmsf) %> +

<%= l(:title_create_link) %>

-<% 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 %>
- <% if @type == 'link_from' %> + <% if (@type == 'link_from') && !@container %>

- <%= radio_button_tag(:external_link, 'false', @link_external == false) %> <%= l(:label_internal) %>
- <%= radio_button_tag(:external_link, 'true', @link_external) %> <%= l(:label_external) %> + <%= radio_button_tag(:external_link, 'false', true) %> <%= l(:label_internal) %>
+ <%= radio_button_tag(:external_link, 'true', false) %> <%= l(:label_external) %>

<% end %> -