Access rights to read (maybe write) individual folders #639

This commit is contained in:
Karel Picman 2017-04-07 15:44:33 +02:00
parent 68ff8c0886
commit 41974657e1
39 changed files with 648 additions and 158 deletions

View File

@ -28,12 +28,19 @@ class DmsfController < ApplicationController
before_filter :find_folder, :except => [:new, :create, :edit_root, :save_root]
before_filter :find_parent, :only => [:new, :create]
before_filter :tree_view, :only => [:delete, :show]
before_filter :permissions
accept_api_auth :show, :create, :save
skip_before_action :verify_authenticity_token, if: -> { request.headers['HTTP_X_REDMINE_API_KEY'].present? }
helper :all
helper :dmsf_folder_permissions
def permissions
render_403 unless DmsfFolder.permissions(@folder)
true
end
def expand_folder
@tree_view = true
@ -46,6 +53,8 @@ class DmsfController < ApplicationController
end
def show
s = DmsfFolder.visible.where(:project_id => 1995).to_sql
Rails.logger.info s
# also try to lookup folder by title if this is API call
find_folder_by_title if [:xml, :json].include? request.format.to_sym
get_display_params
@ -150,6 +159,7 @@ class DmsfController < ApplicationController
if params[:dmsf_folder] && params[:dmsf_folder][:custom_field_values].present?
redirect_to dmsf_folder_path(
:id => @project,
:folder_id => @folder,
:custom_field_id => params[:dmsf_folder][:custom_field_values].first[0],
:custom_value => params[:dmsf_folder][:custom_field_values].first[1])
else
@ -189,10 +199,29 @@ class DmsfController < ApplicationController
end
end
# Permissions
@folder.dmsf_folder_permissions.delete_all
if params[:permissions]
if params[:permissions][:role_ids]
params[:permissions][:role_ids].each do |role_id|
permission = DmsfFolderPermission.new
permission.object_id = role_id
permission.object_type = Role.model_name.to_s
@folder.dmsf_folder_permissions << permission
end
end
if params[:permissions][:user_ids]
params[:permissions][:user_ids].each do |user_id|
permission = DmsfFolderPermission.new
permission.object_id = user_id
permission.object_type = User.model_name.to_s
@folder.dmsf_folder_permissions << permission
end
end
end
saved = @folder.save
respond_to do |format|
format.js
format.api {
@ -217,6 +246,8 @@ class DmsfController < ApplicationController
@parent = @folder.dmsf_folder
@pathfolder = copy_folder(@folder)
@force_file_unlock_allowed = User.current.allowed_to?(:force_file_unlock, @project)
@users = User.find(@folder.dmsf_folder_permissions.users.map{ |p| p.object_id })
@users.delete_if{ |u| !u.active? }
end
def save
@ -236,7 +267,29 @@ class DmsfController < ApplicationController
end
end
# Permissions
@folder.dmsf_folder_permissions.delete_all
if params[:permissions]
if params[:permissions][:role_ids]
params[:permissions][:role_ids].each do |role_id|
permission = DmsfFolderPermission.new
permission.object_id = role_id
permission.object_type = Role.model_name.to_s
@folder.dmsf_folder_permissions << permission
end
end
if params[:permissions][:user_ids]
params[:permissions][:user_ids].each do |user_id|
permission = DmsfFolderPermission.new
permission.object_id = user_id
permission.object_type = User.model_name.to_s
@folder.dmsf_folder_permissions << permission
end
end
end
saved = @folder.save
respond_to do |format|
format.api {
unless saved
@ -600,92 +653,79 @@ class DmsfController < ApplicationController
@workflows_available = DmsfWorkflow.where(['project_id = ? OR project_id IS NULL', @project.id]).exists?
@file_approval_allowed = User.current.allowed_to?(:file_approval, @project)
tag = params[:custom_field_id].present? && params[:custom_value].present?
@folder = nil if tag
@extra_columns = [l(:label_last_approver), l(:field_project), l(:label_document_url), l(:label_last_revision_id)]
if @tree_view
@locked_for_user = false
else
unless @folder
if tag
@subfolders = []
DmsfFolder.where(:project_id => @project.id).visible.each do |f|
f.custom_field_values.each do |v|
if tag
@subfolders = []
folder_id = @folder.id if @folder
DmsfFolder.where(:project_id => @project.id, :dmsf_folder_id => folder_id).visible.each do |f|
f.custom_field_values.each do |v|
if v.custom_field_id == params[:custom_field_id].to_i
if v.custom_field.compare_values?(v.value, params[:custom_value])
@subfolders << f
break
end
end
end
end
@files = []
DmsfFile.where(:container_id => @project.id, :container_type => 'Project', :dmsf_folder_id => folder_id).visible.each do |f|
r = f.last_revision
if r
r.custom_field_values.each do |v|
if v.custom_field_id == params[:custom_field_id].to_i
if v.custom_field.compare_values?(v.value, params[:custom_value])
@subfolders << f
@files << f
break
end
end
end
end
@files = []
DmsfFile.where(:container_id => @project.id, :container_type => 'Project').visible.each do |f|
r = f.last_revision
if r
r.custom_field_values.each do |v|
if v.custom_field_id == params[:custom_field_id].to_i
if v.custom_field.compare_values?(v.value, params[:custom_value])
@files << f
break
end
end
end
@dir_links = []
DmsfLink.where(:project_id => @project.id, :target_type => DmsfFolder.model_name.to_s, :dmsf_folder_id => folder_id).where('target_id IS NOT NULL').visible.each do |l|
l.target_folder.custom_field_values.each do |v|
if v.custom_field_id == params[:custom_field_id].to_i
if v.custom_field.compare_values?(v.value, params[:custom_value])
@dir_links << l
break
end
end
end
@dir_links = []
DmsfLink.where(:project_id => @project.id, :target_type => DmsfFolder.model_name.to_s).visible.each do |l|
l.target_folder.custom_field_values.each do |v|
end
@file_links = []
DmsfLink.where(:project_id => @project.id, :target_type => DmsfFile.model_name.to_s, :dmsf_folder_id => folder_id).visible.each do |l|
r = l.target_file.last_revision if l.target_file
if r
r.custom_field_values.each do |v|
if v.custom_field_id == params[:custom_field_id].to_i
if v.custom_field.compare_values?(v.value, params[:custom_value])
@dir_links << l
@file_links << l
break
end
end
end
end
@file_links = []
DmsfLink.where(:project_id => @project.id, :target_type => DmsfFile.model_name.to_s).visible.each do |l|
r = l.target_file.last_revision if l.target_file
if r
r.custom_field_values.each do |v|
if v.custom_field_id == params[:custom_field_id].to_i
if v.custom_field.compare_values?(v.value, params[:custom_value])
@file_links << l
break
end
end
end
end
end
@url_links = []
DmsfLink.where(:project_id => @project.id, :target_type => 'DmsfUrl').visible.each do |l|
r = l.target_file.last_revision if l.target_file
if r
r.custom_field_values.each do |v|
if v.custom_field_id == params[:custom_field_id].to_i
if v.custom_field.compare_values?(v.value, params[:custom_value])
@file_links << l
break
end
end
end
end
end
end
@url_links = []
else
if @folder
@subfolders = @folder.dmsf_folders.visible
@files = @folder.dmsf_files.visible
@dir_links = @folder.folder_links.visible
@file_links = @folder.file_links.visible
@url_links = @folder.url_links.visible
@locked_for_user = @folder.locked_for_user?
else
@subfolders = @project.dmsf_folders.visible
@files = @project.dmsf_files.visible
@dir_links = @project.folder_links.visible
@file_links = @project.file_links.visible
@url_links = @project.url_links.visible
@locked_for_user = false
end
@locked_for_user = false
else
@subfolders = @folder.dmsf_folders.visible
@files = @folder.dmsf_files.visible
@dir_links = @folder.folder_links.visible
@file_links = @folder.file_links.visible
@url_links = @folder.url_links.visible
@locked_for_user = @folder.locked_for_user?
end
end

View File

@ -28,6 +28,7 @@ class DmsfFilesController < ApplicationController
before_filter :find_revision, :only => [:delete_revision]
before_filter :authorize
before_filter :tree_view, :only => [:delete]
before_filter :permissions
accept_api_auth :show
@ -35,6 +36,11 @@ class DmsfFilesController < ApplicationController
helper :dmsf_workflows
helper :dmsf
def permissions
render_403 unless DmsfFolder.permissions(@file.dmsf_folder)
true
end
def view
begin
if params[:download].blank?

View File

@ -0,0 +1,65 @@
# encoding: utf-8
#
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2011-17 Karel Pičman <karel.picman@konton.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.
class DmsfFolderPermissionsController < ApplicationController
unloadable
before_filter :find_folder, :only => [:destroy]
before_filter :find_project
before_filter :authorize
def new
@users = users_for_new_users
end
def append
@users = User.active.visible.where(:id => params[:user_ids]).to_a
render :nothing => true if @users.blank?
end
def autocomplete_for_user
@users = users_for_new_users
render :layout => false
end
private
def users_for_new_users
if params[:q].blank? && @project.present?
scope = @project.users
else
scope = User.all.limit(100)
end
scope.active.visible.sorted.like(params[:q]).to_a
end
def find_project
if params[:project_id]
@project = Project.visible.find_by_param(params[:project_id])
end
end
def find_folder
if params[:dmsf_folder_id]
@dmsf_folder = DmsfFolder.visible.find_by_id(params[:dmsf_folder_id])
end
end
end

View File

@ -0,0 +1,14 @@
module DmsfFolderPermissionsHelper
def users_checkboxes(users)
s = ''
if users
users.each do |user|
content = check_box_tag('permissions[user_ids][]', user.id, true, :id => nil) + user.name
s << content_tag(:label, content, :id => "user_permission_ids_#{user.id}", :class => 'inline')
end
end
s.html_safe
end
end

View File

@ -29,7 +29,7 @@ class DmsfFolder < ActiveRecord::Base
belongs_to :deleted_by_user, :class_name => 'User', :foreign_key => 'deleted_by_user_id'
belongs_to :user
has_many :dmsf_folders, -> { order(:title) }, :dependent => :destroy
has_many :dmsf_folders, -> { order :title }, :dependent => :destroy
has_many :dmsf_files, :dependent => :destroy
has_many :folder_links, -> { where(:target_type => 'DmsfFolder').order(:name) },
:class_name => 'DmsfLink', :foreign_key => 'dmsf_folder_id', :dependent => :destroy
@ -42,6 +42,7 @@ class DmsfFolder < ActiveRecord::Base
:class_name => 'DmsfLink', :foreign_key => 'target_id', :dependent => :destroy
has_many :locks, -> { where(entity_type: 1).order("#{DmsfLock.table_name}.updated_at DESC") },
:class_name => 'DmsfLock', :foreign_key => 'entity_id', :dependent => :destroy
has_many :dmsf_folder_permissions, :dependent => :destroy
INVALID_CHARACTERS = /\A[^\[\]\/\\\?":<>#%\*]*\z/.freeze
STATUS_DELETED = 1.freeze
@ -49,8 +50,14 @@ class DmsfFolder < ActiveRecord::Base
AVAILABLE_COLUMNS = %w(id title extension size modified version workflow author).freeze
DEFAULT_COLUMNS = %w(title size modified version workflow author).freeze
scope :visible, -> { where(:deleted => STATUS_ACTIVE) }
scope :deleted, -> { where(:deleted => STATUS_DELETED) }
scope :visible, -> { joins(:project).joins(
"LEFT JOIN #{DmsfFolderPermission.table_name} ON #{DmsfFolder.table_name}.id = #{DmsfFolderPermission.table_name}.dmsf_folder_id").where(
:deleted => STATUS_ACTIVE).where(DmsfFolder.visible_condition)
}
scope :deleted, -> { joins(:project).joins(
"LEFT JOIN #{DmsfFolderPermission.table_name} ON #{DmsfFolder.table_name}.id = #{DmsfFolderPermission.table_name}.dmsf_folder_id").where(
:deleted => STATUS_DELETED).where(DmsfFolder.visible_condition)
}
acts_as_customizable
@ -75,6 +82,33 @@ class DmsfFolder < ActiveRecord::Base
validates_length_of :description, :maximum => 65535
before_create :default_values
def self.visible_condition
sql = '1=1'
Project.allowed_to_condition(User.current, :view_dmsf_folders) do |role, user|
if user.id && user.logged?
sql = %{
(#{DmsfFolderPermission.table_name}.object_id IS NULL) OR
(#{DmsfFolderPermission.table_name}.object_id = #{role.id} AND #{DmsfFolderPermission.table_name}.object_type = 'Role') OR
(#{DmsfFolderPermission.table_name}.object_id = #{user.id} AND #{DmsfFolderPermission.table_name}.object_type = 'User')
}
end
end
sql
end
def self.permissions(folder)
return true if (User.current.admin? || folder.nil?)
if !folder.dmsf_folder || permissions(folder.dmsf_folder)
if folder.dmsf_folder_permissions.any?
role_ids = User.current.roles_for_project(folder.project).map{ |r| r.id }
role_permission_ids = folder.dmsf_folder_permissions.roles.map{ |p| p.object_id }
return (role_ids & role_permission_ids).any? || folder.dmsf_folder_permissions.users.map{ |p| p.object_id }.include?(User.current.id)
end
true
end
end
def default_values
@notifications = Setting.plugin_redmine_dmsf['dmsf_default_notifications']
if @notifications == '1'
@ -109,7 +143,7 @@ class DmsfFolder < ActiveRecord::Base
if self.locked?
errors[:base] << l(:error_folder_is_locked)
return false
elsif !self.dmsf_folders.visible.empty? || !self.dmsf_files.visible.empty?
elsif !self.dmsf_folders.visible.empty? || !self.dmsf_files.visible.empty? || !self.dmsf_links.visible.empty?
errors[:base] << l(:error_folder_is_not_empty)
return false
end

View File

@ -0,0 +1,28 @@
# encoding: utf-8
#
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2011-17 Karel Pičman <karel.picman@konton.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.
class DmsfFolderPermission < ActiveRecord::Base
unloadable
belongs_to :dmsf_folder
scope :users, -> { where(:object_type => User.model_name.to_s) }
scope :roles, -> { where(:object_type => Role.model_name.to_s) }
end

View File

@ -112,7 +112,7 @@
<% end %>
<% end %>
</td>
<td id="dmsf_position" class="dmsf_invisible"><%= position %></td>
<td class="dmsf_invisible">0</td>
<td class="dmsf_invisible"><%= subfolder.modified.to_i if subfolder %></td>
<td class="dmsf_invisible">0</td>
<td id="dmsf_position" class="hol"><%= position %></td>
<td class="hol">0</td>
<td class="hol"><%= subfolder.modified.to_i if subfolder %></td>
<td class="hol">0</td>

View File

@ -84,7 +84,7 @@
<% end %>
<% end %>
</td>
<td class="dmsf_invisible">0</td>
<td class="dmsf_invisible">0</td>
<td class="dmsf_invisible"><%= subfolder.modified.to_i if subfolder %></td>
<td class="dmsf_invisible">0</td>
<td class="hol">0</td>
<td class="hol">0</td>
<td class="hol"><%= subfolder.modified.to_i if subfolder %></td>
<td class="hol">0</td>

View File

@ -174,7 +174,7 @@
<% end %>
<% end %>
</td>
<td class="dmsf_invisible"><%= position %></td>
<td class="dmsf_invisible"><%= file.last_revision.size %></td>
<td class="dmsf_invisible"><%= file.last_revision.updated_at.to_i %></td>
<td class="dmsf_invisible"><%= file.last_revision.iversion %></td>
<td class="hol"><%= position %></td>
<td class="hol"><%= file.last_revision.size %></td>
<td class="hol"><%= file.last_revision.updated_at.to_i %></td>
<td class="hol"><%= file.last_revision.iversion %></td>

View File

@ -79,7 +79,7 @@
:class => 'icon icon-delete') %>
<% end %>
</td>
<td class="dmsf_invisible">1</td>
<td class="dmsf_invisible"><%= file.last_revision.size %></td>
<td class="dmsf_invisible"><%= file.last_revision.updated_at.to_i %></td>
<td class="dmsf_invisible"><%= file.last_revision.iversion %></td>
<td class="hol">1</td>
<td class="hol"><%= file.last_revision.size %></td>
<td class="hol"><%= file.last_revision.updated_at.to_i %></td>
<td class="hol"><%= file.last_revision.iversion %></td>

View File

@ -57,10 +57,10 @@
<% end %>
<% end %>
<th class ="dmsf_th"><%# controls %></th>
<th class="dmsf_invisible"><%# position %></th>
<th class="dmsf_invisible"><%# size %></th>
<th class="dmsf_invisible"><%# updated %></th>
<th class="dmsf_invisible"><%# version %></th>
<th class="hol"><%# position %></th>
<th class="hol"><%# size %></th>
<th class="hol"><%# updated %></th>
<th class="hol"><%# version %></th>
</tr>
</thead>
<tbody>

View File

@ -57,10 +57,10 @@
<% end %>
<% end %>
<th class ="dmsf_th"><%# controls %></th>
<th class="dmsf_invisible"><%# position %></th>
<th class="dmsf_invisible"><%# size %></th>
<th class="dmsf_invisible"><%# updated %></th>
<th class="dmsf_invisible"><%# version %></th>
<th class="hol"><%# position %></th>
<th class="hol"><%# size %></th>
<th class="hol"><%# updated %></th>
<th class="hol"><%# version %></th>
</tr>
</thead>
<tbody>

View File

@ -78,7 +78,7 @@
<span class="icon"></span>
<% end %>
</td>
<td class="dmsf_invisible"><%= position %> </td>
<td class="dmsf_invisible"></td>
<td class="dmsf_invisible">link.updated_at.to_i</td>
<td class="dmsf_invisible"></td>
<td class="hol"><%= position %> </td>
<td class="hol"></td>
<td class="hol">link.updated_at.to_i</td>
<td class="hol"></td>

View File

@ -67,7 +67,7 @@
:title => l(:title_delete), :class => "icon icon-delete" ) %>
<% end %>
</td>
<td class="dmsf_invisible">1</td>
<td class="dmsf_invisible"></td>
<td class="dmsf_invisible">link.updated_at.to_i</td>
<td class="dmsf_invisible"></td>
<td class="hol">1</td>
<td class="hol"></td>
<td class="hol">link.updated_at.to_i</td>
<td class="hol"></td>

View File

@ -55,7 +55,7 @@
<% unless @folder.locked? %>
<%= link_to(l(:button_delete), delete_dmsf_path(:id => @project, :folder_id => @folder),
:data => {:confirm => l(:text_are_you_sure)},
:title => l(:title_delete), :class => 'icon icon-del') %>
:title => l(:title_delete), :class => 'icon icon-del', :method => :delete) %>
<% end %>
<% end %>
</div>
@ -81,7 +81,24 @@
<p>
<%= f.text_area :description, :rows => 8, :class => 'wiki-edit' %>
</p>
<% values = @folder ? @folder.custom_field_values : @parent ? @parent.custom_field_values : DmsfFolder.new(:project => @project).custom_field_values %>
<p>
<%= label_tag '', l(:label_permissions) %>
<% User.current.managed_roles(@project).each do |role| %>
<% checked = @folder.dmsf_folder_permissions.roles.exists?(:object_id => role.id) %>
<label class="inline"><%= check_box_tag 'permissions[role_ids][]', role.id, checked, :id => nil %> <%= role %></label>
<% end %>
<span id="user_permissions">
<br/>
<%= users_checkboxes(@users) %>
</span>
<span class="search_for_watchers">
<%= link_to l(:label_user_search),
new_dmsf_folder_permissions_path(:project_id => @project, :dmsf_folder_id => @folder),
:remote => true,
:method => 'get' %>
</span>
</p>
<% values = @folder ? @folder.custom_field_values : @parent ? @parent.custom_field_values : DmsfFolder.new.custom_field_values %>
<% values.each do |value| %>
<p><%= custom_field_tag_with_label(:dmsf_folder, value) %></p>
<% end %>

View File

@ -35,7 +35,7 @@
:title => l(:link_edit, :title => h(@folder.title)),
:class => 'icon icon-edit') %>
<% end %>
<% if @folder && (!@folder.locked_for_user || User.current.allowed_to?(:force_file_unlock, @project)) %>
<% if @folder && (!@locked_for_user || User.current.allowed_to?(:force_file_unlock, @project)) %>
<% if @folder.locked? %>
<%= link_to_if(@folder.unlockable?, l(:button_unlock),
unlock_dmsf_path(:id => @project, :folder_id => @folder, :current => request.url),
@ -92,12 +92,15 @@
<%= submit_tag(l(:button_delete), :title => l(:title_delete_checked), :name => 'delete_entries') if @file_delete_allowed %>
<% end %>
</div>
<% values = @folder ? @folder.custom_field_values : @parent ? @parent.custom_field_values : DmsfFolder.new(:project => @project).custom_field_values %>
<% values = @folder ? @folder.custom_field_values : DmsfFolder.new.custom_field_values %>
<% unless values.empty? %>
<div id="dmsf_tag" class="dmsf_controls" style="float: right">
<%= custom_field_tag_with_label(
:dmsf_folder,
CustomValue.new(:custom_field_id => params[:custom_field_id].present? ? params[:custom_field_id] : values.first.custom_field_id, :value => params[:custom_value])) %>
<% custom_value = values.first %>
<% custom_value.custom_field.is_required = false %>
<% custom_value.value = params[:custom_value].present? ? params[:custom_value] : '' %>
<% name = :dmsf_folder %>
<%= content_tag('label', custom_field_name_tag(custom_value.custom_field),
:for => "#{name}_custom_field_values_#{custom_value.custom_field.id}") + ': ' + custom_field_tag(name, custom_value) %>
</div>
<% end %>
<div class="autoscroll">

View File

@ -81,10 +81,10 @@
<% end %>
<% end %>
<th class ="dmsf_th"><%# controls %></th>
<th class="dmsf_invisible"><%# position %></th>
<th class="dmsf_invisible"><%# size %></th>
<th class="dmsf_invisible"><%# updated %></th>
<th class="dmsf_invisible"><%# version %></th>
<th class="hol"><%# position %></th>
<th class="hol"><%# size %></th>
<th class="hol"><%# updated %></th>
<th class="hol"><%# version %></th>
</tr>
</thead>
<tbody>

View File

@ -0,0 +1,22 @@
<h3 class="title"><%= l(:label_user_search) %></h3>
<%= form_tag(append_dmsf_folder_permissions_path,
:remote => true,
:method => :post,
:id => 'new-user-form') do %>
<%= hidden_field_tag :project_id, @project.id %>
<p><%= label_tag 'user_search', l(:label_user_search) %><%= text_field_tag 'user_search', nil %></p>
<%= javascript_tag "observeSearchfield('user_search', 'users_for_users', '#{ escape_javascript url_for(:controller => 'dmsf_folder_permissions',
:action => 'autocomplete_for_user',
:project_id => @project) }')" %>
<div id="users_for_watcher">
<%= principals_check_box_tags('user_ids[]', users) %>
</div>
<p class="buttons">
<%= submit_tag l(:button_add), :name => nil, :onclick => "hideModal(this);" %>
<%= submit_tag l(:button_cancel), :name => nil, :onclick => "hideModal(this);", :type => 'button' %>
</p>
<% end %>

View File

@ -0,0 +1,4 @@
<% @users.each do |user| %>
$("#user_permission_ids_<%= user.id %>").remove();
<% end %>
$('#user_permissions').append('<%= escape_javascript(users_checkboxes(@users)) %>');

View File

@ -0,0 +1 @@
<%= principals_check_box_tags 'user[user_ids][]', @users %>

View File

@ -0,0 +1,3 @@
$('#ajax-modal').html('<%= escape_javascript(render :partial => 'dmsf_folder_permissions/new', :locals => {:users => @users}) %>');
showModal('ajax-modal', '400px');
$('#ajax-modal').addClass('new-user');

View File

@ -21,7 +21,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
%>
<span id="public_url" class="dmsf_invisible">
<span id="public_url" class="hol">
<%= check_box_tag('email[public_urls]', 1, false) %> <%= l(:label_public_urls) %>
<%= date_field_tag('email[expired_at]', '', :value => (DateTime.now + 3.days).to_date, :size => 10,
:readonly => true) + calendar_for('email_expired_at') %>

View File

@ -24,6 +24,10 @@
border: none;
}
#dmsf_tag {
margin-left: 5px;
}
.list .dmsf_modified {
min-width: 127px;
width: 127px;
@ -103,10 +107,6 @@
white-space: nowrap;
}
.dmsf_invisible {
display: none;
}
.dmsf_upload_select {
float: right;
font-size: 0.9em;

View File

@ -32,7 +32,7 @@ if Redmine::Plugin.installed? :redmine_dmsf
post '/projects/:id/dmsf/create', :controller => 'dmsf', :action => 'create'
get '/projects/:id/dmsf/notify/activate', :controller => 'dmsf', :action => 'notify_activate', :as => 'notify_activate_dmsf'
get '/projects/:id/dmsf/notify/deactivate', :controller => 'dmsf', :action => 'notify_deactivate', :as => 'notify_deactivate_dmsf'
get '/projects/:id/dmsf/delete', :controller => 'dmsf', :action => 'delete', :as => 'delete_dmsf'
delete '/projects/:id/dmsf/delete', :controller => 'dmsf', :action => 'delete', :as => 'delete_dmsf'
post '/projects/:id/dmsf/save', :controller => 'dmsf', :action => 'save'
post '/projects/:id/dmsf/save/root', :controller => 'dmsf', :action => 'save_root'
post '/projects/:id/dmsf/entries', :controller => 'dmsf', :action => 'entries_operation'
@ -157,5 +157,13 @@ if Redmine::Plugin.installed? :redmine_dmsf
# Public URLs
resource :dmsf_public_urls
# Folder permissions
resource :dmsf_folder_permissions do
member do
get 'autocomplete_for_user'
post 'append'
end
end
end
end
end

View File

@ -0,0 +1,34 @@
# 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.
class CreateDmsfFolderPermissions < ActiveRecord::Migration
def up
create_table :dmsf_folder_permissions do |t|
t.references :dmsf_folder
t.integer :object_id, :null => false
t.string :object_type, :limit => 30, :null => false
end
add_index :dmsf_folder_permissions, :dmsf_folder_id
end
def self.down
drop_table :dmsf_folder_permissions
end
end

28
init.rb
View File

@ -69,35 +69,35 @@ Redmine::Plugin.register :redmine_dmsf do
permission :view_dmsf_file_revisions,
:read => true
permission :view_dmsf_folders,
{:dmsf => [:show],
:dmsf_folders_copy => [:new, :copy_to, :move_to]},
{ :dmsf => [:show],
:dmsf_folders_copy => [:new, :copy_to, :move_to] },
:read => true
permission :user_preferences,
{:dmsf_state => [:user_pref_save]}
{ :dmsf_state => [:user_pref_save] }
permission :view_dmsf_files,
{:dmsf => [:entries_operation, :entries_email, :download_email_entries, :tag_changed],
{ :dmsf => [:entries_operation, :entries_email, :download_email_entries, :tag_changed],
:dmsf_files => [:show, :view, :thumbnail],
:dmsf_files_copy => [:new, :create, :move],
:dmsf_workflows => [:log]},
:dmsf_workflows => [:log] },
:read => true
permission :email_documents,
{:dmsf_public_urls => [:create]}
{ :dmsf_public_urls => [:create] }
permission :folder_manipulation,
{:dmsf => [:new, :create, :delete, :edit, :save, :edit_root, :save_root, :lock, :unlock, :notify_activate, :notify_deactivate, :restore]}
{ :dmsf => [:new, :create, :delete, :edit, :save, :edit_root, :save_root, :lock, :unlock, :notify_activate, :notify_deactivate, :restore],
:dmsf_folder_permissions => [:new, :append, :autocomplete_for_user] }
permission :file_manipulation,
{:dmsf_files => [:create_revision, :lock, :unlock, :delete_revision, :notify_activate, :notify_deactivate, :restore],
{ :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] }
permission :file_delete,
{ :dmsf => [:trash, :delete_entries],
:dmsf_files => [:delete]}
:dmsf_files => [:delete] }
permission :force_file_unlock, {}
permission :file_approval,
{:dmsf_workflows => [:action, :new_action, :autocomplete_for_user, :start, :assign, :assignment]}
{ :dmsf_workflows => [:action, :new_action, :autocomplete_for_user, :start, :assign, :assignment] }
permission :manage_workflows,
{:dmsf_workflows => [:index, :new, :create, :destroy, :show, :new_step, :add_step, :remove_step, :reorder_steps,
:update, :update_step, :delete_step, :edit]}
{ :dmsf_workflows => [:index, :new, :create, :destroy, :show, :new_step, :add_step, :remove_step, :reorder_steps,
:update, :update_step, :delete_step, :edit] }
end
# Administration menu extension

