#823 context menu

This commit is contained in:
Karel Picman 2018-02-16 14:27:42 +01:00
parent 7f728e7d14
commit b2ade58d73
16 changed files with 306 additions and 108 deletions

View File

@ -4,9 +4,11 @@ Changelog for Redmine DMSF
1.6.1 *2018-??-??*
------------------
Javascript on pages is loaded asynchronously.
Obsolete Dav4Rack gem replaced with an up to date fork by Planio (Consequently WebDAV caching has been removed, sorry...).
Project members can be chosen as recipients when sending documents by email.
Javascript on pages is loaded asynchronously
Obsolete Dav4Rack gem replaced with an up to date fork by Planio (Consequently WebDAV caching has been removed, sorry...)
Project members can be chosen as recipients when sending documents by email
Responsive view
Direct editing of document in MS Office
1.6.0 *2017-09-12*
------------------

View File

@ -50,7 +50,8 @@ def init
:dmsf_context_menus => [:dmsf]}
pmap.permission :file_delete,
{:dmsf => [:trash, :delete_entries],
:dmsf_files => [:delete]}
:dmsf_files => [:delete],
:dmsf_trash_context_menus => [:trash]}
pmap.permission :force_file_unlock, {}
pmap.permission :file_approval,
{:dmsf_workflows => [:action, :new_action, :autocomplete_for_user, :start, :assign, :assignment]}

View File

@ -22,7 +22,35 @@ class DmsfContextMenusController < ApplicationController
helper :context_menus
before_action :find_project
before_action :find_folder
before_action :find_file, :except => [:trash]
def dmsf
@disabled = params[:ids].blank?
render :layout => false
rescue ActiveRecord::RecordNotFound
render_404
end
def trash
render :layout => false
rescue ActiveRecord::RecordNotFound
render_404
end
private
def find_folder
@folder = DmsfFolder.find params[:folder_id] if params[:folder_id].present?
rescue DmsfAccessError
render_403
rescue ActiveRecord::RecordNotFound
render_404
end
def find_file
if params[:ids].present?
selected_files = params[:ids].select{ |x| x =~ /file-\d+/ }.map{ |x| $1.to_i if x =~ /file-(\d+)/ }
selected_file_links = params[:ids].select{ |x| x =~ /file-link-\d+/ }.map{ |x| $1.to_i if x =~ /file-link-(\d+)/ }
selected_file_links.each do |id|
@ -32,9 +60,7 @@ class DmsfContextMenusController < ApplicationController
if (selected_files.size == 1) && (params[:ids].size == 1)
@file = DmsfFile.find selected_files[0]
end
render :layout => false
rescue ActiveRecord::RecordNotFound
render_404
end
end
end

View File

@ -100,11 +100,19 @@ class DmsfController < ApplicationController
def entries_operation
# Download/Email
if params[:ids].present?
selected_folders = params[:ids].select{ |x| x =~ /folder-\d+/ }.map{ |x| $1.to_i if x =~ /folder-(\d+)/ }
selected_files = params[:ids].select{ |x| x =~ /file-\d+/ }.map{ |x| $1.to_i if x =~ /file-(\d+)/ }
selected_dir_links = params[:ids].select{ |x| x =~ /folder-link-\d+/ }.map{ |x| $1.to_i if x =~ /folder-link-(\d+)/ }
selected_file_links = params[:ids].select{ |x| x =~ /file-link-\d+/ }.map{ |x| $1.to_i if x =~ /file-link-(\d+)/ }
selected_url_links = params[:ids].select{ |x| x =~ /url-link-\d+/ }.map{ |x| $1.to_i if x =~ /url-link-(\d+)/ }
else
selected_folders = []
selected_files = []
selected_dir_links = []
selected_file_links = []
selected_url_links = []
end
if selected_folders.blank? && selected_files.blank? &&
selected_dir_links.blank? && selected_file_links.blank? &&
@ -143,6 +151,7 @@ class DmsfController < ApplicationController
redirect_to :back
else
download_entries(selected_folders, selected_files)
redirect_to :back
end
rescue FileNotFound
render_404
@ -359,7 +368,6 @@ class DmsfController < ApplicationController
end
def email_entries(selected_folders, selected_files)
begin
zip = DmsfZip.new
zip_entries(zip, selected_folders, selected_files)
@ -400,7 +408,6 @@ class DmsfController < ApplicationController
ensure
zip.close if zip
end
end
def download_entries(selected_folders, selected_files)
zip = DmsfZip.new

View File

