Merge branch 'master' into webdav_project_tree

This commit is contained in:
karel.picman@lbcfree.net 2020-07-10 14:54:28 +02:00
commit 2c1fcefdc9
35 changed files with 240 additions and 98 deletions

View File

@ -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

View File

@ -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*
------------------

View File

@ -1,7 +1,7 @@
Redmine DMSF Plugin
===================
The current version of Redmine DMSF is **2.4.4 devel** [![Build Status](https://api.travis-ci.org/danmunn/redmine_dmsf.png)](https://travis-ci.org/danmunn/redmine_dmsf)
The current version of Redmine DMSF is **2.4.4** [![Build Status](https://api.travis-ci.org/danmunn/redmine_dmsf.png)](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.

View File

@ -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

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -72,10 +72,21 @@
</div>
<%= 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 %>
<p class="nodata"><%= l(:label_no_data) %></p>
<% else %>
<%= render partial: 'query_list', locals: { query: @query, dmsf_pages: @dmsf_pages } %>
<span class="pagination"><%= pagination_links_full @dmsf_pages, @dmsf_count %></span>
<% end %>
<% end %>
<%= render partial: 'query_list', locals: { query: @query, dmsf_pages: @dmsf_pages } %>
<span class="pagination"><%= pagination_links_full @dmsf_pages, @dmsf_count %></span>
<%= context_menu %>

View File

@ -22,7 +22,7 @@
<li>
<%= context_menu_link l(:button_edit), dmsf_file_path(id: dmsf_file), class: 'icon icon-edit',
disabled: !allowed || locked %>
disabled: !allowed || (locked && !unlockable) %>
</li>
<li>
<%= link_to "#{l(:button_copy)}/#{l(:button_move)}", copy_file_path(id: dmsf_file),
@ -40,6 +40,7 @@
<li>
<% if locked %>
<%= context_menu_link l(:button_unlock), unlock_dmsf_files_path(id: dmsf_file), class: 'icon icon-unlock',
title: l(:title_locked_by_user, user: dmsf_file.locked_by),
disabled: !allowed || !unlockable %>
<% else %>
<%= context_menu_link l(:button_lock), lock_dmsf_files_path(id: dmsf_file), class: 'icon icon-lock',

View File

@ -44,4 +44,3 @@
</tbody>
</table>
</div>
<span class="pagination"><%= pagination_links_full revision_access_pages, revision_access_count %></span>

View File

@ -158,20 +158,14 @@
<% if @file_manipulation_allowed %>
<% revision_access_query = DmsfFileRevisionAccessQuery.new %>
<% revision_access_query.revision_id = revision.id %>
<% revision_access_count = revision_access_query.access_count %>
<% revision_access_pages = Redmine::Pagination::Paginator.new revision_access_count,
Setting.per_page_options_array.first || 25, params['page'] %>
<%= render partial: 'revision_access', locals: { revision: revision, query: revision_access_query,
revision_access_count: revision_access_count,
revision_access_pages: revision_access_pages } %>
<%= render partial: 'revision_access', locals: { revision: revision, query: revision_access_query } %>
<% end %>
</div>
</div>
</div>
<br/>
<% end %>
<span class="pagination"><%= pagination_links_full @revision_pages, @file.dmsf_file_revisions.visible.count %></span>
<span class="pagination"><%= pagination_links_full @revision_pages, @revision_count %></span>
<%= late_javascript_tag do %>
$('a.delete-revision').click(function(event) {

View File

@ -0,0 +1,89 @@
<%= hidden_field_tag 'set_filter', '1' %>
<%= hidden_field_tag 'type', @query.type, :disabled => true, :id => 'query_type' %>
<%= query_hidden_sort_tag(@query) %>
<div id="query_form_with_buttons" class="hide-when-print">
<div id="query_form_content">
<fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>">
<legend onclick="toggleFieldset(this);" class="icon icon-<%= @query.new_record? ? "expended" : "collapsed" %>"><%= l(:label_filter_plural) %></legend>
<div style="<%= @query.new_record? ? "" : "display: none;" %>">
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
</div>
</fieldset>
<% if @query.available_columns.any? %>
<fieldset id="options" class="collapsible collapsed">
<legend onclick="toggleFieldset(this);" class="icon icon-collapsed"><%= l(:label_options) %></legend>
<div class="hidden">
<% if @query.available_display_types.size > 1 %>
<div>
<span class="field"><label for='display_type'><%= l(:label_display_type) %></label></span>
<%= available_display_types_tags(@query) %>
</div>
<% end %>
<table id="list-definition" class="<%= 'hidden' if (@query.display_type != 'list') %>">
<% if @query.available_columns.any? %>
<tr>
<td class="field"><%= l(:field_column_names) %></td>
<td><%= render_query_columns_selection(@query) %></td>
</tr>
<% end %>
<% if @query.groupable_columns.any? %>
<tr>
<td class="field"><label for='group_by'><%= l(:field_group_by) %></label></td>
<td><%= group_by_column_select_tag(@query) %></td>
</tr>
<% end %>
<% if @query.available_block_columns.any? %>
<tr>
<td class="field"><%= l(:button_show) %></td>
<td><%= available_block_columns_tags(@query) %></td>
</tr>
<% end %>
<% if @query.available_totalable_columns.any? %>
<tr>
<td><%= l(:label_total_plural) %></td>
<td><%= available_totalable_columns_tags(@query) %></td>
</tr>
<% end %>
</table>
</div>
</fieldset>
<% end %>
</div>
<p class="buttons">
<%= link_to_function l(:button_apply), '$("#query_form").submit()', :class => 'icon icon-checked' %>
<%= link_to l(:button_clear), { :set_filter => 1, :sort => '', :project_id => @project }, :class => 'icon icon-reload' %>
<% if @query.new_record? %>
<% if User.current.allowed_to?(:save_queries, @project, :global => true) %>
<%= link_to_function l(:button_save),
"$('#query_type').prop('disabled',false);$('#query_form').attr('action', '#{ @project ? new_project_query_path(@project) : new_query_path }').submit()",
:class => 'icon icon-save' %>
<% end %>
<% else %>
<% if @query.editable_by?(User.current) %>
<%= link_to l(:button_edit), edit_query_path(@query), :class => 'icon icon-edit' %>
<%= delete_link query_path(@query) %>
<% end %>
<% end %>
</p>
</div>
<%= error_messages_for @query %>
<%# DMS modification do %>
<%#= javascript_tag do %>
<%= late_javascript_tag do %>
<%# end %>
$(function ($) {
$('input[name=display_type]').change(function (e) {
if ($("#display_type_list").is(':checked')) {
$('table#list-definition').show();
} else {
$('table#list-definition').hide();
}
})
});
<% end %>

View File

@ -65,3 +65,13 @@ table.dmsf a.sort.asc.icon.icon-sorted-desc {
#query_form.dmsf-query-form .collapsed#filters {
opacity: 1;
}
#query_form.dmsf-query-form .add-filter {
float: right;
text-align: right;
vertical-align: top;
}
#query_form.dmsf-query-form td.values span:first-child {
display: inline !important;
}

View File

@ -80,7 +80,6 @@ cs:
title_notifications_not_active_activate: "Notifikace nejsou aktivní: Aktivovat"
title_title_version_version_download: "%{title} verze %{version} stáhnout"
title_locked_by_user: "Zamčeno uživatelem %{user}"
title_locked_by_you: Zamčeno Vámi
title_waiting_for_approval: Čeká na schválení
title_approved: Schváleno
title_unlock_file: Odemknout a umožnit změny ostatním uživatelům

View File

@ -80,7 +80,6 @@ de:
title_notifications_not_active_activate: "Benachrichtigungen sind nicht aktiv: Einschalten"
title_title_version_version_download: "%{title} Version %{version} Download"
title_locked_by_user: "Gesperrt von %{user}"
title_locked_by_you: Gesperrt von dir
title_waiting_for_approval: Warte auf Zustimmung
title_approved: Zugestimmt
title_unlock_file: Hebe Sperre auf um Änderungen anderer Nutzer zu ermöglichen

View File

@ -80,7 +80,6 @@ en:
title_notifications_not_active_activate: "Notifications not active: Activate"
title_title_version_version_download: "%{title} version %{version} download"
title_locked_by_user: "Locked by %{user}"
title_locked_by_you: Locked by you
title_waiting_for_approval: Waiting for Approval
title_approved: Approved
title_unlock_file: Unlock to allow changes for other members

View File

@ -80,7 +80,6 @@ es:
title_notifications_not_active_activate: "Notificacciones No Activas: Activadas"
title_title_version_version_download: "%{title} version %{version} descargar"
title_locked_by_user: "bloqueado por %{user}"
title_locked_by_you: Bloqueado por ti
title_waiting_for_approval: Esperando Aprobación
title_approved: Aprobado
title_unlock_file: Desbloquear para que otros miembros puedan editarlo

View File

@ -80,7 +80,6 @@ fr:
title_notifications_not_active_activate: "Notifications désactivées : cliquer pour activer"
title_title_version_version_download: "Télécharger la version %{version} de %{title}"
title_locked_by_user: "Verrouillé par %{user}"
title_locked_by_you: Verrouillé par vous-même
title_waiting_for_approval: Attente de validation
title_approved: Validé
title_unlock_file: Déverrouiller afin de permettre la modification par les membres du projet

View File

@ -80,7 +80,6 @@ hu:
title_notifications_not_active_activate: "Értesítések nincsnek bekapcsolva: Bekapcsolás"
title_title_version_version_download: "%{title} verzió %{version} letöltés"
title_locked_by_user: "Zárolva %{user}"
title_locked_by_you: Ön zárolta
title_waiting_for_approval: Elfogadásra vár
title_approved: Elfogadva
title_unlock_file: Zárolás megszüntetése, hogy más felhasználók is változtatni tudjanak

View File

@ -80,7 +80,6 @@ it: # Italian strings thx 2 Matteo Arceci!
title_notifications_not_active_activate: "Notifiche disattivate: Attiva"
title_title_version_version_download: "%{title} versione %{version} download"
title_locked_by_user: "Bloccato da %{user}"
title_locked_by_you: Bloccato da te
title_waiting_for_approval: In attesa di Approvazione
title_approved: Approvato
title_unlock_file: Sblocca per consentire modifiche degli altri membri

View File

@ -80,7 +80,6 @@ ja:
title_notifications_not_active_activate: "通知は無効です: 有効にする"
title_title_version_version_download: "%{title} のバージョン %{version} をダウンロードする"
title_locked_by_user: "%{user} によってロックされています"
title_locked_by_you: あなたがロックしています
title_waiting_for_approval: 承認待ち
title_approved: 承認済み
title_unlock_file: ロック解除して他のメンバーの変更を許可する

View File

@ -80,7 +80,6 @@ ko:
title_notifications_not_active_activate: "알림 비활성화: 활성화"
title_title_version_version_download: "%{title} 버전 %{version} 다운로드"
title_locked_by_user: "%{user} 에 의해 잠겨짐"
title_locked_by_you: 사용자에 의해 잠겨짐
title_waiting_for_approval: 승인을 기다리는 중
title_approved: 승인됨
title_unlock_file: 다른 구성원들의 변경 허가 잠금 해제

View File

@ -80,7 +80,6 @@ nl:
title_notifications_not_active_activate: "Notificaties niet actief: Activeren"
title_title_version_version_download: "%{title} versie %{version} downloaden"
title_locked_by_user: "Vergrendeld door %{user}"
title_locked_by_you: Vergrendeld door u
title_waiting_for_approval: Wacht op goedkeuring
title_approved: Goedgekeurd
title_unlock_file: Ontgrendel om wijzigingen door andere leden toe te staan

View File

@ -80,7 +80,6 @@ pl:
title_notifications_not_active_activate: "Powiadomienia wyłączone: Aktywuj"
title_title_version_version_download: "pobierz %{title} wersja %{version}"
title_locked_by_user: "Zablokowany przez %{user}"
title_locked_by_you: Zablokowany przez Ciebie
title_waiting_for_approval: Oczekiwanie na akceptację
title_approved: Zaakceptowany
title_unlock_file: Odblokuj aby umożliwić wprowadzanie zmian innym użytkownikom

View File

@ -80,7 +80,6 @@ pt-BR:
title_notifications_not_active_activate: "Notificações não ativas: Ativar"
title_title_version_version_download: "%{title} revisão %{version} download"
title_locked_by_user: "Bloqueado por %{user}"
title_locked_by_you: Bloqueado por você
title_waiting_for_approval: Aguardando aprovação
title_approved: Aprovado
title_unlock_file: Clique aqui para permitir alterações por outro usuário

View File

@ -80,7 +80,6 @@ ru:
title_notifications_not_active_activate: "Уведомления не включены: Включить"
title_title_version_version_download: "Скачать %{title} редакции %{version}"
title_locked_by_user: "Заблокировано пользователем %{user}"
title_locked_by_you: Заблокировано Вами
title_waiting_for_approval: Ожидается утверждение
title_approved: Утверждено
title_unlock_file: Разблокируйте файл, чтобы разрешить его изменение другими участниками

View File

@ -80,7 +80,6 @@ sl:
title_notifications_not_active_activate: "Obveščanje ni aktivno: Aktiviraj"
title_title_version_version_download: "%{title} verzija %{version} prenesi dol"
title_locked_by_user: "Zaklenil %{user}"
title_locked_by_you: Zaklenili ste
title_waiting_for_approval: V postopku odobritve
title_approved: Odobreno
title_unlock_file: Odkleni drugim članom za posodabljanje

View File

@ -80,7 +80,6 @@ zh-TW:
title_notifications_not_active_activate: "通知關閉中: 點擊啟用通知"
title_title_version_version_download: " 版本: %{version} 檔案: %{title} 下載"
title_locked_by_user: "被使用者%{user},鎖定了。"
title_locked_by_you: 被您鎖定了。
title_waiting_for_approval: 等待批准
title_approved: 已經被批准
title_unlock_file: 解除鎖定。允許其它使用者修改。

View File

@ -80,7 +80,6 @@ zh:
title_notifications_not_active_activate: "通知无效:点击激活通知"
title_title_version_version_download: " 下载‘%{title}’版本‘%{version}"
title_locked_by_user: "%{user}锁定"
title_locked_by_you: 您锁定
title_waiting_for_approval: 待批准
title_approved: 已批准
title_unlock_file: 解除锁定允许其他成员修改

View File

@ -35,7 +35,7 @@ Redmine::Plugin.register :redmine_dmsf do
description 'Document Management System Features'
version '2.4.4 devel'
requires_redmine version_or_higher: '4.1.0'
requires_redmine version_or_higher: '4.0.0'
settings partial: 'settings/dmsf_settings',
default: {

View File

@ -219,10 +219,13 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase
@role.add_permission! :view_dmsf_files
@role.add_permission! :view_dmsf_folders
@role.add_permission! :file_manipulation
@role.add_permission! :folder_manipulation
get :show, params: { id: @project.id }
assert_response :success
# New file link
assert_select 'a[href$=?]', '/dmsf/upload/multi_upload'
# New folder link
assert_select 'a[href$=?]', '/dmsf/new'
# Filters
assert_select 'fieldset#filters'
# Options

View File

@ -291,4 +291,11 @@ class DmsfFileTest < RedmineDmsf::Test::UnitTest
assert @file7.assigned?(@jsmith)
end
def test_locked_by
# Locked file
assert_equal @jsmith.name, @file2.locked_by
# Unlocked file
assert_equal '', @file1.locked_by
end
end