View File

@ -33,6 +33,7 @@ require 'redmine_dmsf/patches/user_preference_patch'
require 'redmine_dmsf/patches/user_patch'
require 'redmine_dmsf/patches/issue_patch'
require 'redmine_dmsf/patches/application_helper_patch'
require 'redmine_dmsf/patches/role_patch'
# Load up classes that make up our WebDAV solution ontop of DAV4Rack
require 'redmine_dmsf/webdav/base_resource'

View File

@ -0,0 +1,53 @@
# 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.
module RedmineDmsf
module Patches
module RolePatch
def self.included(base) # :nodoc:
base.send(:include, InstanceMethods)
base.class_eval do
unloadable
before_destroy :remove_dmsf_references
end
end
module InstanceMethods
def remove_dmsf_references
return if self.id.nil?
substitute = Role.anonymous
DmsfFolderPermission.where(:object_id => self.id, :object_type => 'Role').update_all(
:object_id => substitute.id)
end
end
end
end
end
# Apply patch
Rails.configuration.to_prepare do
unless Role.included_modules.include?(RedmineDmsf::Patches::RolePatch)
Role.send(:include, RedmineDmsf::Patches::RolePatch)
end
end

View File

@ -48,6 +48,7 @@ module RedmineDmsf
DmsfWorkflowStepAssignment.where(:user_id => id).update_all(:user_id => substitute.id)
DmsfWorkflowStep.where(:user_id => id).update_all(:user_id => substitute.id)
DmsfWorkflow.where(:author_id => id).update_all(:author_id => substitute.id)
DmsfFolderPermission.where(:object_id => id, :object_type => 'User').update_all(:object_id => substitute.id)
end
end