@ -36,12 +36,16 @@ class DmsfFileRevision < ActiveRecord::Base
PROTOCOLS = {
'application/msword' => 'ms-word',
'application/excel' => 'ms-excel',
'application/vnd.ms-excel' => 'ms-excel',
'application/vnd.ms-powerpoint' => 'ms-powerpoint',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'ms-word',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'ms-excel',
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'ms-powerpoint',
'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ms-powerpoint'
'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ms-powerpoint',
'application/vnd.oasis.opendocument.spreadsheet' => 'ms-excel',
'application/vnd.oasis.opendocument.text' => 'ms-word',
'application/vnd.oasis.opendocument.presentation' => 'ms-powerpoint',
}.freeze
scope :visible, -> { where(:deleted => STATUS_ACTIVE) }

View File

@ -24,7 +24,9 @@
<thead>
<tr>
<th class="dmsf_checkbox dmsf_th">
<% if !@system_folder %>
<input id="check_all_entries" title="<%= l(:title_check_uncheck_all_for_zip_download_or_email) %>" type="checkbox" />
<% end %>
</th>
<% if DmsfFolder.is_column_on?('id') %>
<th class="dmsf_th">#</th>

View File

@ -24,8 +24,10 @@
<thead>
<tr>
<th class="dmsf_checkbox dmsf_th">
<% if !@system_folder %>
<%= check_box_tag 'check_all', '', false, :class => 'toggle-selection',
:title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
<% end %>
</th>
<% if DmsfFolder.is_column_on?('id') %>
<th class="dmsf_th">#</th>

View File

@ -20,7 +20,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
%>
<td class="dmsf_checkbox"></td>
<td class="dmsf_checkbox"><%= check_box_tag('ids[]', "#{name}-#{id}", false) %></td>
<% if DmsfFolder.is_column_on?('id') %>
<td class="id"></td>
<% end %>

View File

@ -86,6 +86,7 @@
<%= form_tag(entries_operations_dmsf_path(:id => @project, :folder_id => @folder), :method => :post,
:class => 'dmsf_entries', :id => 'entries_form', :data => {:cm_url => dmsf_context_menu_path}) do %>
<%= hidden_field_tag('action') %>
<% if !@system_folder %>
<div id="dmsf_buttons" class="dmsf_controls" style="float: left">
<%= submit_tag(l(:button_download), :title => l(:title_download_checked), :name => 'download_entries',
:class => 'toggle-selection') if @file_view_allowed %>
@ -96,6 +97,7 @@
:class => 'toggle-selection') if @file_delete_allowed %>
<% end %>
</div>
<% end %>
<% unless @system_folder %>
<% values = @folder ? @folder.custom_field_values : DmsfFolder.new.custom_field_values %>
<% unless values.empty? %>

View File

@ -33,7 +33,7 @@
<%= error_messages_for('dmsf_workflow') %>
<%= form_tag(entries_operations_dmsf_path(:id => @project, :folder_id => @folder), :method => :post,
:class => 'dmsf_entries', :id => 'entries_form', :data => {:cm_url => dmsf_context_menu_path}) do %>
:class => 'dmsf_entries', :id => 'entries_form', :data => {:cm_url => dmsf_trash_context_menu_path}) do %>
<%= hidden_field_tag('action') %>
<div class="dmsf_controls" style="float: left">
<% if @file_manipulation_allowed && @folder_manipulation_allowed %>
@ -99,7 +99,7 @@
:subfolder => subfolder,
:link => nil,
:id => subfolder.id,
:name => 'subfolders[]',
:name => 'folder',
:title => subfolder.title }) %>
</tr>
<% end %>
@ -112,7 +112,7 @@
:subfolder => link.target_folder,
:link => link,
:id => link.id,
:name => 'dir_links[]',
:name => 'folder-link',
:title => link.name }) %>
</tr>
<% end %>
@ -128,7 +128,7 @@
:file => file,
:link => nil,
:id => file.id,
:name => 'files[]',
:name => 'file',
:title => file.title }) %>
</tr>
<% end %>
@ -148,7 +148,7 @@
:file => link.target_file,
:link => link,
:id => link.id,
:name => 'file_links[]',
:name => 'file-link',
:title => link.name }) %>
</tr>
<% end %>

View File

@ -22,7 +22,23 @@
<ul>
<li>
<% if @file && @file.last_revision && @file.last_revision.protocol %>
<%= context_menu_link l(:button_download), entries_operations_dmsf_path(:id => @project, :folder_id => @folder,
:ids => params[:ids], :download_entries => true), :method => :post, :class => 'icon-download',
:disabled => @disabled %>
</li>
<li>
<%= context_menu_link l(:field_mail), entries_operations_dmsf_path(:id => @project, :folder_id => @folder,
:ids => params[:ids], :email_entries => true), :method => :post, :class => 'icon-email',
:disabled => @disabled %>
</li>
<li>
<%= context_menu_link l(:button_delete), entries_operations_dmsf_path(:id => @project, :folder_id => @folder,
:ids => params[:ids], :destroy_entries => true), :method => :post, :class => 'icon-del',
:disabled => @disabled %>
</li>
<% if @file %>
<li>
<% if @file.last_revision && @file.last_revision.protocol %>
<% url = "#{@file.last_revision.protocol}:ofe|u|#{Setting.protocol.strip}://#{Setting.host_name.strip}/dmsf/webdav/#{Addressable::URI.escape(@file.project.identifier)}/" %>
<% if @file.dmsf_folder %>
<% url << @file.dmsf_folder.dmsf_path_str %>
@ -31,4 +47,5 @@
<% end %>
<%= context_menu_link l(:button_edit), url, :class => 'icon-edit', :disabled => url.blank? %>
</li>
<% end %>
</ul>

