diff --git a/.travis.yml b/.travis.yml index 012dc77d..47f092e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,6 +49,9 @@ after_script: - bash -x ./test/ci/redmine_install.sh -u env: + - DB=sqlite REDMINE_GIT_TAG=4.0-stable + - DB=mysql REDMINE_GIT_TAG=4.0-stable + - DB=postgres REDMINE_GIT_TAG=4.0-stable - DB=sqlite REDMINE_GIT_TAG=4.1-stable - DB=mysql REDMINE_GIT_TAG=4.1-stable - DB=postgres REDMINE_GIT_TAG=4.1-stable diff --git a/CHANGELOG.md b/CHANGELOG.md index f2bfe4d8..ceeeb0f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,13 @@ Changelog for Redmine DMSF ========================== -2.4.4 *????-??-??* +2.4.4 *2020-07-10* ------------------ - ? + Maintenance release + +* New: #1144 - Who has locked the document information is missing. +* Bug: #1142 - How to configure "Direct document or document link sending via email"? 2.4.3 *2020-06-12* ------------------ diff --git a/README.md b/README.md index 7911897b..da28f89a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Redmine DMSF Plugin =================== -The current version of Redmine DMSF is **2.4.4 devel** [](https://travis-ci.org/danmunn/redmine_dmsf) +The current version of Redmine DMSF is **2.4.4** [](https://travis-ci.org/danmunn/redmine_dmsf) Redmine DMSF is Document Management System Features plugin for Redmine issue tracking system; It is aimed to replace current Redmine's Documents module. @@ -36,12 +36,12 @@ Features * Documents and folders symbolic links * Trash bin * Documents attachable to issues - * Compatible with Redmine 4.1.x + * Compatible with Redmine 4.0.x and 4.1.x Dependencies ------------ - * Redmine 4.1.0 or higher + * Redmine 4.0.0 or higher ### Full-text search (optional) @@ -224,7 +224,7 @@ You can either clone the master branch or download the latest zipped version. Be `RAILS_ENV=production bundle exec rake db:migrate` - `RAILS_ENV=production bundle exec rake plugins:migrate NAME=redmine_dmsf` + `RAILS_ENV=production bundle exec rake redmine:plugins:migrate NAME=redmine_dmsf` 5. The access rights must be set for web server, example: `chown -R www-data:www-data plugins/redmine_dmsf`. 6. Restart the web server, e.g. `systemctl apache2 restart` 7. You should configure the plugin via Redmine interface: Administration -> Plugins -> DMSF -> Configure. diff --git a/app/controllers/dmsf_controller.rb b/app/controllers/dmsf_controller.rb index 45e70e15..b4a462b9 100644 --- a/app/controllers/dmsf_controller.rb +++ b/app/controllers/dmsf_controller.rb @@ -69,18 +69,30 @@ class DmsfController < ApplicationController render_404 return end - respond_to do |format| - format.html { - @dmsf_count = @query.dmsf_count - @dmsf_pages = Paginator.new @dmsf_count, per_page_option, params['page'] - render layout: !request.xhr? - } - format.api { - @offset, @limit = api_offset_and_limit - } - format.csv { - send_data query_to_csv(@query.dmsf_nodes, @query), type: 'text/csv; header=present', filename: 'dmsf.csv' - } + if @query.valid? + respond_to do |format| + format.html { + @dmsf_count = @query.dmsf_count + @dmsf_pages = Paginator.new @dmsf_count, per_page_option, params['page'] + render layout: !request.xhr? + } + format.api { + @offset, @limit = api_offset_and_limit + } + format.csv { + send_data query_to_csv(@query.dmsf_nodes, @query), type: 'text/csv; header=present', filename: 'dmsf.csv' + } + end + else + respond_to do |format| + format.html { + @dmsf_count = 0 + @dmsf_pages = Paginator.new @dmsf_count, per_page_option, params['page'] + render layout: !request.xhr? + } + format.any(:atom, :csv, :pdf) { head 422 } + format.api { render_validation_errors(@query) } + end end end diff --git a/app/controllers/dmsf_files_controller.rb b/app/controllers/dmsf_files_controller.rb index a988bc09..b12e138a 100644 --- a/app/controllers/dmsf_files_controller.rb +++ b/app/controllers/dmsf_files_controller.rb @@ -85,7 +85,8 @@ class DmsfFilesController < ApplicationController @revision = @file.last_revision @file_delete_allowed = User.current.allowed_to?(:file_delete, @project) @file_manipulation_allowed = User.current.allowed_to?(:file_manipulation, @project) - @revision_pages = Paginator.new @file.dmsf_file_revisions.visible.count, params['per_page'] ? params['per_page'].to_i : 25, params['page'] + @revision_count = @file.dmsf_file_revisions.visible.all.size + @revision_pages = Paginator.new @revision_count, params['per_page'] ? params['per_page'].to_i : 25, params['page'] respond_to do |format| format.html { diff --git a/app/models/dmsf_file.rb b/app/models/dmsf_file.rb index 424125ed..2dd09089 100644 --- a/app/models/dmsf_file.rb +++ b/app/models/dmsf_file.rb @@ -128,6 +128,16 @@ class DmsfFile < ActiveRecord::Base deleted == STATUS_DELETED end + def locked_by + if lock && lock.reverse[0] + user = lock.reverse[0].user + if user + return (user == User.current) ? l(:label_me) : user.name + end + end + '' + end + def delete(commit) if locked_for_user? && (!User.current.allowed_to?(:force_file_unlock, project)) Rails.logger.info l(:error_file_is_locked) diff --git a/app/models/dmsf_file_revision_access_query.rb b/app/models/dmsf_file_revision_access_query.rb index 515ce693..5f9fe8a0 100644 --- a/app/models/dmsf_file_revision_access_query.rb +++ b/app/models/dmsf_file_revision_access_query.rb @@ -35,7 +35,7 @@ class DmsfFileRevisionAccessQuery < Query QueryColumn.new(:last_at, frozen: true) ] - def initialize(attributes) + def initialize(attributes=nil, *args) super attributes self.sort_criteria = [] self.filters = {} @@ -73,8 +73,6 @@ class DmsfFileRevisionAccessQuery < Query @available_columns end - #alias default_columns_names available_columns - ###################################################################################################################### # New @@ -82,7 +80,7 @@ class DmsfFileRevisionAccessQuery < Query base_scope. access_grouped. joins(:user). - order('`count` DESC'). + order(Arel.sql('COUNT(*) DESC')). limit(options[:limit]). offset(options[:offset]) end diff --git a/app/models/dmsf_mailer.rb b/app/models/dmsf_mailer.rb index 5e4b2657..9e0e4a4c 100644 --- a/app/models/dmsf_mailer.rb +++ b/app/models/dmsf_mailer.rb @@ -91,8 +91,21 @@ class DmsfMailer < Mailer Rails.logger.error "Cannot attach #{email_params[:zipped_content]}, it doesn't exist." end end - mail to: email_params[:to], cc: email_params[:cc], subject: email_params[:subject], 'From' => email_params[:from], - 'Reply-To' => email_params[:reply_to] + skip_no_self_notified = false + begin + # We need to switch off no_self_notified temporarily otherwise the email won't be sent + if (author == User.current) && author.pref.no_self_notified + author.pref.no_self_notified = false + skip_no_self_notified = true + end + res = mail(to: email_params[:to], cc: email_params[:cc], subject: email_params[:subject], 'From' => email_params[:from], + 'Reply-To' => email_params[:reply_to]) + ensure + if skip_no_self_notified + author.pref.no_self_notified = true + end + end + res end def self.deliver_workflow_notification(users, workflow, revision, subject_id, text1_id, text2_id, notice = nil) diff --git a/app/models/dmsf_query.rb b/app/models/dmsf_query.rb index 963679dd..3c4dd9a4 100644 --- a/app/models/dmsf_query.rb +++ b/app/models/dmsf_query.rb @@ -41,7 +41,7 @@ class DmsfQuery < Query def initialize(attributes=nil, *args) super attributes self.sort_criteria = [] - self.filters = {} + self.filters ||= { 'title' => { operator: '~', values: ['']} } end ###################################################################################################################### @@ -110,15 +110,19 @@ class DmsfQuery < Query def statement unless @statement + @filter_dmsf_folder_id = false filters_clauses = [] filters.each_key do |field| v = values_for(field).clone next unless v and !v.empty? operator = operator_for(field) - if field == 'author' + case field + when 'author' if v.delete('me') - v.push User.current.id.to_s + v.push User.current.id.to_s end + when 'title' + next if v.include?('') end filters_clauses << '(' + sql_for_field(field, operator, v, queried_table_name, field) + ')' end @@ -128,11 +132,19 @@ class DmsfQuery < Query @statement end + def validate_query_filters + # Skip validation for empty title (default filter) + filter = filters.delete('title') + super + # Add it back + filters['title'] = filter if filter + end + ###################################################################################################################### # New def dmsf_nodes(options={}) - order_option = ['sort', group_by_sort_order, (options[:order] || sort_clause[0]), 'title'].flatten.reject(&:blank?) + order_option = ['sort', group_by_sort_order, (options[:order] || sort_clause[0])].flatten.reject(&:blank?) if order_option.size > 2 DmsfFileRevisionCustomField.visible.pluck(:id, :name).each do |id, name| order_option[1].gsub!("COALESCE(cf_#{id}.value, '')", "\"#{name}\"") @@ -157,7 +169,7 @@ class DmsfQuery < Query def dmsf_folders_scope cf_columns = +'' - if filters.any? + if statement.present? DmsfFileRevisionCustomField.visible.order(:position).pluck(:id).each do |id| cf_columns << ",(SELECT value from custom_values WHERE custom_field_id = #{id} AND customized_type = 'DmsfFolder' AND customized_id = dmsf_folders.id) AS cf_#{id}" end @@ -183,13 +195,11 @@ class DmsfQuery < Query 0 AS sort #{cf_columns}}). joins('LEFT JOIN users ON dmsf_folders.user_id = users.id'). visible(!deleted) - if deleted - scope.where dmsf_folders: { project_id: project.id, deleted: deleted } + if dmsf_folder_id + scope.where dmsf_folders: { dmsf_folder_id: dmsf_folder_id, deleted: deleted } else - if filters.any? + if statement.present? || deleted scope.where dmsf_folders: { project_id: project.id, deleted: deleted } - elsif dmsf_folder_id - scope.where dmsf_folders: { dmsf_folder_id: dmsf_folder_id, deleted: deleted } else scope.where dmsf_folders: { project_id: project.id, dmsf_folder_id: nil, deleted: deleted } end @@ -198,7 +208,7 @@ class DmsfQuery < Query def dmsf_folder_links_scope cf_columns = +'' - if filters.any? + if statement.present? DmsfFileRevisionCustomField.visible.order(:position).pluck(:id).each do |id| cf_columns << ",(SELECT value from custom_values WHERE custom_field_id = #{id} AND customized_type = 'DmsfFolder' AND customized_id = dmsf_folders.id) AS cf_#{id}" end @@ -224,13 +234,11 @@ class DmsfQuery < Query 0 AS sort #{cf_columns}}). joins('LEFT JOIN dmsf_folders ON dmsf_links.target_id = dmsf_folders.id'). joins('LEFT JOIN users ON users.id = COALESCE(dmsf_folders.user_id, dmsf_links.user_id)') - if deleted - scope.where dmsf_links: { target_type: 'DmsfFolder', project_id: project.id, deleted: deleted } + if dmsf_folder_id + scope.where dmsf_links: { target_type: 'DmsfFolder', dmsf_folder_id: dmsf_folder_id, deleted: deleted } else - if filters.any? + if statement.present? || deleted scope.where dmsf_links: { target_type: 'DmsfFolder', project_id: project.id, deleted: deleted } - elsif dmsf_folder_id - scope.where dmsf_links: { target_type: 'DmsfFolder', dmsf_folder_id: dmsf_folder_id, deleted: deleted } else scope.where dmsf_links: { target_type: 'DmsfFolder', project_id: project.id, dmsf_folder_id: nil, deleted: deleted } end @@ -239,7 +247,7 @@ class DmsfQuery < Query def dmsf_files_scope cf_columns = +'' - if filters.any? + if statement.present? DmsfFileRevisionCustomField.visible.order(:position).pluck(:id).each do |id| cf_columns << ",(SELECT value from custom_values WHERE custom_field_id = #{id} AND customized_type = 'DmsfFileRevision' AND customized_id = dmsf_file_revisions.id) AS cf_#{id}" end @@ -266,22 +274,20 @@ class DmsfQuery < Query joins(:dmsf_file_revisions). joins('LEFT JOIN users ON dmsf_file_revisions.user_id = users.id '). where('dmsf_file_revisions.created_at = (SELECT MAX(r.created_at) FROM dmsf_file_revisions r WHERE r.dmsf_file_id = dmsf_file_revisions.dmsf_file_id)') - if deleted - scope.where dmsf_files: { project_id: project.id, deleted: deleted } - else - if filters.any? - scope.where dmsf_files: { project_id: project.id, deleted: deleted } - elsif dmsf_folder_id + if dmsf_folder_id scope.where dmsf_files: { dmsf_folder_id: dmsf_folder_id, deleted: deleted } else - scope.where dmsf_files: { project_id: project.id, dmsf_folder_id: nil, deleted: deleted } + if statement.present? || deleted + scope.where dmsf_files: { project_id: project.id, deleted: deleted } + else + scope.where dmsf_files: { project_id: project.id, dmsf_folder_id: nil, deleted: deleted } + end end - end end def dmsf_file_links_scope cf_columns = +'' - if filters.any? + if statement.present? DmsfFileRevisionCustomField.visible.order(:position).pluck(:id).each do |id| cf_columns << ",(SELECT value from custom_values WHERE custom_field_id = #{id} AND customized_type = 'DmsfFileRevision' AND customized_id = dmsf_file_revisions.id) AS cf_#{id}" end @@ -309,22 +315,21 @@ class DmsfQuery < Query joins('JOIN dmsf_file_revisions ON dmsf_file_revisions.dmsf_file_id = dmsf_files.id'). joins('LEFT JOIN users ON dmsf_file_revisions.user_id = users.id '). where('dmsf_file_revisions.created_at = (SELECT MAX(r.created_at) FROM dmsf_file_revisions r WHERE r.dmsf_file_id = dmsf_file_revisions.dmsf_file_id)') - if deleted - scope.where project_id: project.id, deleted: deleted + if dmsf_folder_id + scope.where dmsf_links: { target_type: 'DmsfFile', dmsf_folder_id: dmsf_folder_id, deleted: deleted } else - if filters.any? - scope.where project_id: project.id, deleted: deleted - elsif dmsf_folder_id - scope.where dmsf_folder_id: dmsf_folder_id, deleted: deleted + if statement.present? || deleted + scope.where dmsf_links: { target_type: 'DmsfFile', project_id: project.id, deleted: deleted } else - scope.where project_id: project.id, dmsf_folder_id: nil, deleted: deleted + scope.where dmsf_links: { target_type: 'DmsfFile', project_id: project.id, dmsf_folder_id: nil, deleted: deleted } end end + end def dmsf_url_links_scope cf_columns = +'' - if filters.any? + if statement.present? DmsfFileRevisionCustomField.visible.order(:position).pluck(:id).each do |id| cf_columns << ",NULL AS cf_#{id}" end @@ -349,17 +354,16 @@ class DmsfQuery < Query dmsf_links.deleted AS deleted, 1 AS sort #{cf_columns}}). joins('LEFT JOIN users ON dmsf_links.user_id = users.id ') - if deleted - scope.where target_type: 'DmsfUrl', project_id: project.id, deleted: deleted + if dmsf_folder_id + scope.where dmsf_links: { target_type: 'DmsfUrl', dmsf_folder_id: dmsf_folder_id, deleted: deleted } else - if filters.any? - scope.where target_type: 'DmsfUrl', project_id: project.id, deleted: deleted - elsif dmsf_folder_id - scope.where target_type: 'DmsfUrl', dmsf_folder_id: dmsf_folder_id, deleted: deleted + if statement.present? || deleted + scope.where dmsf_links: { target_type: 'DmsfUrl', project_id: project.id, deleted: deleted } else - scope.where target_type: 'DmsfUrl', project_id: project.id, dmsf_folder_id: nil, deleted: deleted + scope.where dmsf_links: { target_type: 'DmsfUrl', project_id: project.id, dmsf_folder_id: nil, deleted: deleted } end end + end end diff --git a/app/models/dmsf_upload.rb b/app/models/dmsf_upload.rb index 50237f6e..9b851ffb 100644 --- a/app/models/dmsf_upload.rb +++ b/app/models/dmsf_upload.rb @@ -41,7 +41,7 @@ class DmsfUpload end def self.create_from_uploaded_attachment(project, folder, uploaded_file) - a = Attachment.find_by_token(uploaded_file[:token]) + a = Attachment.find_by_token(uploaded_file[:token]) if uploaded_file[:token].present? if a uploaded = { disk_filename: DmsfHelper.temp_filename(a.filename), @@ -50,7 +50,7 @@ class DmsfUpload comment: uploaded_file[:description], tempfile_path: a.diskfile } - DmsfUpload.new(project, folder, uploaded) + DmsfUpload.new project, folder, uploaded else Rails.logger.error "An attachment not found by its token: #{uploaded_file[:token]}" nil diff --git a/app/views/dmsf/show.html.erb b/app/views/dmsf/show.html.erb index bf6ec323..a2c32198 100644 --- a/app/views/dmsf/show.html.erb +++ b/app/views/dmsf/show.html.erb @@ -72,10 +72,21 @@ <%= form_tag(dmsf_folder_path(id: @project, folder_id: @folder), method: :get, id: 'query_form', class: 'dmsf-query-form') do %> - <%= render partial: 'queries/query_form' %> + <%= hidden_field_tag('folder_id', @folder.id) if @folder %> + <% if defined?(EasyExtensions) %> + <%= render partial: 'dmsf_queries/query_form' %> + <% else %> + <%= render partial: 'queries/query_form' %> + <% end %> +<% end %> +<% if @query.valid? %> + <% if @dmsf_count == 0 %> +
<%= l(:label_no_data) %>
+ <% else %> + <%= render partial: 'query_list', locals: { query: @query, dmsf_pages: @dmsf_pages } %> + <%= pagination_links_full @dmsf_pages, @dmsf_count %> + <% end %> <% end %> -<%= render partial: 'query_list', locals: { query: @query, dmsf_pages: @dmsf_pages } %> -<%= pagination_links_full @dmsf_pages, @dmsf_count %> <%= context_menu %> diff --git a/app/views/dmsf_context_menus/_file.html.erb b/app/views/dmsf_context_menus/_file.html.erb index f94169d8..1f0a738e 100644 --- a/app/views/dmsf_context_menus/_file.html.erb +++ b/app/views/dmsf_context_menus/_file.html.erb @@ -22,7 +22,7 @@