View File

@ -0,0 +1,12 @@
---
dmsf_folder_permissions_001:
id: 1
dmsf_folder_id: 7
object_id: 1
object_type: 'Role'
dmsf_folder_permissions_002:
id: 2
dmsf_folder_id: 7
object_id: 3
object_type: 'User'

View File

@ -39,4 +39,11 @@ dmsf_folders_006:
title: folder6
project_id: 1
dmsf_folder_id: NULL
user_id: 2
user_id: 2
dmsf_folders_007:
id: 7
title: folder7
project_id: 1
dmsf_folder_id: NULL
user_id: 1

View File

View File

@ -25,7 +25,7 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase
fixtures :users, :email_addresses, :dmsf_folders, :custom_fields,
:custom_values, :projects, :roles, :members, :member_roles, :dmsf_links,
:dmsf_files, :dmsf_file_revisions
:dmsf_files, :dmsf_file_revisions, :dmsf_folder_permissions, :dmsf_locks
def setup
@project = Project.find_by_id 1
@ -40,9 +40,10 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase
@role = Role.find_by_id 1
@custom_field = CustomField.find_by_id 21
@custom_value = CustomValue.find_by_id 21
@user_member = User.find_by_id 2
@folder7 = DmsfFolder.find_by_id 7
@manager = User.find_by_id 2 #1
User.current = nil
@request.session[:user_id] = @user_member.id
@request.session[:user_id] = @manager.id
end
def test_truth
@ -56,7 +57,7 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase
assert_kind_of Role, @role
assert_kind_of CustomField, @custom_field
assert_kind_of CustomValue, @custom_value
assert_kind_of User, @user_member
assert_kind_of User, @manager
end
def test_edit_folder_forbidden
@ -197,8 +198,8 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase
def test_show_tree_view
@role.add_permission! :view_dmsf_files
@role.add_permission! :view_dmsf_folders
@user_member.pref[:dmsf_tree_view] = '1'
@user_member.preference.save
@manager.pref[:dmsf_tree_view] = '1'
@manager.preference.save
get :show, :id => @project.id
assert_response :success
assert_select 'tr.dmsf_tree'