View File

@ -0,0 +1,32 @@
<%
# encoding: utf-8
#
# Redmine plugin for Document Management System "Features"
#
# Copyright © 2011-18 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.
%>
<ul>
<li>
<%= context_menu_link l(:button_delete), entries_operations_dmsf_path(:id => @project, :folder_id => @folder,
:ids => params[:ids], :destroy_entries => true), :method => :post, :class => 'icon-del' %>
</li>
<li>
<%= context_menu_link l(:title_restore), entries_operations_dmsf_path(:id => @project, :folder_id => @folder,
:ids => params[:ids], :restore_entries => true), :method => :post, :class => 'icon-cancel' %>
</li>
</ul>

View File

@ -55,6 +55,7 @@ if Redmine::Plugin.installed? :redmine_dmsf
# dmsf_context_menu_controller
match '/projects/:id/dmsf/context_menu', :to => 'dmsf_context_menus#dmsf', :as => 'dmsf_context_menu', :via => [:get, :post]
match '/projects/:id/dmsf/trash/context_menu', :to => 'dmsf_context_menus#trash', :as => 'dmsf_trash_context_menu', :via => [:get, :post]
#
# dmsf_state controller

View File

@ -0,0 +1,72 @@
# encoding: utf-8
#
# Redmine plugin for Document Management System "Features"
#
# Copyright © 2011-18 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.
require File.expand_path('../../test_helper', __FILE__)
class DmsfContextMenusControllerTest < RedmineDmsf::Test::TestCase
include Redmine::I18n
fixtures :users, :email_addresses, :projects, :members, :roles, :member_roles, :dmsf_folders,
:dmsf_files, :dmsf_file_revisions
def setup
@user_member = User.find_by_id 2 # John Smith - manager
@project1 = Project.find_by_id 1
assert_not_nil @project1
@project1.enable_module! :dmsf
@file1 = DmsfFile.find_by_id 1
@folder1 = DmsfFolder.find_by_id 1
User.current = nil
@request.session[:user_id] = @user_member.id
end
def test_truth
assert_kind_of User, @user_member
assert_kind_of Project, @project1
assert_kind_of DmsfFile, @file1
assert_kind_of DmsfFolder, @folder1
end
def test_dmsf
get :dmsf, :id => @project1.id, :ids => ["file-#{@file1.id}"]
assert_response :success
assert_select 'a.icon-edit', :text => l(:button_edit)
assert_select 'a.icon-del', :text => l(:button_delete)
assert_select 'a.icon-download', :text => l(:button_download)
assert_select 'a.icon-email', :text => l(:field_mail)
end
def test_dmsf_no_edit
get :dmsf, :id => @project1.id, :ids => ["folder-#{@folder1.id}"]
assert_response :success
assert_select 'a.icon-edit', :text => l(:button_edit), :count => 0
assert_select 'a.icon-del', :text => l(:button_delete)
assert_select 'a.icon-download', :text => l(:button_download)
assert_select 'a.icon-email', :text => l(:field_mail)
end
def test_trash
get :trash, :id => @project1.id, :ids => ["file-#{@file1.id}"]
assert_response :success
assert_select 'a.icon-cancel', :text => l(:title_restore)
assert_select 'a.icon-del', :text => l(:button_delete)
end
end

View File