View File

@ -0,0 +1,68 @@
# 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.
require File.expand_path('../../test_helper', __FILE__)
class DmsfFolderPermissionsControllerTest < RedmineDmsf::Test::TestCase
fixtures :users, :dmsf_folders, :projects, :roles, :members, :member_roles, :dmsf_folder_permissions
def setup
@project1 = Project.find_by_id 1
assert_not_nil @project1
@project1.enable_module! :dmsf
@folder7 = DmsfFolder.find_by_id 7
@manager = User.find_by_id 2
@developer = User.find_by_id 3
@manager_role = Role.find_by_id 1
User.current = nil
@request.session[:user_id] = @manager.id
@manager_role.add_permission! :view_dmsf_folders
@manager_role.add_permission! :folder_manipulation
end
def test_truth
assert_kind_of Project, @project1
assert_kind_of DmsfFolder, @folder7
assert_kind_of User, @manager
assert_kind_of User, @developer
assert_kind_of Role, @manager_role
end
def test_new
xhr :get, :new, :project_id => @project1, :format => 'js'
assert_response :success
assert_template 'new'
assert_equal 'text/javascript', response.content_type
end
def test_autocomplete_for_user
xhr :get, :autocomplete_for_user, :project_id => @project1, :q => 'smi', :format => 'js'
assert_response :success
assert_include 'John Smith', response.body
end
def test_append
xhr :get, :new, :project_id => @project1, :user_ids => [@manager.id], :format => 'js'
assert_response :success
assert_template 'new'
assert_equal 'text/javascript', response.content_type
end
end

View File

@ -324,7 +324,7 @@ class DmsfWebdavPropfindTest < RedmineDmsf::Test::IntegrationTest
def test_propfind_depth1_on_project1_for_admin_with_cache
RedmineDmsf::Webdav::Cache.init_testcache
assert_difference 'RedmineDmsf::Webdav::Cache.cache.instance_variable_get(:@data).count', +7 do
assert_difference 'RedmineDmsf::Webdav::Cache.cache.instance_variable_get(:@data).count', +8 do
xml_http_request :propfind, "/dmsf/webdav/#{@project1.identifier}", nil,
@admin.merge!({:HTTP_DEPTH => '1'})
assert_response 207 # MultiStatus
@ -378,7 +378,7 @@ class DmsfWebdavPropfindTest < RedmineDmsf::Test::IntegrationTest
log_user 'admin', 'admin' # login as admin
assert !User.current.anonymous?, 'Current user is anonymous'
assert_difference 'RedmineDmsf::Webdav::Cache.cache.instance_variable_get(:@data).count', +7 do
assert_difference 'RedmineDmsf::Webdav::Cache.cache.instance_variable_get(:@data).count', +8 do
xml_http_request :propfind, "/dmsf/webdav/#{@project1.identifier}", nil,
@admin.merge!({:HTTP_DEPTH => '1'})
assert_response 207 # MultiStatus