@ -140,8 +140,7 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase
def test_delete_restore_entries_forbidden
# Missing permissions
get :entries_operation, :id => @project, :delete_entries => 'Delete',
:subfolders => [@folder1.id.to_s], :files => [@file1.id.to_s],
:dir_links => [@folder_link1.id.to_s], :file_links => [@file_link2.id.to_s]
:ids => ["folder-#{@folder1.id}", "file-#{@file1.id}", "folder-link-#{@folder_link1.id}", "file-link-#{@file_link2.id}"]
assert_response :forbidden
end
@ -150,8 +149,7 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase
@request.env['HTTP_REFERER'] = dmsf_folder_path(:id => @project.id)
@role.add_permission! :view_dmsf_files
get :entries_operation, :id => @project, :delete_entries => 'Delete',
:subfolders => [@folder1.id.to_s], :files => [@file1.id.to_s],
:dir_links => [@folder_link1.id.to_s], :file_links => [@file_link2.id.to_s]
:ids => ["folder-#{@folder1.id}", "file-#{@file1.id}", "folder-link-#{@folder_link1.id}", "file-link-#{@file_link2.id}"]
assert_response :redirect
assert_equal flash[:error].to_s, l(:error_folder_is_not_empty)
end
@ -162,8 +160,7 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase
@role.add_permission! :view_dmsf_files
flash[:error] = nil
get :entries_operation, :id => @project, :delete_entries => 'Delete',
:subfolders => [], :files => [@file1.id.to_s],
:dir_links => [], :file_links => [@file_link2.id.to_s]
:ids => ["file-#{@file1.id}", "file-link-#{@file_link2.id}"]
assert_response :redirect
assert_nil flash[:error]
end
@ -173,8 +170,7 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase
@role.add_permission! :view_dmsf_files
@request.env['HTTP_REFERER'] = trash_dmsf_path(:id => @project.id)
get :entries_operation, :id => @project, :restore_entries => 'Restore',
:subfolders => [], :files => [@file1.id.to_s],
:dir_links => [], :file_links => [@file_link2.id.to_s]
:ids => ["file-#{@file1.id}", "file-link-#{@file_link2.id}"]
assert_response :redirect
assert_nil flash[:error]
end
@ -229,7 +225,7 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase
Setting.plugin_redmine_dmsf['dmsf_documents_email_from'] = 'karel.picman@kontron.com'
Setting.plugin_redmine_dmsf['dmsf_storage_directory'] = File.expand_path '../../fixtures/files', __FILE__
@role.add_permission! :view_dmsf_files
get :entries_operation, :id => @project, :email_entries => 'Email', :files => [@file1.id]
get :entries_operation, :id => @project, :email_entries => 'Email', :ids => ["file-#{@file1.id}"]
assert_response :success
assert_select "input:match('value', ?)", Setting.plugin_redmine_dmsf['dmsf_documents_email_from']
end
@ -238,7 +234,7 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase
Setting.plugin_redmine_dmsf['dmsf_documents_email_reply_to'] = 'karel.picman@kontron.com'
Setting.plugin_redmine_dmsf['dmsf_storage_directory'] = File.expand_path '../../fixtures/files', __FILE__
@role.add_permission! :view_dmsf_files
get :entries_operation, :id => @project, :email_entries => 'Email', :files => [@file1.id]
get :entries_operation, :id => @project, :email_entries => 'Email', :ids => ["file-#{@file1.id}"]
assert_response :success
assert_select "input:match('value', ?)", Setting.plugin_redmine_dmsf['dmsf_documents_email_reply_to']
end
@ -247,7 +243,7 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase
Setting.plugin_redmine_dmsf['dmsf_documents_email_links_only'] = '1'
Setting.plugin_redmine_dmsf['dmsf_storage_directory'] = File.expand_path '../../fixtures/files', __FILE__
@role.add_permission! :view_dmsf_files
get :entries_operation, :id => @project, :email_entries => 'Email', :files => [@file1.id]
get :entries_operation, :id => @project, :email_entries => 'Email', :ids => ["file-#{@file1.id}"]
assert_response :success
assert_select "input:match('value', ?)", Setting.plugin_redmine_dmsf['dmsf_documents_email_links_only']
end

View File

@ -182,11 +182,45 @@ class DmsfFileRevisionTest < RedmineDmsf::Test::UnitTest
assert_equal -(' '.ord), @revision1.minor_version
end
def description_max_length
@revision1.description = 2.megabytes * 'a'
def test_description_max_length
@revision1.description = 'a' * 2.megabytes
assert !@revision1.save
@revision1.description = 1.megabyte * 'a'
@revision1.description = 'a' * 1.megabyte
assert @revision1.save
end
def test_protocol_txt
assert !@revision1.protocol
end
def test_protocol_doc
@revision1.mime_type = Redmine::MimeType.of('test.doc')
assert_equal 'ms-word', @revision1.protocol
end
def test_protocol_docx
@revision1.mime_type = Redmine::MimeType.of('test.docx')
assert_equal 'ms-word', @revision1.protocol
end
def test_protocol_odt
@revision1.mime_type = Redmine::MimeType.of('test.odt')
assert_equal 'ms-word', @revision1.protocol
end
def test_protocol_xls
@revision1.mime_type = Redmine::MimeType.of('test.xls')
assert_equal 'ms-excel', @revision1.protocol
end
def test_protocol_xlsx
@revision1.mime_type = Redmine::MimeType.of('test.xlsx')
assert_equal 'ms-excel', @revision1.protocol
end
def test_protocol_ods
@revision1.mime_type = Redmine::MimeType.of('test.ods')
assert_equal 'ms-excel', @revision1.protocol
end
end