View File

@ -0,0 +1,38 @@
# 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.
require File.expand_path('../../test_helper', __FILE__)
class DmsfFolderPermissionTest < RedmineDmsf::Test::UnitTest
fixtures :dmsf_folder_permissions
def test_scope
assert_equal 2, DmsfFolderPermission.count
end
def test_scope_users
assert_equal 1, DmsfFolderPermission.users.count
end
def test_scope_roles
assert_equal 1, DmsfFolderPermission.roles.count
end
end

View File

@ -22,18 +22,48 @@
require File.expand_path('../../test_helper', __FILE__)
class DmsfFolderTest < RedmineDmsf::Test::UnitTest
fixtures :projects, :users, :email_addresses, :dmsf_folders, :roles, :members, :member_roles
fixtures :projects, :users, :email_addresses, :dmsf_folders, :roles, :members, :member_roles, :dmsf_folder_permissions
def setup
def setup
@project = Project.find_by_id 1
assert_not_nil @project
@project.enable_module! :dmsf
@folder4 = DmsfFolder.find_by_id 4
@folder5 = DmsfFolder.find_by_id 5
@folder6 = DmsfFolder.find_by_id 6
@folder7 = DmsfFolder.find_by_id 7
@manager = User.find_by_id 2
@developer = User.find_by_id 3
end
def test_truth
assert_kind_of DmsfFolder, @folder4
assert_kind_of DmsfFolder, @folder5
assert_kind_of DmsfFolder, @folder6
assert_kind_of DmsfFolder, @folder7
assert_kind_of Project, @project
assert_kind_of User, @manager
assert_kind_of User, @developer
end
def test_visiblity
# The role has got permissions
User.current = @manager
assert_equal 6, DmsfFolder.visible.where(:project_id => 1).count
# The user has got permissions
User.current = @developer
assert_equal 6, DmsfFolder.visible.where(:project_id => 1).count
# Hasn't got permissions for @folder7
@folder7.dmsf_folder_permissions.where(:object_type => 'User').delete_all
assert_equal 5, DmsfFolder.visible.where(:project_id => 1).count
end
def test_permissions
User.current = @developer
assert DmsfFolder.permissions(@folder7)
@folder7.dmsf_folder_permissions.where(:object_type => 'User').delete_all
@folder7.reload
assert !DmsfFolder.permissions(@folder7)
end
def test_delete

View File

@ -66,7 +66,7 @@ class ProjectPatchTest < RedmineDmsf::Test::UnitTest
def test_dmsf_count
hash = @project1.dmsf_count
assert_equal 7, hash[:files]
assert_equal 5, hash[:folders]
assert_equal 7, hash[:folders]
end
def test_copy_approval_workflows
@ -78,7 +78,7 @@ class ProjectPatchTest < RedmineDmsf::Test::UnitTest
def test_copy_dmsf
assert_equal 3, @project1.dmsf_files.visible.count
assert_equal 2, @project1.dmsf_folders.visible.count
assert_equal 4, @project1.dmsf_folders.visible.count
assert_equal 1, @project1.file_links.visible.count
assert_equal 1, @project1.folder_links.visible.count
assert_equal 1, @project1.url_links.visible.count
@ -89,7 +89,7 @@ class ProjectPatchTest < RedmineDmsf::Test::UnitTest
assert_equal 0, @project3.url_links.visible.count
@project3.copy_dmsf(@project1)
assert_equal 3, @project3.dmsf_files.visible.count
assert_equal 2, @project3.dmsf_folders.visible.count
assert_equal 3, @project3.dmsf_folders.visible.count # Folder if 7 is not visible due to folder permissions
assert_equal 1, @project3.file_links.visible.count
assert_equal 1, @project3.folder_links.visible.count
assert_equal 1, @project3.url_links.visible.count

View File

@ -23,28 +23,28 @@ require File.expand_path('../../test_helper', __FILE__)
class UserPatchTest < RedmineDmsf::Test::UnitTest
fixtures :users, :projects, :dmsf_files, :dmsf_file_revisions, :dmsf_folders, :dmsf_links
def setup
@user1 = User.find_by_id 1
@user2 = User.find_by_id 2
end
def test_truth
assert_kind_of User, @user1
assert_kind_of User, @user2
end
def test_remove_dmsf_references
id = @user1.id
@user1.destroy
assert_equal 0 ,DmsfFileRevisionAccess.where(:user_id => id).count
assert_equal 0 ,DmsfFileRevision.where(:user_id => id).count
assert_equal 0 ,DmsfFile.where(:deleted_by_user_id => id).count
assert_equal 0 ,DmsfFolder.where(:user_id => id).count
assert_equal 0 ,DmsfFolder.where(:deleted_by_user_id => id).count
assert_equal 0 ,DmsfLink.where(:user_id => id).count
assert_equal 0 ,DmsfLink.where(:deleted_by_user_id => id).count
assert_equal 0 ,DmsfLock.where(:user_id => id).count
assert_equal 0 ,DmsfWorkflowStepAction.where(:author_id => id).count
assert_equal 0 ,DmsfWorkflowStepAssignment.where(:user_id => id).count
assert_equal 0 ,DmsfWorkflowStep.where(:user_id => id).count
assert_equal 0 ,DmsfWorkflow.where(:author_id => id).count
id = @user2.id
@user2.destroy
assert_equal 0, DmsfFileRevisionAccess.where(:user_id => id).count
assert_equal 0, DmsfFileRevision.where(:user_id => id).count
assert_equal 0, DmsfFile.where(:deleted_by_user_id => id).count
assert_equal 0, DmsfFolder.where(:user_id => id).count
assert_equal 0, DmsfFolder.where(:deleted_by_user_id => id).count
assert_equal 0, DmsfLink.where(:user_id => id).count
assert_equal 0, DmsfLink.where(:deleted_by_user_id => id).count
assert_equal 0, DmsfLock.where(:user_id => id).count
assert_equal 0, DmsfWorkflowStepAction.where(:author_id => id).count
assert_equal 0, DmsfWorkflowStepAssignment.where(:user_id => id).count
assert_equal 0, DmsfWorkflowStep.where(:user_id => id).count
assert_equal 0, DmsfWorkflow.where(:author_id => id).count
end
end