diff --git a/CHANGELOG.md b/CHANGELOG.md index 88009758..f673388d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ Changelog for Redmine DMSF ========================== +1.5.7 *2015-??-??* +------------------ + 1.5.6 *2015-01-25* ------------------ diff --git a/Gemfile b/Gemfile index e74c0d78..3a1540e0 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,5 @@ # encoding: utf-8 -# +# # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš @@ -23,11 +23,11 @@ source 'https://rubygems.org' gem 'rubyzip', '>= 1.0.0' -gem 'zip-zip' # Just to avoid 'cannot load such file -- zip/zip' error +gem 'zip-zip' gem 'simple_enum' -gem 'uuidtools', '~> 2.1.1' -gem 'dav4rack', '~> 0.3.0' +gem 'uuidtools' +gem 'dav4rack' -group :xapian do +group :xapian do gem 'xapian-full-alaveteli', :require => false end \ No newline at end of file diff --git a/README.md b/README.md index 6af69759..2d0ba68e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Redmine DMSF Plugin =================== -The current version of Redmine DMSF is **1.5.6** [![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 **1.5.7** [![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. @@ -77,18 +77,18 @@ From Omega documentation: On Debian use: -```sudo apt-get install xapian-omega libxapian-dev xpdf xpdf-utils \ +```sudo apt-get install xapian-omega libxapian-dev xpdf poppler-utils \ antiword unzip catdoc libwpd-tools libwps-tools gzip unrtf catdvi \ djview djview3 uuid uuid-dev xz-utils``` On Ubuntu use: -```sudo apt-get install xapian-omega libxapian-dev xpdf xpdf-utils antiword \ +```sudo apt-get install xapian-omega libxapian-dev xpdf poppler-utils antiword \ unzip catdoc libwpd-tools libwps-tools gzip unrtf catdvi djview djview3 \ uuid uuid-dev xz-utils``` On CentOS user: -```sudo yum install xapian-omega libxapian-dev xpdf xpdf-utils antiword \ +```sudo yum install xapian-omega libxapian-dev xpdf poppler-utils antiword \ unzip catdoc libwpd-tools libwps-tools gzip unrtf catdvi djview djview3 \ uuid uuid-dev xz``` @@ -101,16 +101,16 @@ Search will now automatically search DMSF content when a Redmine search is perfo ###Linking DMSF files from Wiki entries: -####Link to a file with id 17: +####Link to a document with id 17: `{{dmsf(17)}}` -####Link to a file with id 17 with link text "File" +####Link to a document with id 17 with link text "File" `{{dmsf(17, File)}}` -####Link to the description of a file with id 17 +####Link to the description of a document with id 17 `{{dmsfd(17)}}` -####Link to the preview of the first 5 lines from a file with id 17 +####Link to the preview of 5 lines from a document with id 17 `{{dmsft(17, 5)}}` ####An inline picture of the file with id 8; it must be an image file such as JPEG, PNG,... @@ -122,10 +122,26 @@ Search will now automatically search DMSF content when a Redmine search is perfo ####An inline picture with custom size `{{dmsf_image(8, size=50%)}}` +####An inline picture with custom height +`{{dmsf_image(8, height=300)}}` + +####An inline picture with custom width +`{{dmsf_image(8, width=300)}}` + ####An inline picture with custom size `{{dmsf_image(8, size=640x480)}}` -The DMSF file/revision id can be found in link for file/revision download from within Redmine. +####A thumbnail with height of 200px +`{{dmsftn(8)}}` + +####A thumbnail with custom size +`{{dmsftn(8, size=300)}}` + +####Approval workflow status of a document with id 8 +`{{dmsfw(8)}}` + + +The DMSF document/revision id can be found in document details. ###Linking DMSF folders from Wiki entries: @@ -153,7 +169,13 @@ In the file /public/help//wiki_syntax_detailed.html, aft
  • {{dmsff(5, Folder)}} (a link to the folder with id 5 with the link text "Folder")
  • {{dmsf_image(8)}} (an inline picture of the file with id 8; it must be an image file such as JPEG, PNG,...)
  • {{dmsf_image(8, size=300)}} (an inline picture with custom size)
  • -
  • {{dmsf_image(8, size=640x480)}} (an inline picture with custom size)
  • +
  • {{dmsf_image(8, size=640x480)}} (an inline picture with custom size)
  • +
  • {{dmsf_image(8, size=50%)}} (an inline picture with custom size)
  • +
  • {{dmsf_image(8, height=300)}} (an inline picture with custom size)
  • +
  • {{dmsf_image(8, width=300)}} (an inline picture with custom size)
  • +
  • {{dmsftn(8)}} (a thumbnail with height of 200px)
  • +
  • {{dmsftn(8, size=300)}} (a thumbnail with custom size)
  • +
  • {{dmsfw(8)}} (approval workflow status of a document with id 8)
  • The DMSF file/revision id can be found in the link for file/revision download from within Redmine.
    The DMSF folder id can be found in the link when opening folders within Redmine. @@ -179,6 +201,7 @@ Before installing ensure that the Redmine instance is stopped. 1. In case of upgrade BACKUP YOUR DATABASE first 2. Put redmine_dmsf plugin directory into plugins. 3. Install dependencies: `bundle install`. +3.1 To install dependencies without Xapian (full-text searching): `bundle install --without xapian`. This option might be useful especially in Windows. 4. Initialize/Update database: `bundle exec rake redmine:plugins:migrate RAILS_ENV="production"`. 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. @@ -198,6 +221,10 @@ Before installing ensure that the Redmine instance is stopped. rake redmine:dmsf_convert_documents project=test RAILS_ENV="production" + (If you don't run the rake task as the web server user, don't forget to change the ownership of the imported files, e.g. + chown -R www-data:www-data /redmine/files/dmsf + afterwards) + b) To alert all users who are expected to do an approval in the current approval steps Example: diff --git a/app/controllers/dmsf_controller.rb b/app/controllers/dmsf_controller.rb index 1c9deea9..ae1419f1 100644 --- a/app/controllers/dmsf_controller.rb +++ b/app/controllers/dmsf_controller.rb @@ -4,7 +4,7 @@ # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -23,133 +23,133 @@ class DmsfController < ApplicationController unloadable - class ZipMaxFilesError < StandardError; end - class EmailMaxFileSize < StandardError; end - class FileNotFound < StandardError; end - before_filter :find_project before_filter :authorize before_filter :find_folder, :except => [:new, :create, :edit_root, :save_root] - before_filter :find_parent, :only => [:new, :create] - + before_filter :find_parent, :only => [:new, :create] + before_filter :tree_view, :only => [:delete, :show] + accept_api_auth :show, :create helper :all - def show + def show @folder_manipulation_allowed = User.current.allowed_to?(:folder_manipulation, @project) @file_manipulation_allowed = User.current.allowed_to?(:file_manipulation, @project) @file_delete_allowed = User.current.allowed_to?(:file_delete, @project) @file_view_allowed = User.current.allowed_to?(:view_dmsf_files, @project) @force_file_unlock_allowed = User.current.allowed_to?(:force_file_unlock, @project) - @workflows_available = DmsfWorkflow.where(['project_id = ? OR project_id IS NULL', @project.id]).count > 0 + @workflows_available = DmsfWorkflow.where(['project_id = ? OR project_id IS NULL', @project.id]).exists? @file_approval_allowed = User.current.allowed_to?(:file_approval, @project) - - unless @folder - if params[:custom_field_id].present? && params[:custom_value].present? - @subfolders = [] - DmsfFolder.where(:project_id => @project.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(:project_id => @project.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]) - @files << f - break - end - end - 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| - 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 - 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 - 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 - end + tag = params[:custom_field_id].present? && params[:custom_value].present? + @folder = nil if tag + if @tree_view @locked_for_user = false else - - if @folder.deleted - render_404 - return - end - - @subfolders = @folder.subfolders.visible - @files = @folder.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? + unless @folder + if tag + @subfolders = [] + DmsfFolder.where(:project_id => @project.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(:project_id => @project.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]) + @files << f + break + end + end + 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| + 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 + 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 + 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 + end + @locked_for_user = false + else + if @folder.deleted? + render_404 + return + end + @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 @ajax_upload_size = Setting.plugin_redmine_dmsf['dmsf_max_ajax_upload_filesize'].present? ? Setting.plugin_redmine_dmsf['dmsf_max_ajax_upload_filesize'] : 100 # Trash - @trash_visible = @folder_manipulation_allowed && @file_manipulation_allowed && + @trash_visible = @folder_manipulation_allowed && @file_manipulation_allowed && @file_delete_allowed && !@locked_for_user && !@folder @trash_enabled = DmsfFolder.deleted.where(:project_id => @project.id).any? || DmsfFile.deleted.where(:project_id => @project.id).any? || DmsfLink.deleted.where(:project_id => @project.id).any? - + respond_to do |format| format.html { render :layout => !request.xhr? } format.api end - end + end def trash @folder_manipulation_allowed = User.current.allowed_to? :folder_manipulation, @project @@ -188,19 +188,22 @@ class DmsfController < ApplicationController return end - if selected_dir_links.present? - selected_dir_links.each do |id| - link = DmsfLink.find_by_id id - selected_folders << link.target_id if link && !selected_folders.include?(link.target_id.to_s) + if selected_dir_links.present? && + (params[:email_entries].present? || params[:download_entries].present?) + selected_dir_links.each do |id| + link = DmsfLink.find_by_id id + selected_folders << link.target_id if link && !selected_folders.include?(link.target_id.to_s) end end - if selected_file_links.present? - selected_file_links.each do |id| - link = DmsfLink.find_by_id id - selected_files << link.target_id if link && !selected_files.include?(link.target_id.to_s) + if selected_file_links.present? && + (params[:email_entries].present? || params[:download_entries].present?) + selected_file_links.each do |id| + link = DmsfLink.find_by_id id + selected_files << link.target_id if link && !selected_files.include?(link.target_id.to_s) end - end + end + if params[:email_entries].present? email_entries(selected_folders, selected_files) elsif params[:restore_entries].present? @@ -215,16 +218,14 @@ class DmsfController < ApplicationController else download_entries(selected_folders, selected_files) end - rescue ZipMaxFilesError - flash[:error] = l(:error_max_files_exceeded, :number => Setting.plugin_redmine_dmsf['dmsf_max_file_download']) - redirect_to :back - rescue EmailMaxFileSize - flash[:error] = l(:error_max_email_filesize_exceeded, :number => Setting.plugin_redmine_dmsf['dmsf_max_email_filesize']) - redirect_to :back rescue FileNotFound render_404 rescue DmsfAccessError - render_403 + render_403 + rescue Exception => e + flash[:error] = e.message + Rails.logger.error e.message + redirect_to :back end def tag_changed @@ -239,7 +240,7 @@ class DmsfController < ApplicationController end end - def entries_email + def entries_email if params[:email][:to].strip.blank? flash.now[:error] = l(:error_email_to_must_be_entered) render :action => 'email_entries' @@ -258,67 +259,68 @@ class DmsfController < ApplicationController render :action => 'edit' end - def create + def create @folder = DmsfFolder.new @folder.title = params[:dmsf_folder][:title].strip @folder.description = params[:dmsf_folder][:description].strip @folder.dmsf_folder_id = params[:dmsf_folder][:dmsf_folder_id] @folder.project = @project @folder.user = User.current - + # Custom fields if params[:dmsf_folder][:custom_field_values].present? params[:dmsf_folder][:custom_field_values].each_with_index do |v, i| @folder.custom_field_values[i].value = v[1] end end - + saved = @folder.save - + respond_to do |format| format.js - format.api { - unless saved - render_validation_errors(@folder) - end + format.api { + unless saved + render_validation_errors(@folder) + end } - format.html { + format.html { if saved - flash[:notice] = l(:notice_folder_created) + flash[:notice] = l(:notice_folder_created) redirect_to dmsf_folder_path(:id => @project, :folder_id => @folder) else @pathfolder = @parent render :action => 'edit' end } - end - + end + end def edit - @parent = @folder.folder + @parent = @folder.dmsf_folder @pathfolder = copy_folder(@folder) + @force_file_unlock_allowed = User.current.allowed_to?(:force_file_unlock, @project) end def save - unless params[:dmsf_folder] + unless params[:dmsf_folder] redirect_to dmsf_folder_path(:id => @project, :folder_id => @folder) return end - @pathfolder = copy_folder(@folder) + @pathfolder = copy_folder(@folder) @folder.title = params[:dmsf_folder][:title].strip @folder.description = params[:dmsf_folder][:description].strip @folder.dmsf_folder_id = params[:dmsf_folder][:dmsf_folder_id] - + # Custom fields if params[:dmsf_folder][:custom_field_values].present? params[:dmsf_folder][:custom_field_values].each_with_index do |v, i| @folder.custom_field_values[i].value = v[1] end end - + if @folder.save - flash[:notice] = l(:notice_folder_details_were_saved) + flash[:notice] = l(:notice_folder_details_were_saved) redirect_to dmsf_folder_path(:id => @project, :folder_id => @folder) else render :action => 'edit' @@ -332,10 +334,10 @@ class DmsfController < ApplicationController else flash[:error] = @folder.errors.full_messages.to_sentence end - if commit + if commit || @tree_view redirect_to :back else - redirect_to dmsf_folder_path(:id => @project, :folder_id => @folder.folder) + redirect_to dmsf_folder_path(:id => @project, :folder_id => @folder.dmsf_folder) end end @@ -352,10 +354,15 @@ class DmsfController < ApplicationController end def save_root - @project.dmsf_description = params[:project][:dmsf_description] - @project.save! - flash[:notice] = l(:notice_folder_details_were_saved) - redirect_to :controller => 'dmsf', :action => 'show', :id => @project + if params[:project] + @project.dmsf_description = params[:project][:dmsf_description] + if @project.save + flash[:notice] = l(:notice_folder_details_were_saved) + else + flash[:error] = @project.errors.full_messages.to_sentence + end + end + redirect_to dmsf_folder_path(:id => @project) end def notify_activate @@ -442,11 +449,11 @@ class DmsfController < ApplicationController end zip.files.each do |f| - log_activity(f, 'emailing zip') + log_activity(f, 'emailing zip') audit = DmsfFileRevisionAccess.new audit.user = User.current - audit.revision = f.last_revision - audit.action = DmsfFileRevisionAccess::EmailAction + audit.dmsf_file_revision = f.last_revision + audit.action = DmsfFileRevisionAccess::EmailAction audit.save! end @@ -469,11 +476,11 @@ class DmsfController < ApplicationController zip_entries(zip, selected_folders, selected_files) zip.files.each do |f| - log_activity(f, 'download zip') + log_activity(f, 'download zip') audit = DmsfFileRevisionAccess.new audit.user = User.current - audit.revision = f.last_revision - audit.action = DmsfFileRevisionAccess::DownloadAction + audit.dmsf_file_revision = f.last_revision + audit.action = DmsfFileRevisionAccess::DownloadAction audit.save! end @@ -481,7 +488,7 @@ class DmsfController < ApplicationController :filename => filename_for_content_disposition("#{@project.name}-#{DateTime.now.strftime('%y%m%d%H%M%S')}.zip"), :type => 'application/zip', :disposition => 'attachment') - rescue Exception + rescue Exception raise ensure zip.close if zip @@ -494,7 +501,7 @@ class DmsfController < ApplicationController selected_folders.each do |selected_folder_id| folder = DmsfFolder.visible.find_by_id selected_folder_id if folder - zip.add_folder(folder, member, (folder.folder.dmsf_path_str if folder.folder)) + zip.add_folder(folder, member, (folder.dmsf_folder.dmsf_path_str if folder.dmsf_folder)) else raise FileNotFound end @@ -503,19 +510,18 @@ class DmsfController < ApplicationController if selected_files && selected_files.is_a?(Array) selected_files.each do |selected_file_id| file = DmsfFile.visible.find_by_id selected_file_id + unless file && file.last_revision && File.exists?(file.last_revision.disk_file) + raise FileNotFound + end unless (file.project == @project) || User.current.allowed_to?(:view_dmsf_files, file.project) raise DmsfAccessError end - if file && file.last_revision && File.exists?(file.last_revision.disk_file) - zip.add_file(file, member, (file.folder.dmsf_path_str if file.folder)) if file - else - raise FileNotFound - end + zip.add_file(file, member, (file.dmsf_folder.dmsf_path_str if file.dmsf_folder)) if file end end max_files = Setting.plugin_redmine_dmsf['dmsf_max_file_download'].to_i if max_files > 0 && zip.files.length > max_files - raise ZipMaxFilesError, zip.files.length + raise ZipMaxFilesError#, zip.files.length end zip end @@ -563,6 +569,7 @@ class DmsfController < ApplicationController if folder unless folder.delete commit flash[:error] = folder.errors.full_messages.to_sentence + return end elsif !commit raise FileNotFound @@ -589,9 +596,17 @@ class DmsfController < ApplicationController log_activity(f, 'deleted') end begin - DmsfMailer.get_notify_users(@project, deleted_files).each do |u| + recipients = DmsfMailer.get_notify_users(@project, deleted_files) + recipients.each do |u| DmsfMailer.files_deleted(u, @project, deleted_files).deliver end + if Setting.plugin_redmine_dmsf[:dmsf_display_notified_recipients] == '1' + unless recipients.empty? + to = recipients.collect{ |r| r.name }.first(DMSF_MAX_NOTIFICATION_RECEIVERS_INFO).join(', ') + to << ((recipients.count > DMSF_MAX_NOTIFICATION_RECEIVERS_INFO) ? ',...' : '.') + flash[:warning] = l(:warning_email_notifications, :to => to) + end + end rescue Exception => e Rails.logger.error "Could not send email notifications: #{e.message}" end @@ -614,7 +629,7 @@ class DmsfController < ApplicationController rescue DmsfAccessError render_403 rescue ActiveRecord::RecordNotFound - render_404 + render_404 end def find_parent @@ -625,18 +640,15 @@ class DmsfController < ApplicationController render_404 end + def tree_view + tag = params[:custom_field_id].present? && params[:custom_value].present? + @tree_view = (User.current.pref[:dmsf_tree_view] == '1') && (!%w(atom xml json).include?(params[:format])) && !tag + end + def copy_folder(folder) copy = folder.clone copy.id = folder.id copy end - private - - def e_params - params.fetch(:email, {}).permit( - :to, :zipped_content, :email, - :cc, :subject, :zipped_content => [], :files => []) - end - end \ No newline at end of file diff --git a/app/controllers/dmsf_files_controller.rb b/app/controllers/dmsf_files_controller.rb index 27bfb5a3..6a5b6a7e 100644 --- a/app/controllers/dmsf_files_controller.rb +++ b/app/controllers/dmsf_files_controller.rb @@ -3,7 +3,7 @@ # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,7 +26,8 @@ class DmsfFilesController < ApplicationController before_filter :find_file, :except => [:delete_revision] before_filter :find_revision, :only => [:delete_revision] - before_filter :authorize + before_filter :authorize + before_filter :tree_view, :only => [:delete] accept_api_auth :show @@ -39,21 +40,21 @@ class DmsfFilesController < ApplicationController @revision = @file.last_revision else @revision = DmsfFileRevision.find(params[:download].to_i) - raise DmsfAccessError if @revision.file != @file + raise DmsfAccessError if @revision.dmsf_file != @file end - check_project(@revision.file) - raise ActionController::MissingFile if @file.deleted - log_activity('downloaded') + check_project(@revision.dmsf_file) + raise ActionController::MissingFile if @file.deleted? + log_activity('downloaded') access = DmsfFileRevisionAccess.new access.user = User.current - access.revision = @revision - access.action = DmsfFileRevisionAccess::DownloadAction + access.dmsf_file_revision = @revision + access.action = DmsfFileRevisionAccess::DownloadAction access.save! member = Member.where(:user_id => User.current.id, :project_id => @file.project.id).first - send_file(@revision.disk_file, + send_file(@revision.disk_file, :filename => filename_for_content_disposition(@revision.formatted_name(member ? member.title_format : nil)), :type => @revision.detect_content_type, - :disposition => 'inline') + :disposition => 'inline') rescue DmsfAccessError => e Rails.logger.error e.message render_403 @@ -71,18 +72,18 @@ class DmsfFilesController < ApplicationController @revision = @file.last_revision else @revision = DmsfFileRevision.find(params[:download].to_i) - raise DmsfAccessError if @revision.file != @file + raise DmsfAccessError if @revision.dmsf_file != @file end - check_project(@revision.file) - raise ActionController::MissingFile if @revision.file.deleted - log_activity('downloaded') + check_project(@revision.dmsf_file) + raise ActionController::MissingFile if @revision.dmsf_file.deleted? + log_activity('downloaded') access = DmsfFileRevisionAccess.new access.user = User.current - access.revision = @revision + access.dmsf_file_revision = @revision access.action = DmsfFileRevisionAccess::DownloadAction access.save! member = Member.where(:user_id => User.current.id, :project_id => @file.project.id).first - send_file(@revision.disk_file, + send_file(@revision.disk_file, :filename => filename_for_content_disposition(@revision.formatted_name(member ? member.title_format : nil)), :type => @revision.detect_content_type, :disposition => 'attachment') @@ -98,7 +99,8 @@ class DmsfFilesController < ApplicationController @revision = @file.last_revision @file_delete_allowed = User.current.allowed_to?(:file_delete, @project) - @revision_pages = Paginator.new @file.revisions.visible.count, params['per_page'] ? params['per_page'].to_i : 25, params['page'] + @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'] respond_to do |format| format.html { @@ -119,7 +121,7 @@ class DmsfFilesController < ApplicationController revision.description = params[:dmsf_file_revision][:description] revision.comment = params[:dmsf_file_revision][:comment] - revision.file = @file + revision.dmsf_file = @file last_revision = @file.last_revision revision.source_revision = last_revision revision.user = User.current @@ -127,29 +129,29 @@ class DmsfFilesController < ApplicationController revision.major_version = last_revision.major_version revision.minor_version = last_revision.minor_version version = params[:version].to_i + if version == 3 + revision.major_version = params[:custom_version_major].to_i + revision.minor_version = params[:custom_version_minor].to_i + else + revision.increase_version(version) + end file_upload = params[:file_upload] unless file_upload - revision.disk_filename = last_revision.disk_filename - if version == 3 - revision.major_version = params[:custom_version_major].to_i - revision.minor_version = params[:custom_version_minor].to_i - else - revision.increase_version(version, false) - end - revision.mime_type = last_revision.mime_type revision.size = last_revision.size - else - if version == 3 - revision.major_version = params[:custom_version_major].to_i - revision.minor_version = params[:custom_version_minor].to_i + revision.disk_filename = last_revision.disk_filename + revision.mime_type = last_revision.mime_type + if last_revision.digest.blank? + revision.digest = DmsfFileRevision.create_digest last_revision.disk_file else - revision.increase_version(version, true) + revision.digest = last_revision.digest end + else revision.size = file_upload.size revision.disk_filename = revision.new_storage_filename revision.mime_type = Redmine::MimeType.of(file_upload.original_filename) + revision.digest = DmsfFileRevision.create_digest file_upload.path end - + # Custom fields if params[:dmsf_file_revision][:custom_field_values].present? params[:dmsf_file_revision][:custom_field_values].each_with_index do |v, i| @@ -195,7 +197,7 @@ class DmsfFilesController < ApplicationController flash[:error] = @file.errors.full_messages.join(', ') end else - flash[:error] = revision.errors.full_messages.join(', ') + flash[:error] = revision.errors.full_messages.join(', ') end end end @@ -210,35 +212,39 @@ class DmsfFilesController < ApplicationController if commit log_activity('deleted') begin - DmsfMailer.get_notify_users(@project, [@file]).each do |u| + recipients = DmsfMailer.get_notify_users(@project, [@file]) + recipients.each do |u| DmsfMailer.files_deleted(u, @project, [@file]).deliver end + if Setting.plugin_redmine_dmsf[:dmsf_display_notified_recipients] == '1' + unless recipients.empty? + to = recipients.collect{ |r| r.name }.first(DMSF_MAX_NOTIFICATION_RECEIVERS_INFO).join(', ') + to << ((recipients.count > DMSF_MAX_NOTIFICATION_RECEIVERS_INFO) ? ',...' : '.') + flash[:warning] = l(:warning_email_notifications, :to => to) + end + end rescue Exception => e Rails.logger.error "Could not send email notifications: #{e.message}" end end else - @file.errors.each do |e, msg| - flash[:error] = msg - end + flash[:error] = @file.errors.full_messages.join(', ') end end - if commit + if commit || @tree_view redirect_to :back else - redirect_to dmsf_folder_path(:id => @project, :folder_id => @file.folder) + redirect_to dmsf_folder_path(:id => @project, :folder_id => @file.dmsf_folder) end end def delete_revision - if @revision # && !@revision.deleted + if @revision if @revision.delete(true) flash[:notice] = l(:notice_revision_deleted) log_activity('deleted') else - @revision.errors.each do |e, msg| - flash[:error] = msg - end + flash[:error] = @revision.errors.full_messages.join(', ') end end redirect_to :action => 'show', :id => @file @@ -248,8 +254,12 @@ class DmsfFilesController < ApplicationController if @file.locked? flash[:warning] = l(:warning_file_already_locked) else - @file.lock! - flash[:notice] = l(:notice_file_locked) + begin + @file.lock! + flash[:notice] = l(:notice_file_locked) + rescue Exception => e + flash[:error] = e.message + end end redirect_to :back end @@ -259,8 +269,12 @@ class DmsfFilesController < ApplicationController flash[:warning] = l(:warning_file_not_locked) else if @file.locks[0].user == User.current || User.current.allowed_to?(:force_file_unlock, @file.project) - @file.unlock! - flash[:notice] = l(:notice_file_unlocked) + begin + @file.unlock! + flash[:notice] = l(:notice_file_unlocked) + rescue Exception => e + flash[:error] = e.message + end else flash[:error] = l(:error_only_user_that_locked_file_can_unlock_it) end @@ -300,11 +314,6 @@ class DmsfFilesController < ApplicationController private - def frev_params - params.require(:dmsf_file_revision).permit( - :title, :name, :description, :comment) - end - def log_activity(action) Rails.logger.info "#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} #{User.current.login}@#{request.remote_ip}/#{request.env['HTTP_X_FORWARDED_FOR']}: #{action} dmsf://#{@file.project.identifier}/#{@file.id}/#{@revision.id if @revision}" end @@ -318,16 +327,21 @@ class DmsfFilesController < ApplicationController def find_revision @revision = DmsfFileRevision.visible.find params[:id] - @file = @revision.file + @file = @revision.dmsf_file @project = @file.project rescue ActiveRecord::RecordNotFound render_404 end - + def check_project(entry) if entry && entry.project != @project raise DmsfAccessError, l(:error_entry_project_does_not_match_current_project) end end -end + def tree_view + tag = params[:custom_field_id].present? && params[:custom_value].present? + @tree_view = (User.current.pref[:dmsf_tree_view] == '1') && (!%w(atom xml json).include?(params[:format])) && !tag + end + +end \ No newline at end of file diff --git a/app/controllers/dmsf_files_copy_controller.rb b/app/controllers/dmsf_files_copy_controller.rb index 5213e3ff..85d2c60a 100644 --- a/app/controllers/dmsf_files_copy_controller.rb +++ b/app/controllers/dmsf_files_copy_controller.rb @@ -18,9 +18,9 @@ class DmsfFilesCopyController < ApplicationController unloadable - + menu_item :dmsf - + before_filter :find_file before_filter :authorize @@ -34,10 +34,10 @@ class DmsfFilesCopyController < ApplicationController else @target_project ||= DmsfFile.allowed_target_projects_on_copy[0] end - + @target_folder = DmsfFolder.visible.find(params[:target_folder_id]) unless params[:target_folder_id].blank? - @target_folder ||= @file.folder if @target_project == @project - + @target_folder ||= @file.dmsf_folder if @target_project == @project + render :layout => !request.xhr? end @@ -47,20 +47,20 @@ class DmsfFilesCopyController < ApplicationController render_403 return end - @target_folder = DmsfFolder.visible.find(params[:target_folder_id]) unless params[:target_folder_id].blank? - if !@target_folder.nil? && @target_folder.project != @target_project - raise DmsfAccessError, l(:error_entry_project_does_not_match_current_project) + @target_folder = DmsfFolder.visible.find_by_id(params[:target_folder_id]) unless params[:target_folder_id].blank? + if @target_folder && (@target_folder.project != @target_project) + raise DmsfAccessError, l(:error_entry_project_does_not_match_current_project) end - if (@target_folder && @target_folder == @file.folder) || - (@target_folder.nil? && @file.folder.nil? && @target_project == @file.project) + if (@target_folder && @target_folder == @file.dmsf_folder) || + (@target_folder.nil? && @file.dmsf_folder.nil? && @target_project == @file.project) flash[:error] = l(:error_target_folder_same) redirect_to :action => 'new', :id => @file, :target_project_id => @target_project, :target_folder_id => @target_folder return end new_file = @file.copy_to(@target_project, @target_folder) - + unless new_file.errors.empty? flash[:error] = "#{l(:error_file_cannot_be_copied)}: #{new_file.errors.full_messages.join(', ')}" redirect_to :action => 'new', :id => @file, :target_project_id => @target_project, :target_folder_id => @target_folder @@ -68,8 +68,8 @@ class DmsfFilesCopyController < ApplicationController end flash[:notice] = l(:notice_file_copied) - log_activity(new_file, 'was copied (is copy)') - + log_activity(new_file, 'was copied (is copy)') + redirect_to dmsf_file_path(new_file) end @@ -81,11 +81,11 @@ class DmsfFilesCopyController < ApplicationController end @target_folder = DmsfFolder.visible.find(params[:target_folder_id]) unless params[:target_folder_id].blank? if @target_folder && @target_folder.project != @target_project - raise DmsfAccessError, l(:error_entry_project_does_not_match_current_project) + raise DmsfAccessError, l(:error_entry_project_does_not_match_current_project) end - if (@target_folder && @target_folder == @file.folder) || - (@target_folder.nil? && @file.folder.nil? && @target_project == @file.project) + if (@target_folder && @target_folder == @file.dmsf_folder) || + (@target_folder.nil? && @file.dmsf_folder.nil? && @target_project == @file.project) flash[:error] = l(:error_target_folder_same) redirect_to :action => 'new', :id => @file, :target_project_id => @target_project, :target_folder_id => @target_folder return @@ -98,14 +98,14 @@ class DmsfFilesCopyController < ApplicationController end @file.reload - + flash[:notice] = l(:notice_file_moved) - log_activity(@file, 'was moved (is copy)') - + log_activity(@file, 'was moved (is copy)') + redirect_to dmsf_file_path(@file) end - private +private def log_activity(file, action) Rails.logger.info "#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} #{User.current.login}@#{request.remote_ip}/#{request.env['HTTP_X_FORWARDED_FOR']}: #{action} dmsf://#{file.project.identifier}/#{file.id}/#{file.last_revision.id}" @@ -115,5 +115,5 @@ class DmsfFilesCopyController < ApplicationController @file = DmsfFile.visible.find(params[:id]) @project = @file.project end - + end diff --git a/app/controllers/dmsf_folders_copy_controller.rb b/app/controllers/dmsf_folders_copy_controller.rb index 3b53ae82..e3d685bd 100644 --- a/app/controllers/dmsf_folders_copy_controller.rb +++ b/app/controllers/dmsf_folders_copy_controller.rb @@ -18,9 +18,9 @@ class DmsfFoldersCopyController < ApplicationController unloadable - + menu_item :dmsf - + before_filter :find_folder before_filter :authorize @@ -32,10 +32,10 @@ class DmsfFoldersCopyController < ApplicationController else @target_project ||= DmsfFolder.allowed_target_projects_on_copy[0] end - + @target_folder = DmsfFolder.visible.find(params[:target_folder_id]) unless params[:target_folder_id].blank? - @target_folder ||= @folder.folder if @target_project == @project - + @target_folder ||= @folder.dmsf_folder if @target_project == @project + render :layout => !request.xhr? end @@ -47,18 +47,18 @@ class DmsfFoldersCopyController < ApplicationController end @target_folder = DmsfFolder.visible.find(params[:target_folder_id]) unless params[:target_folder_id].blank? if !@target_folder.nil? && @target_folder.project != @target_project - raise DmsfAccessError, l(:error_entry_project_does_not_match_current_project) + raise DmsfAccessError, l(:error_entry_project_does_not_match_current_project) end - if (@target_folder && @target_folder == @folder.folder) || - (@target_folder.nil? && @folder.folder.nil? && @target_project == @folder.project) + if (@target_folder && @target_folder == @folder.dmsf_folder) || + (@target_folder.nil? && @folder.dmsf_folder.nil? && @target_project == @folder.project) flash[:error] = l(:error_target_folder_same) redirect_to :action => 'new', :id => @folder, :target_project_id => @target_project, :target_folder_id => @target_folder return end new_folder = @folder.copy_to(@target_project, @target_folder) - + unless new_folder.errors.empty? flash[:error] = "#{l(:error_folder_cannot_be_copied)}: #{new_folder.errors.full_messages.join(', ')}" redirect_to :action => 'new', :id => @folder, :target_project_id => @target_project, :target_folder_id => @target_folder @@ -66,10 +66,10 @@ class DmsfFoldersCopyController < ApplicationController end new_folder.reload - + flash[:notice] = l(:notice_folder_copied) log_activity(new_folder, 'was copied (is copy)') - + redirect_to dmsf_folder_path(:id => @target_project, :folder_id => new_folder) end @@ -83,5 +83,5 @@ class DmsfFoldersCopyController < ApplicationController @folder = DmsfFolder.visible.find(params[:id]) @project = @folder.project end - + end diff --git a/app/controllers/dmsf_links_controller.rb b/app/controllers/dmsf_links_controller.rb index 9a9a5d8e..519a57f6 100644 --- a/app/controllers/dmsf_links_controller.rb +++ b/app/controllers/dmsf_links_controller.rb @@ -2,7 +2,7 @@ # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,13 +26,13 @@ class DmsfLinksController < ApplicationController before_filter :find_link_project before_filter :authorize - def new + def new @dmsf_link = DmsfLink.new @dmsf_link.project_id = params[:project_id] if params[:dmsf_link].present? # Reload - @dmsf_link.dmsf_folder_id = params[:dmsf_link][:dmsf_folder_id] # TODO: Add stuff in here for external links so that if error occurs, repopulate the same + @dmsf_link.dmsf_folder_id = params[:dmsf_link][:dmsf_folder_id] @dmsf_file_id = params[:dmsf_link][:dmsf_file_id] @type = params[:dmsf_link][:type] @link_external = (@type == 'link_from') && (params[:external_link] == 'true') @@ -53,7 +53,7 @@ class DmsfLinksController < ApplicationController if file folder = DmsfFolder.find_by_id params[:dmsf_link][:target_folder_id] - if (folder && (folder.project_id == @dmsf_link.target_project_id) && folder.files.include?(file)) || folder.nil? + if (folder && (folder.project_id == @dmsf_link.target_project_id) && folder.dmsf_files.include?(file)) || folder.nil? @dmsf_link.name = file.title end end @@ -111,7 +111,7 @@ class DmsfLinksController < ApplicationController @dmsf_link.target_type = DmsfFolder.model_name.to_s end @dmsf_link.name = params[:dmsf_link][:name] - + if @dmsf_link.save flash[:notice] = l(:notice_successful_create) redirect_to dmsf_folder_path(:id => @project.id, :folder_id => @dmsf_link.dmsf_folder_id) diff --git a/app/controllers/dmsf_upload_controller.rb b/app/controllers/dmsf_upload_controller.rb index 6077fcd8..628b38d1 100644 --- a/app/controllers/dmsf_upload_controller.rb +++ b/app/controllers/dmsf_upload_controller.rb @@ -1,5 +1,5 @@ # encoding: utf-8 -# +# # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš @@ -21,16 +21,16 @@ class DmsfUploadController < ApplicationController unloadable - + menu_item :dmsf - + before_filter :find_project before_filter :authorize before_filter :find_folder, :except => [:upload_file, :upload, :commit] - + helper :all helper :dmsf_workflows - + accept_api_auth :upload, :commit def upload_files @@ -38,7 +38,7 @@ class DmsfUploadController < ApplicationController @uploads = [] if uploaded_files && uploaded_files.is_a?(Hash) # standard file input uploads - uploaded_files.each_value do |uploaded_file| + uploaded_files.each_value do |uploaded_file| upload = DmsfUpload.create_from_uploaded_attachment(@project, @folder, uploaded_file) @uploads.push(upload) if upload end @@ -53,40 +53,42 @@ class DmsfUploadController < ApplicationController end end - # async single file upload handling - def upload_file - @tempfile = params[:file] + # async single file upload handling + def upload_file + @tempfile = params[:file] unless @tempfile.original_filename render_404 return end @disk_filename = DmsfHelper.temp_filename(@tempfile.original_filename) + target = "#{DmsfHelper.temp_dir}/#{@disk_filename}" begin - FileUtils.cp @tempfile.path, "#{DmsfHelper.temp_dir}/#{@disk_filename}" + FileUtils.cp @tempfile.path, target + FileUtils.chmod 'u=wr,g=r', target rescue Exception => e Rails.logger.error e.message end - if File.size("#{DmsfHelper.temp_dir}/#{@disk_filename}") <= 0 + if File.size(target) <= 0 begin - File.delete "#{DmsfHelper.temp_dir}/#{@disk_filename}" + File.delete target rescue Exception => e Rails.logger.error e.message end - render :layout => nil, :json => { :jsonrpc => '2.0', - :error => { - :code => 103, - :message => l(:header_minimum_filesize), - :details => l(:error_minimum_filesize, - :file => @tempfile.original_filename.to_s) + render :layout => nil, :json => { :jsonrpc => '2.0', + :error => { + :code => 103, + :message => l(:header_minimum_filesize), + :details => l(:error_minimum_filesize, + :file => @tempfile.original_filename.to_s) } } else render :layout => false end end - + # REST API document upload - def upload + def upload unless request.content_type == 'application/octet-stream' render :nothing => true, :status => 406 return @@ -109,69 +111,70 @@ class DmsfUploadController < ApplicationController } end end - - def commit_files + + def commit_files commit_files_internal params[:commited_files] - end - + end + # REST API file commit def commit - attachments = params[:attachments] + attachments = params[:attachments] if attachments && attachments.is_a?(Hash) @folder = DmsfFolder.visible.find_by_id attachments[:folder_id].to_i if attachments[:folder_id].present? # standard file input uploads uploaded_files = attachments.select { |key, value| key == 'uploaded_file'} - uploaded_files.each_value do |uploaded_file| + uploaded_files.each_value do |uploaded_file| upload = DmsfUpload.create_from_uploaded_attachment(@project, @folder, uploaded_file) uploaded_file[:disk_filename] = upload.disk_filename end end - commit_files_internal uploaded_files + commit_files_internal uploaded_files end private - + def commit_files_internal(commited_files) if commited_files && commited_files.is_a?(Hash) @files = [] failed_uploads = [] commited_files.each_value do |commited_file| name = commited_file[:name] - + new_revision = DmsfFileRevision.new file = DmsfFile.visible.find_file_by_name(@project, @folder, name) unless file link = DmsfLink.find_link_by_file_name(@project, @folder, name) file = link.target_file if link end - + unless file file = DmsfFile.new file.project = @project file.name = name - file.folder = @folder - file.notification = Setting.plugin_redmine_dmsf[:dmsf_default_notifications].present? + file.dmsf_folder = @folder + file.notification = Setting.plugin_redmine_dmsf[:dmsf_default_notifications].present? new_revision.minor_version = 0 new_revision.major_version = 0 else - if file.locked_for_user? - failed_uploads.push(commited_file) - next - end if file.last_revision last_revision = file.last_revision new_revision.source_revision = last_revision new_revision.major_version = last_revision.major_version - new_revision.minor_version = last_revision.minor_version + new_revision.minor_version = last_revision.minor_version else new_revision.minor_version = 0 new_revision.major_version = 0 end end + if file.locked_for_user? + failed_uploads.push(commited_file) + next + end + commited_disk_filepath = "#{DmsfHelper.temp_dir}/#{commited_file[:disk_filename].gsub(/[\/\\]/,'')}" - - new_revision.file = file + + new_revision.dmsf_file = file new_revision.user = User.current new_revision.name = name new_revision.title = commited_file[:title] @@ -182,53 +185,50 @@ class DmsfUploadController < ApplicationController new_revision.major_version = commited_file[:custom_version_major].to_i new_revision.minor_version = commited_file[:custom_version_minor].to_i else - new_revision.increase_version(version, true) + new_revision.increase_version(version) end new_revision.mime_type = Redmine::MimeType.of(new_revision.name) new_revision.size = File.size(commited_disk_filepath) + new_revision.digest = DmsfFileRevision.create_digest commited_disk_filepath - if file.locked? - begin - file.unlock! - flash[:notice] = l(:notice_file_unlocked) - rescue DmsfLockError => e - flash[:error] = e.message - next - end - end - - # Need to save file first to generate id for it in case of creation. - # File id is needed to properly generate revision disk filename + # Need to save file first to generate id for it in case of creation. + # File id is needed to properly generate revision disk filename if commited_file[:dmsf_file_revision].present? commited_file[:dmsf_file_revision][:custom_field_values].each_with_index do |v, i| new_revision.custom_field_values[i].value = v[1] end end - + if new_revision.valid? && file.save new_revision.disk_filename = new_revision.new_storage_filename else failed_uploads.push(commited_file) next end - + if new_revision.save - new_revision.assign_workflow(commited_file[:dmsf_workflow_id]) - FileUtils.mv(commited_disk_filepath, new_revision.disk_file) - file.set_last_revision new_revision - @files.push(file) + new_revision.assign_workflow(commited_file[:dmsf_workflow_id]) + begin + FileUtils.mv(commited_disk_filepath, new_revision.disk_file) + file.set_last_revision new_revision + @files.push(file) + rescue Exception => e + Rails.logger.error e.message + flash[:error] = e.message + failed_uploads.push(file) + end else failed_uploads.push(commited_file) end end - unless @files.empty? - @files.each { |file| log_activity(file, 'uploaded') if file } + unless @files.empty? + @files.each { |file| log_activity(file, 'uploaded') if file } if (@folder && @folder.notification?) || (!@folder && @project.dmsf_notification?) begin recipients = DmsfMailer.get_notify_users(@project, @files) recipients.each do |u| DmsfMailer.files_updated(u, @project, @files).deliver - end + end if Setting.plugin_redmine_dmsf[:dmsf_display_notified_recipients] == '1' unless recipients.empty? to = recipients.collect{ |r| r.name }.first(DMSF_MAX_NOTIFICATION_RECEIVERS_INFO).join(', ') @@ -247,22 +247,22 @@ class DmsfUploadController < ApplicationController end respond_to do |format| format.js - format.api { - render_validation_errors(failed_uploads) unless failed_uploads.empty? + format.api { + render_validation_errors(failed_uploads) unless failed_uploads.empty? } format.html { redirect_to dmsf_folder_path(:id => @project, :folder_id => @folder) } end - + end - + def log_activity(file, action) Rails.logger.info "#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} #{User.current.login}@#{request.remote_ip}/#{request.env['HTTP_X_FORWARDED_FOR']}: #{action} dmsf://#{file.project.identifier}/#{file.id}/#{file.last_revision.id}" end - + def find_folder - @folder = DmsfFolder.visible.find(params[:folder_id]) if params.keys.include?('folder_id') + @folder = DmsfFolder.visible.find(params[:folder_id]) if params.keys.include?('folder_id') rescue DmsfAccessError render_403 end - + end diff --git a/app/controllers/dmsf_workflows_controller.rb b/app/controllers/dmsf_workflows_controller.rb index 126fd262..9646388b 100644 --- a/app/controllers/dmsf_workflows_controller.rb +++ b/app/controllers/dmsf_workflows_controller.rb @@ -1,8 +1,8 @@ # encoding: utf-8 -# +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,54 +19,54 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class DmsfWorkflowsController < ApplicationController - unloadable + unloadable model_object DmsfWorkflow - - before_filter :find_model_object, :except => [:create, :new, :index, :assign, :assignment] + + before_filter :find_model_object, :except => [:create, :new, :index, :assign, :assignment] before_filter :find_project before_filter :authorize_custom - + layout :workflows_layout - - def index - @workflow_pages, @workflows = paginate DmsfWorkflow.global.sorted, :per_page => 25 + + def index + @status = params[:status] || 1 + @workflow_pages, @workflows = paginate DmsfWorkflow.status(@status).global.sorted, :per_page => 25 end - - def action + + def action end - + def new_action if params[:commit] == l(:button_submit) action = DmsfWorkflowStepAction.new( :dmsf_workflow_step_assignment_id => params[:dmsf_workflow_step_assignment_id], :action => (params[:step_action].to_i >= 10) ? DmsfWorkflowStepAction::ACTION_DELEGATE : params[:step_action], - :note => params[:note]) + :note => params[:note]) if request.post? if action.save revision = DmsfFileRevision.find_by_id params[:dmsf_file_revision_id] if revision - if @dmsf_workflow.try_finish revision, action, (params[:step_action].to_i / 10) - file = DmsfFile.joins(:revisions).where(:dmsf_file_revisions => {:id => revision.id}).first - if file + if @dmsf_workflow.try_finish revision, action, (params[:step_action].to_i / 10) + if revision.dmsf_file begin - file.unlock! true + revision.dmsf_file.unlock! true rescue DmsfLockError => e - flash[:info] = e.message + flash[:info] = e.message end - end + end if revision.workflow == DmsfWorkflow::STATE_APPROVED - # Just approved - recipients = DmsfMailer.get_notify_users(@project) + # Just approved + recipients = DmsfMailer.get_notify_users(@project, [revision.dmsf_file]) recipients.each do |user| DmsfMailer.workflow_notification( user, - @dmsf_workflow, + @dmsf_workflow, revision, :text_email_subject_approved, :text_email_finished_approved, :text_email_to_see_history).deliver if user end - if Setting.plugin_redmine_dmsf[:dmsf_display_notified_recipients] == '1' + if Setting.plugin_redmine_dmsf[:dmsf_display_notified_recipients] == '1' unless recipients.blank? to = recipients.collect{ |r| r.name }.first(DMSF_MAX_NOTIFICATION_RECEIVERS_INFO).join(', ') to << ((recipients.count > DMSF_MAX_NOTIFICATION_RECEIVERS_INFO) ? ',...' : '.') @@ -74,15 +74,15 @@ class DmsfWorkflowsController < ApplicationController end end else - # Just rejected + # Just rejected recipients = @dmsf_workflow.participiants recipients.push User.find_by_id revision.dmsf_workflow_assigned_by recipients.uniq! - recipients = recipients & DmsfMailer.get_notify_users(@project) + recipients = recipients & DmsfMailer.get_notify_users(@project, [revision.dmsf_file]) recipients.each do |user| DmsfMailer.workflow_notification( - user, - @dmsf_workflow, + user, + @dmsf_workflow, revision, :text_email_subject_rejected, :text_email_finished_rejected, @@ -99,19 +99,19 @@ class DmsfWorkflowsController < ApplicationController end else if action.action == DmsfWorkflowStepAction::ACTION_DELEGATE - # Delegation + # Delegation delegate = User.find_by_id params[:step_action].to_i / 10 if DmsfMailer.get_notify_users(@project).include?(delegate) DmsfMailer.workflow_notification( - delegate, - @dmsf_workflow, + delegate, + @dmsf_workflow, revision, :text_email_subject_delegated, :text_email_finished_delegated, :text_email_to_proceed, action.note).deliver if Setting.plugin_redmine_dmsf[:dmsf_display_notified_recipients] == '1' - flash[:warning] = l(:warning_email_notifications, :to => delegate.name) + flash[:warning] = l(:warning_email_notifications, :to => delegate.name) end end else @@ -123,19 +123,19 @@ class DmsfWorkflowsController < ApplicationController assignments.each do |assignment| if assignment.user && DmsfMailer.get_notify_users(@project).include?(assignment.user) DmsfMailer.workflow_notification( - assignment.user, - @dmsf_workflow, + assignment.user, + @dmsf_workflow, revision, :text_email_subject_requires_approval, :text_email_finished_step, :text_email_to_proceed).deliver end end - to = User.find_by_id revision.dmsf_workflow_assigned_by + to = User.find_by_id revision.dmsf_workflow_assigned_by if to && DmsfMailer.get_notify_users(@project).include?(to) DmsfMailer.workflow_notification( - to, - @dmsf_workflow, + to, + @dmsf_workflow, revision, :text_email_subject_updated, :text_email_finished_step_short, @@ -152,31 +152,31 @@ class DmsfWorkflowsController < ApplicationController flash[:warning] = l(:warning_email_notifications, :to => to) end end - end + end end end end end flash[:notice] = l(:notice_successful_update) elsif action.action != DmsfWorkflowStepAction::ACTION_APPROVE && action.note.blank? - flash[:error] = l(:error_empty_note) + flash[:error] = l(:error_empty_note) end - end + end end - redirect_to :back + redirect_to :back end - - def assign + + def assign end - + def assignment - if (params[:commit] == l(:button_submit)) && + if (params[:commit] == l(:button_submit)) && params[:dmsf_workflow_id].present? && (params[:dmsf_workflow_id] != '-1') revision = DmsfFileRevision.find_by_id params[:dmsf_file_revision_id] if revision - revision.set_workflow(params[:dmsf_workflow_id], params[:action]) + revision.set_workflow(params[:dmsf_workflow_id], params[:action]) revision.assign_workflow(params[:dmsf_workflow_id]) - if request.post? + if request.post? if revision.save file = DmsfFile.find_by_id revision.dmsf_file_id if file @@ -185,43 +185,44 @@ class DmsfWorkflowsController < ApplicationController rescue DmsfLockError => e logger.warn e.message end - flash[:notice] = l(:notice_successful_update) + flash[:notice] = l(:notice_successful_update) end else flash[:error] = l(:error_workflow_assign) end - end - end + end + end end - redirect_to :back + redirect_to :back end def log end - - def new + + def new @dmsf_workflow = DmsfWorkflow.new - + # Reload if params[:dmsf_workflow] && params[:dmsf_workflow][:name].present? @dmsf_workflow.name = params[:dmsf_workflow][:name] - elsif params[:dmsf_workflow_id].present? - wf = DmsfWorkflow.find_by_id params[:dmsf_workflow_id] + elsif params[:dmsf_workflow] && params[:dmsf_workflow][:id].present? + wf = DmsfWorkflow.find_by_id params[:dmsf_workflow][:id] @dmsf_workflow.name = wf.name if wf end - + render :layout => !request.xhr? end - + def create if params[:dmsf_workflow] - if (params[:dmsf_workflow_id].to_i > 0) - wf = DmsfWorkflow.find_by_id params[:dmsf_workflow_id] - @dmsf_workflow = wf.copy_to(@project, params[:dmsf_workflow][:name]) if wf + if (params[:dmsf_workflow][:id].to_i > 0) + wf = DmsfWorkflow.find_by_id params[:dmsf_workflow][:id] + @dmsf_workflow = wf.copy_to(@project, params[:dmsf_workflow][:name]) if wf else @dmsf_workflow = DmsfWorkflow.new @dmsf_workflow.name = params[:dmsf_workflow][:name] @dmsf_workflow.project_id = @project.id if @project + @dmsf_workflow.author = User.current @dmsf_workflow.save end end @@ -232,29 +233,34 @@ class DmsfWorkflowsController < ApplicationController else redirect_to dmsf_workflows_path end - else + else render :action => 'new' end end - - def update - if params[:dmsf_workflow] && @dmsf_workflow.update_attributes( - {:name => params[:dmsf_workflow][:name]}) - flash[:notice] = l(:notice_successful_update) - if @project - redirect_to settings_project_path(@project, :tab => 'dmsf_workflow') + + def update + if params[:dmsf_workflow] + res = @dmsf_workflow.update_attributes({:name => params[:dmsf_workflow][:name]}) if params[:dmsf_workflow][:name].present? + res = @dmsf_workflow.update_attributes({:status => params[:dmsf_workflow][:status]}) if params[:dmsf_workflow][:status].present? + if res + flash[:notice] = l(:notice_successful_update) + if @project + redirect_to settings_project_path(@project, :tab => 'dmsf_workflow') + else + redirect_to dmsf_workflows_path + end else - redirect_to dmsf_workflows_path - end + flash[:error] = @dmsf_workflow.errors.full_messages.to_sentence + redirect_to dmsf_workflow_path(@dmsf_workflow) + end else - flash[:error] = @dmsf_workflow.errors.full_messages.to_sentence redirect_to dmsf_workflow_path(@dmsf_workflow) end end - - def destroy + + def destroy begin - @dmsf_workflow.destroy + @dmsf_workflow.destroy flash[:notice] = l(:notice_successful_delete) rescue flash[:error] = l(:error_unable_delete_dmsf_workflow) @@ -263,33 +269,33 @@ class DmsfWorkflowsController < ApplicationController redirect_to settings_project_path(@project, :tab => 'dmsf_workflow') else redirect_to dmsf_workflows_path - end + end end - - def autocomplete_for_user + + def autocomplete_for_user render :layout => false end - + def new_step @steps = @dmsf_workflow.dmsf_workflow_steps.collect{|s| s.step}.uniq respond_to do |format| - format.html + format.html format.js - end + end end - - def add_step - if request.post? + + def add_step + if request.post? if params[:step] == '0' - step = @dmsf_workflow.dmsf_workflow_steps.collect{|s| s.step}.uniq.count + 1 + step = @dmsf_workflow.dmsf_workflow_steps.collect{|s| s.step}.uniq.count + 1 else step = params[:step].to_i end operator = (params[:commit] == l(:dmsf_and)) ? DmsfWorkflowStep::OPERATOR_AND : DmsfWorkflowStep::OPERATOR_OR users = User.where(:id => params[:user_ids]).to_a if users.count > 0 - users.each do |user| + users.each do |user| ws = DmsfWorkflowStep.new ws.dmsf_workflow_id = @dmsf_workflow.id ws.step = step @@ -303,49 +309,52 @@ class DmsfWorkflowsController < ApplicationController end else flash[:error] = l(:error_workflow_assign) - end - end + end + end respond_to do |format| - format.html - end + format.html + end end - - def remove_step - if request.delete? + + def remove_step + if request.delete? DmsfWorkflowStep.where(:dmsf_workflow_id => @dmsf_workflow.id, :step => params[:step]).each do |ws| @dmsf_workflow.dmsf_workflow_steps.delete(ws) - end + end @dmsf_workflow.dmsf_workflow_steps.each do |ws| n = ws.step.to_i - if n > params[:step].to_i + if n > params[:step].to_i ws.step = n - 1 unless ws.save flash[:error] = l(:notice_cannot_renumber_steps) end end - end - end + end + end respond_to do |format| - format.html + format.html end end - - def reorder_steps + + def reorder_steps if request.put? - unless @dmsf_workflow.reorder_steps(params[:step].to_i, params[:workflow_step][:move_to]) + unless @dmsf_workflow.reorder_steps(params[:step].to_i, params[:dmsf_workflow][:position].to_i) flash[:error] = l(:notice_cannot_renumber_steps) - end - end + end + end respond_to do |format| - format.html + format.html + format.js { + render inline: "location.replace('#{dmsf_workflow_path(@dmsf_workflow)}');" + } end end - + def start revision = DmsfFileRevision.find_by_id(params[:dmsf_file_revision_id]) - if revision + if revision revision.set_workflow(@dmsf_workflow.id, params[:action]) - if revision.save + if revision.save assignments = @dmsf_workflow.next_assignments revision.id recipients = assignments.collect{ |a| a.user } recipients.uniq! @@ -353,13 +362,13 @@ class DmsfWorkflowsController < ApplicationController recipients.each do |user| DmsfMailer.workflow_notification( user, - @dmsf_workflow, + @dmsf_workflow, revision, :text_email_subject_started, :text_email_started, :text_email_to_proceed).deliver end - if Setting.plugin_redmine_dmsf[:dmsf_display_notified_recipients] == '1' + if Setting.plugin_redmine_dmsf[:dmsf_display_notified_recipients] == '1' unless recipients.blank? to = recipients.collect{ |r| r.name }.first(DMSF_MAX_NOTIFICATION_RECEIVERS_INFO).join(', ') to << ((recipients.count > DMSF_MAX_NOTIFICATION_RECEIVERS_INFO) ? ',...' : '.') @@ -369,35 +378,37 @@ class DmsfWorkflowsController < ApplicationController flash[:notice] = l(:notice_workflow_started) else flash[:error] = l(:notice_cannot_start_workflow) - end + end end redirect_to :back end - -private - def find_project +private + + def find_project if @dmsf_workflow if @dmsf_workflow.project # Project workflow @project = @dmsf_workflow.project else # Global workflow revision = DmsfFileRevision.find_by_id params[:dmsf_file_revision_id] - @project = revision.file.project if revision && revision.file + @project = revision.dmsf_file.project if revision && revision.dmsf_file end else - if params[:project_id].present? + if params[:dmsf_workflow] + @project = Project.find_by_id params[:dmsf_workflow][:project_id] + elsif params[:project_id] @project = Project.find_by_id params[:project_id] else @project = Project.find_by_identifier params[:id] end - end + end end - - def workflows_layout + + def workflows_layout @project ? 'base' : 'admin' - end - - def authorize_custom + end + + def authorize_custom if @project authorize else diff --git a/app/helpers/dmsf_helper.rb b/app/helpers/dmsf_helper.rb index e06e403d..a5f33c6c 100644 --- a/app/helpers/dmsf_helper.rb +++ b/app/helpers/dmsf_helper.rb @@ -1,6 +1,9 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -33,7 +36,7 @@ module DmsfHelper end "#{timestamp}_#{filename}" end - + def self.sanitize_filename(filename) # get only the filename, not the whole path just_filename = File.basename(filename.gsub('\\\\', '/')) @@ -46,10 +49,10 @@ module DmsfHelper extension = $1 if just_filename =~ %r{(\.[a-zA-Z0-9]+)$} just_filename = Digest::MD5.hexdigest(just_filename) << extension end - + just_filename end - + def self.filetype_css(filename) extension = File.extname(filename) extension = extension[1, extension.length-1] @@ -60,20 +63,44 @@ module DmsfHelper end end - def plugin_asset_path(plugin, asset_type, source) - return "#{Redmine::Utils.relative_url_root}/plugin_assets/#{plugin}/#{asset_type}/#{source}" + def plugin_asset_path(plugin, asset_type, source) + return "#{Redmine::Utils.relative_url_root}/plugin_assets/#{plugin}/#{asset_type}/#{source}" end - def self.to_time(obj) - #Right, enough of bugs, let's try a better approach here. - return if !obj - return obj.to_time(ActiveRecord::Base.default_timezone) if obj.is_a?(String) + def self.dmsf_tree(parent, obj, tree = nil) + tree ||= [] + # Folders && files && links + nodes = obj.dmsf_folders.visible + obj.dmsf_links.visible + obj.dmsf_files.visible + # Alphabetical and type sort + nodes.sort! do |x, y| + if ((x.is_a?(DmsfFolder) || (x.is_a?(DmsfLink) && x.is_folder?)) && + (y.is_a?(DmsfFile) || (y.is_a?(DmsfLink) && y.is_file?))) + -1 + elsif ((x.is_a?(DmsfFile) || (x.is_a?(DmsfLink) && x.is_file?)) && + (y.is_a?(DmsfFolder) || (y.is_a?(DmsfLink) && y.is_folder?))) + 1 + else + x.title.downcase <=> y.title.downcase + end + end + # Create the tree + nodes.each do |node| + local_parent = node.dmsf_folder + level = 0 + while local_parent && (local_parent != parent) + level += 1 + local_parent = local_parent.dmsf_folder + end + position = tree.size + # Files of the same parent and on the same level have the same position + if position > 0 && tree[-1][1] == level && + (tree[-1][0].is_a?(DmsfFile) || (tree[-1][0].is_a?(DmsfLink) && tree[-1][0].is_file?)) + position = tree[-1][2] + end + tree << [node, level, position] + self.dmsf_tree(parent, node, tree) if node.is_a?(DmsfFolder) + end + tree + end - # Why can't Mysql::Time conform to time object? - without a utc? method it breaks redmine's - # rendering method, so we convert it to string, and back into time - not the most efficient - # of methods - however seems functional. Not sure if MySQL - return obj.to_s.to_time(ActiveRecord::Base.default_timezone) if obj.class.name == 'Mysql::Time' - return obj - end - end diff --git a/app/helpers/dmsf_links_helper.rb b/app/helpers/dmsf_links_helper.rb index 9a1a89a7..ad704e67 100644 --- a/app/helpers/dmsf_links_helper.rb +++ b/app/helpers/dmsf_links_helper.rb @@ -1,6 +1,8 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2014 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/dmsf_workflows_helper.rb b/app/helpers/dmsf_workflows_helper.rb index d930bb76..15b9d44b 100644 --- a/app/helpers/dmsf_workflows_helper.rb +++ b/app/helpers/dmsf_workflows_helper.rb @@ -2,7 +2,7 @@ # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,31 +19,33 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. module DmsfWorkflowsHelper - + def render_principals_for_new_dmsf_workflow_users(workflow, dmsf_workflow_step_assignment_id, dmsf_file_revision_id) scope = workflow.delegates(params[:q], dmsf_workflow_step_assignment_id, dmsf_file_revision_id) principal_count = scope.count principal_pages = Redmine::Pagination::Paginator.new principal_count, 10, params['page'] principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).all - - if dmsf_workflow_step_assignment_id + + # Delegation + if dmsf_workflow_step_assignment_id s = content_tag('div', - content_tag('div', principals_radio_button_tags('user_ids[]', principals), :id => 'users_for_delegate'), + content_tag('div', principals_radio_button_tags('step_action', principals), :id => 'users_for_delegate'), :class => 'objects-selection') - else + # New step + else s = content_tag('div', content_tag('div', principals_check_box_tags('user_ids[]', principals), :id => 'users'), :class => 'objects-selection') end links = pagination_links_full(principal_pages, principal_count, :per_page_links => false) {|text, parameters, options| - link_to text, autocomplete_for_user_dmsf_workflow_path(workflow, parameters.merge(:q => params[:q], :format => 'js')), :remote => true + link_to text, autocomplete_for_user_dmsf_workflow_path(workflow, parameters.merge(:q => params[:q], :format => 'js')), :remote => true } s + content_tag('p', links, :class => 'pagination') - s.html_safe + s.html_safe end - + def dmsf_workflow_steps_options_for_select(steps) options = Array.new options << [l(:dmsf_new_step), 0] @@ -51,25 +53,25 @@ module DmsfWorkflowsHelper options << [step.to_s, step] end options_for_select(options, 0) - end - + end + def dmsf_workflows_for_select(project, dmsf_workflow_id) - options = Array.new + options = Array.new options << ['', -1] - DmsfWorkflow.sorted.where(['project_id = ? OR project_id IS NULL', project.id]).each do |wf| + DmsfWorkflow.active.sorted.where(['project_id = ? OR project_id IS NULL', project.id]).each do |wf| if wf.project_id options << [wf.name, wf.id] else options << ["#{wf.name} (global)", wf.id] end - end + end options_for_select(options, :selected => dmsf_workflow_id) - end - + end + def dmsf_all_workflows_for_select(dmsf_workflow_id) - options = Array.new - options << ['', 0] - DmsfWorkflow.sorted.all.each do |wf| + options = Array.new + options << ['', 0] + DmsfWorkflow.active.sorted.all.each do |wf| if wf.project_id prj = Project.find_by_id wf.project_id if User.current.allowed_to?(:manage_workflows, prj) @@ -83,16 +85,33 @@ module DmsfWorkflowsHelper else # Global approval workflows options << [wf.name, wf.id] - end - end + end + end options_for_select(options, :selected => dmsf_workflow_id) - end - + end + def principals_radio_button_tags(name, principals) s = '' principals.each do |principal| s << "\n" end - s.html_safe + s.html_safe end -end + + def change_status_link(workflow) + url = { :controller => 'dmsf_workflows', :action => 'update', :id => workflow.id } + if workflow.locked? + link_to l(:button_unlock), url.merge(:dmsf_workflow => {:status => DmsfWorkflow::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock' + else + link_to l(:button_lock), url.merge(:dmsf_workflow => {:status => DmsfWorkflow::STATUS_LOCKED}), :method => :put, :class => 'icon icon-lock' + end + end + + def workflows_status_options_for_select(selected) + worflows_count_by_status = DmsfWorkflow.global.group('status').count.to_hash + options_for_select([[l(:label_all), ''], + ["#{l(:status_active)} (#{worflows_count_by_status[DmsfWorkflow::STATUS_ACTIVE].to_i})", DmsfWorkflow::STATUS_ACTIVE.to_s], + ["#{l(:status_locked)} (#{worflows_count_by_status[DmsfWorkflow::STATUS_LOCKED].to_i})", DmsfWorkflow::STATUS_LOCKED.to_s]], selected.to_s) + end + +end \ No newline at end of file diff --git a/app/models/dmsf_file.rb b/app/models/dmsf_file.rb index b975d7c7..bd8b2a3c 100644 --- a/app/models/dmsf_file.rb +++ b/app/models/dmsf_file.rb @@ -29,28 +29,25 @@ end class DmsfFile < ActiveRecord::Base unloadable - - include RedmineDmsf::Lockable + + include RedmineDmsf::Lockable belongs_to :project - belongs_to :folder, :class_name => 'DmsfFolder', :foreign_key => 'dmsf_folder_id' + belongs_to :dmsf_folder belongs_to :deleted_by_user, :class_name => 'User', :foreign_key => 'deleted_by_user_id' - - has_many :revisions, -> { order("#{DmsfFileRevision.table_name}.major_version DESC, #{DmsfFileRevision.table_name}.minor_version DESC, #{DmsfFileRevision.table_name}.updated_at DESC") }, - :class_name => 'DmsfFileRevision', :foreign_key => 'dmsf_file_id', + + has_many :dmsf_file_revisions, -> { order("#{DmsfFileRevision.table_name}.major_version DESC, #{DmsfFileRevision.table_name}.minor_version DESC, #{DmsfFileRevision.table_name}.updated_at DESC") }, :dependent => :destroy has_many :locks, -> { where(entity_type: 0).order("#{DmsfLock.table_name}.updated_at DESC") }, :class_name => 'DmsfLock', :foreign_key => 'entity_id', :dependent => :destroy has_many :referenced_links, -> { where target_type: DmsfFile.model_name.to_s}, :class_name => 'DmsfLink', :foreign_key => 'target_id', :dependent => :destroy - accepts_nested_attributes_for :revisions, :locks, :referenced_links, :project - - scope :visible, lambda { |*args| - where(deleted: false) - } - scope :deleted, lambda { |*args| - where(deleted: true) - } + + STATUS_DELETED = 1 + STATUS_ACTIVE = 0 + + scope :visible, -> { where(:deleted => STATUS_ACTIVE) } + scope :deleted, -> { where(:deleted => STATUS_DELETED) } validates :name, :presence => true validates_format_of :name, :with => DmsfFolder.invalid_characters, @@ -59,30 +56,30 @@ class DmsfFile < ActiveRecord::Base validate :validates_name_uniqueness def validates_name_uniqueness - existing_file = DmsfFile.visible.find_file_by_name(self.project, self.folder, self.name) + existing_file = DmsfFile.visible.find_file_by_name(self.project, self.dmsf_folder, self.name) errors.add(:name, l('activerecord.errors.messages.taken')) unless existing_file.nil? || existing_file.id == self.id - end + end acts_as_event :title => Proc.new { |o| o.name }, - :description => Proc.new { |o| - desc = Redmine::Search.cache_store.fetch("DmsfFile-#{o.id}") + :description => Proc.new { |o| + desc = Redmine::Search.cache_store.fetch("DmsfFile-#{o.id}") if desc - Redmine::Search.cache_store.delete("DmsfFile-#{o.id}") + Redmine::Search.cache_store.delete("DmsfFile-#{o.id}") else desc = o.description desc += ' / ' if o.description.present? && o.last_revision.comment.present? desc += o.last_revision.comment if o.last_revision.comment.present? - end + end desc }, - :url => Proc.new { |o| {:controller => 'dmsf_files', :action => 'show', :id => o} }, + :url => Proc.new { |o| {:controller => 'dmsf_files', :action => 'view', :id => o} }, :datetime => Proc.new { |o| o.updated_at }, :author => Proc.new { |o| o.last_revision.user } - + acts_as_searchable :columns => ["#{table_name}.name", "#{DmsfFileRevision.table_name}.title", "#{DmsfFileRevision.table_name}.description", "#{DmsfFileRevision.table_name}.comment"], :project_key => 'project_id', - :date_column => "#{table_name}.updated_at" + :date_column => "#{table_name}.updated_at" before_create :default_values def default_values @@ -97,19 +94,23 @@ class DmsfFile < ActiveRecord::Base @@storage_path = nil def self.storage_path - unless @@storage_path.present? - @@storage_path = Setting.plugin_redmine_dmsf['dmsf_storage_directory'].strip if Setting.plugin_redmine_dmsf['dmsf_storage_directory'].present? - @@storage_path = Pathname(Redmine::Configuration['attachments_storage_path']).join('dmsf') if @@storage_path.blank? && Redmine::Configuration['attachments_storage_path'].present? - @@storage_path = Rails.root.join('files/dmsf').to_s if @@storage_path.blank? - Dir.mkdir(@@storage_path) unless File.exists?(@@storage_path) - end - @@storage_path + return @@storage_path if @@storage_path.present? + path = Setting.plugin_redmine_dmsf['dmsf_storage_directory'] + path = Pathname(Redmine::Configuration['attachments_storage_path']).join('dmsf') if path.blank? && Redmine::Configuration['attachments_storage_path'].present? + path = Rails.root.join('files/dmsf').to_s if path.blank? + path.strip if path + path end # Lets introduce a write for storage path, that way we can also # better interact from test-cases etc - def self.storage_path=(obj) - @@storage_path = obj + def self.storage_path=(path) + begin + FileUtils.mkdir_p(path) unless File.exists?(path) + rescue Exception => e + Rails.logger.error e.message + end + @@storage_path = path end def self.find_file_by_name(project, folder, name) @@ -121,7 +122,7 @@ class DmsfFile < ActiveRecord::Base def last_revision unless @last_revision - @last_revision = deleted ? self.revisions.first : self.revisions.visible.first + @last_revision = self.deleted? ? self.dmsf_file_revisions.first : self.dmsf_file_revisions.visible.first end @last_revision end @@ -130,6 +131,10 @@ class DmsfFile < ActiveRecord::Base @last_revision = new_revision end + def deleted? + self.deleted == STATUS_DELETED + end + def delete(commit) if locked_for_user? Rails.logger.info l(:error_file_is_locked) @@ -138,12 +143,11 @@ class DmsfFile < ActiveRecord::Base end begin # Revisions and links of a deleted file SHOULD be deleted too - self.revisions.each { |r| r.delete(commit, true) } - self.referenced_links.each { |l| l.delete(commit) } + self.dmsf_file_revisions.each { |r| r.delete(commit, true) } if commit self.destroy else - self.deleted = true + self.deleted = STATUS_DELETED self.deleted_by_user = User.current save end @@ -155,13 +159,12 @@ class DmsfFile < ActiveRecord::Base end def restore - if self.dmsf_folder_id && (self.folder.nil? || self.folder.deleted) + if self.dmsf_folder_id && (self.dmsf_folder.nil? || self.dmsf_folder.deleted?) errors[:base] << l(:error_parent_folder) return false end - self.revisions.each { |r| r.restore } - self.referenced_links.each { |l| l.restore } - self.deleted = false + self.dmsf_file_revisions.each { |r| r.restore } + self.deleted = STATUS_ACTIVE self.deleted_by_user = nil save end @@ -171,8 +174,8 @@ class DmsfFile < ActiveRecord::Base end def description - self.last_revision ? self.last_revision.description : '' - end + self.last_revision ? self.last_revision.description : '' + end def version self.last_revision ? self.last_revision.version : '0' @@ -187,7 +190,7 @@ class DmsfFile < ActiveRecord::Base end def dmsf_path - path = self.folder.nil? ? [] : self.folder.dmsf_path + path = self.dmsf_folder ? self.dmsf_folder.dmsf_path : [] path.push(self) path end @@ -198,8 +201,8 @@ class DmsfFile < ActiveRecord::Base def notify? return true if self.notification - return true if folder && folder.notify? - return true if !folder && self.project.dmsf_notification + return true if self.dmsf_folder && delf.dmsf_folder.notify? + return true if !self.dmsf_folder && self.project.dmsf_notification return false end @@ -232,7 +235,7 @@ class DmsfFile < ActiveRecord::Base # If the target project differs from the source project we must physically move the disk files if self.project != project - self.revisions.all.each do |rev| + self.dmsf_file_revisions.all.each do |rev| if File.exist? rev.disk_file(self.project) FileUtils.mv rev.disk_file(self.project), rev.disk_file(project) end @@ -240,9 +243,9 @@ class DmsfFile < ActiveRecord::Base end self.project = project - self.folder = folder + self.dmsf_folder = folder new_revision = self.last_revision.clone - new_revision.file = self + new_revision.dmsf_file = self new_revision.comment = l(:comment_moved_from, :source => "#{self.project.identifier}:#{self.dmsf_path_str}") new_revision.custom_values = [] @@ -257,7 +260,7 @@ class DmsfFile < ActiveRecord::Base # If the target project differs from the source project we must physically move the disk files if self.project != project - self.revisions.all.each do |rev| + self.dmsf_file_revisions.all.each do |rev| if File.exist? rev.disk_file(self.project) FileUtils.cp rev.disk_file(self.project), rev.disk_file(project) end @@ -265,14 +268,14 @@ class DmsfFile < ActiveRecord::Base end file = DmsfFile.new - file.folder = folder + file.dmsf_folder = folder file.project = project file.name = self.name file.notification = Setting.plugin_redmine_dmsf['dmsf_default_notifications'].present? if file.save && self.last_revision new_revision = self.last_revision.clone - new_revision.file = file + new_revision.dmsf_file = file new_revision.comment = l(:comment_copied_from, :source => "#{self.project.identifier}: #{self.dmsf_path_str}") new_revision.custom_values = [] @@ -290,33 +293,33 @@ class DmsfFile < ActiveRecord::Base def self.search(tokens, projects = nil, options = {}, user = User.current) tokens = [] << tokens unless tokens.is_a?(Array) projects = [] << projects if projects.is_a?(Project) - project_ids = projects.collect(&:id) if projects - - if options[:offset] + project_ids = projects.collect(&:id) if projects + + if options[:offset] limit_options = ["dmsf_files.updated_at #{options[:before] ? '<' : '>'} ?", options[:offset]] end - + if options[:titles_only] columns = [searchable_options[:columns][1]] - else + else columns = searchable_options[:columns] end - + token_clauses = columns.collect{ |column| "(LOWER(#{column}) LIKE ?)" } - sql = (['(' + token_clauses.join(' OR ') + ')'] * tokens.size).join(options[:all_words] ? ' AND ' : ' OR ') + sql = (['(' + token_clauses.join(' OR ') + ')'] * tokens.size).join(options[:all_words] ? ' AND ' : ' OR ') find_options = [sql, * (tokens.collect {|w| "%#{w.downcase}%"} * token_clauses.size).sort] project_conditions = [] - project_conditions << Project.allowed_to_condition(user, :view_dmsf_files) + project_conditions << Project.allowed_to_condition(user, :view_dmsf_files) project_conditions << "#{DmsfFile.table_name}.project_id IN (#{project_ids.join(',')})" if project_ids.present? - results = [] - - scope = self.visible.joins(:project, :revisions) - scope = scope.limit(options[:limit]) unless options[:limit].blank? + results = [] + + scope = self.visible.joins(:project, :dmsf_file_revisions) + scope = scope.limit(options[:limit]) unless options[:limit].blank? scope = scope.where(limit_options) unless limit_options.blank? - scope = scope.where(project_conditions.join(' AND ')) + scope = scope.where(project_conditions.join(' AND ')) results = scope.where(find_options).uniq.to_a if !options[:titles_only] && $xapian_bindings_available @@ -327,7 +330,7 @@ class DmsfFile < ActiveRecord::Base Setting.plugin_redmine_dmsf['dmsf_index_database'].strip, lang) database = Xapian::Database.new(databasepath) rescue Exception => e - Rails.logger.warn 'REDMAIN_XAPIAN ERROR: Xapian database is not properly set or initiated or is corrupted.' + Rails.logger.warn "REDMAIN_XAPIAN ERROR: Xapian database is not properly set, initiated or it's corrupted." Rails.logger.warn e.message end @@ -360,7 +363,7 @@ class DmsfFile < ActiveRecord::Base enquire.query = query matchset = enquire.mset(0, 1000) - if matchset + if matchset matchset.matches.each { |m| docdata = m.document.data{url} dochash = Hash[*docdata.scan(/(url|sample|modtime|author|type|size)=\/?([^\n\]]+)/).flatten] @@ -371,14 +374,14 @@ class DmsfFile < ActiveRecord::Base id_attribute = dmsf_attrs[0][1] if dmsf_attrs.length > 0 next if dmsf_attrs.length == 0 || id_attribute == 0 next unless results.select{|f| f.id.to_s == id_attribute}.empty? - + dmsf_file = DmsfFile.visible.where(limit_options).where(:id => id_attribute).first if dmsf_file - if user.allowed_to?(:view_dmsf_files, dmsf_file.project) && - (project_ids.blank? || (project_ids.include?(dmsf_file.project.id))) - Redmine::Search.cache_store.write("DmsfFile-#{dmsf_file.id}", - dochash['sample'].force_encoding('UTF-8')) if dochash['sample'] + if user.allowed_to?(:view_dmsf_files, dmsf_file.project) && + (project_ids.blank? || (project_ids.include?(dmsf_file.project.id))) + Redmine::Search.cache_store.write("DmsfFile-#{dmsf_file.id}", + dochash['sample'].force_encoding('UTF-8')) if dochash['sample'] break if(!options[:limit].blank? && results.count >= options[:limit]) results << dmsf_file end @@ -388,14 +391,14 @@ class DmsfFile < ActiveRecord::Base end end end - + [results, results.count] end - + def self.search_result_ranks_and_ids(tokens, user = User.current, projects = nil, options = {}) r = self.search(tokens, projects, options, user)[0] r.map{ |f| [f.updated_at.to_i, f.id]} - end + end def display_name if self.name.length > 50 @@ -403,33 +406,33 @@ class DmsfFile < ActiveRecord::Base end self.name end - + def image? self.last_revision && !!(self.last_revision.disk_filename =~ /\.(bmp|gif|jpg|jpe|jpeg|png|svg)$/i) end - + def preview(limit) result = 'No preview available' if (self.last_revision.disk_filename =~ /\.(txt|ini|diff|c|cpp|php|csv|rb|h|erb|html|css|py)$/i) begin f = File.new(self.last_revision.disk_file) - f.each_line do |line| + f.each_line do |line| case f.lineno when 1 result = line when limit.to_i + 1 break else - result << line - end + result << line + end end rescue Exception => e result = e.message end - end + end result end - + def formatted_name(format) if self.last_revision self.last_revision.formatted_name(format) @@ -437,5 +440,16 @@ class DmsfFile < ActiveRecord::Base self.name end end - + + def owner?(user) + self.last_revision && (self.last_revision.user == user) + end + + def involved?(user) + self.dmsf_file_revisions.each do |file_revision| + return true if file_revision.user == user + end + false + end + end diff --git a/app/models/dmsf_file_revision.rb b/app/models/dmsf_file_revision.rb index c8d5e909..310e4174 100644 --- a/app/models/dmsf_file_revision.rb +++ b/app/models/dmsf_file_revision.rb @@ -3,7 +3,7 @@ # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,28 +19,30 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +require 'digest/md5' + class DmsfFileRevision < ActiveRecord::Base unloadable - belongs_to :file, :class_name => 'DmsfFile', :foreign_key => 'dmsf_file_id' + belongs_to :dmsf_file belongs_to :source_revision, :class_name => 'DmsfFileRevision', :foreign_key => 'source_dmsf_file_revision_id' belongs_to :user - belongs_to :folder, :class_name => 'DmsfFolder', :foreign_key => 'dmsf_folder_id' belongs_to :deleted_by_user, :class_name => 'User', :foreign_key => 'deleted_by_user_id' - has_many :access, :class_name => 'DmsfFileRevisionAccess', :foreign_key => 'dmsf_file_revision_id', :dependent => :destroy - has_many :dmsf_workflow_step_assignment, :dependent => :destroy - accepts_nested_attributes_for :access, :dmsf_workflow_step_assignment, :file, :user - - # Returns a list of revisions that are not deleted here, or deleted at parent level either - scope :visible, -> { where(deleted: false) } - scope :deleted, -> { where(deleted: true) } + has_many :dmsf_file_revision_access, :dependent => :destroy + has_many :dmsf_workflow_step_assignment, :dependent => :destroy + + STATUS_DELETED = 1 + STATUS_ACTIVE = 0 + + scope :visible, -> { where(:deleted => STATUS_ACTIVE) } + scope :deleted, -> { where(:deleted => STATUS_DELETED) } acts_as_customizable - acts_as_event :title => Proc.new {|o| "#{l(:label_dmsf_updated)}: #{o.file.dmsf_path_str}"}, - :url => Proc.new {|o| {:controller => 'dmsf_files', :action => 'show', :id => o.file}}, + acts_as_event :title => Proc.new {|o| "#{l(:label_dmsf_updated)}: #{o.dmsf_file.dmsf_path_str}"}, + :url => Proc.new {|o| {:controller => 'dmsf_files', :action => 'show', :id => o.dmsf_file}}, :datetime => Proc.new {|o| o.updated_at }, :description => Proc.new {|o| o.comment }, :author => Proc.new {|o| o.user } - + acts_as_activity_provider :type => 'dmsf_file_revisions', :timestamp => "#{DmsfFileRevision.table_name}.updated_at", :author_key => "#{DmsfFileRevision.table_name}.user_id", @@ -49,18 +51,18 @@ class DmsfFileRevision < ActiveRecord::Base joins( "INNER JOIN #{DmsfFile.table_name} ON #{DmsfFileRevision.table_name}.dmsf_file_id = #{DmsfFile.table_name}.id " + "INNER JOIN #{Project.table_name} ON #{DmsfFile.table_name}.project_id = #{Project.table_name}.id"). - where("#{DmsfFile.table_name}.deleted = :false", {:false => false}) + where("#{DmsfFile.table_name}.deleted = ?", STATUS_ACTIVE) - validates :title, :presence => true + validates :title, :presence => true validates_format_of :name, :with => DmsfFolder.invalid_characters, :message => l(:error_contains_invalid_character) def project - self.file.project if self.file + self.dmsf_file.project if self.dmsf_file end def folder - self.file.folder if self.file + self.dmsf_file.dmsf_folder if self.dmsf_file end def self.remove_extension(filename) @@ -72,11 +74,11 @@ class DmsfFileRevision < ActiveRecord::Base end def delete(commit = false, force = true) - if self.file.locked_for_user? + if self.dmsf_file.locked_for_user? errors[:base] << l(:error_file_is_locked) return false end - if !commit && (!force && (self.file.revisions.length <= 1)) + if !commit && (!force && (self.dmsf_file.dmsf_file_revisions.length <= 1)) errors[:base] << l(:error_at_least_one_revision_must_be_present) return false end @@ -88,14 +90,14 @@ class DmsfFileRevision < ActiveRecord::Base if commit self.destroy else - self.deleted = true + self.deleted = DmsfFile::STATUS_DELETED self.deleted_by_user = User.current save end end def restore - self.deleted = false + self.deleted = DmsfFile::STATUS_ACTIVE self.deleted_by_user = nil save end @@ -120,7 +122,7 @@ class DmsfFileRevision < ActiveRecord::Base # custom SQL into a temporary object # def access_grouped - access.select('user_id, COUNT(*) AS count, MIN(created_at) AS first_at, MAX(created_at) AS last_at').group('user_id') + self.dmsf_file_revision_access.select('user_id, COUNT(*) AS count, MIN(created_at) AS first_at, MAX(created_at) AS last_at').group('user_id') end def version @@ -128,13 +130,13 @@ class DmsfFileRevision < ActiveRecord::Base end def disk_file(project = nil) - project = self.file.project unless project + project = self.dmsf_file.project unless project storage_base = DmsfFile.storage_path.dup - if self.file && project + if self.dmsf_file && project project_base = project.identifier.gsub(/[^\w\.\-]/,'_') storage_base << "/p_#{project_base}" end - Dir.mkdir(storage_base) unless File.exists?(storage_base) + FileUtils.mkdir_p(storage_base) unless File.exists?(storage_base) "#{storage_base}/#{self.disk_filename}" end @@ -147,7 +149,7 @@ class DmsfFileRevision < ActiveRecord::Base def clone new_revision = DmsfFileRevision.new - new_revision.file = self.file + new_revision.dmsf_file = self.dmsf_file new_revision.disk_filename = self.disk_filename new_revision.size = self.size new_revision.mime_type = self.mime_type @@ -159,6 +161,7 @@ class DmsfFileRevision < ActiveRecord::Base new_revision.source_revision = self new_revision.user = User.current new_revision.name = self.name + new_revision.digest = self.digest new_revision end @@ -200,34 +203,31 @@ class DmsfFileRevision < ActiveRecord::Base wf.assign(self.id) if wf && self.id end - def increase_version(version_to_increase, new_content) - if new_content - self.minor_version = case version_to_increase - when 2 then 0 - else self.minor_version + 1 - end - else - self.minor_version = case version_to_increase - when 1 then self.minor_version + 1 - when 2 then 0 - else self.minor_version - end + def increase_version(version_to_increase) + self.minor_version = case version_to_increase + when 1 + self.minor_version + 1 + when 2 + 0 + else + self.minor_version end - self.major_version = case version_to_increase - when 2 then self.major_version + 1 - else self.major_version + when 2 + self.major_version + 1 + else + major_version end end def new_storage_filename - raise DmsfAccessError, 'File id is not set' unless self.file.id + raise DmsfAccessError, 'File id is not set' unless self.dmsf_file.id filename = DmsfHelper.sanitize_filename(self.name) timestamp = DateTime.now.strftime("%y%m%d%H%M%S") - while File.exist?(File.join(DmsfFile.storage_path, "#{timestamp}_#{self.file.id}_#{filename}")) + while File.exist?(File.join(DmsfFile.storage_path, "#{timestamp}_#{self.dmsf_file.id}_#{filename}")) timestamp.succ! end - "#{timestamp}_#{self.file.id}_#{filename}" + "#{timestamp}_#{self.dmsf_file.id}_#{filename}" end def copy_file_content(open_file) @@ -247,7 +247,7 @@ class DmsfFileRevision < ActiveRecord::Base parts = self.version.split '.' parts.size == 2 ? parts[0].to_i * 1000 + parts[1].to_i : 0 end - + def formatted_name(format) return self.name if format.blank? if self.name =~ /(.*)(\..*)$/ @@ -255,13 +255,43 @@ class DmsfFileRevision < ActiveRecord::Base ext = $2 else filename = self.name - end - format.sub!('%t', filename) + end + format.sub!('%t', self.title) + format.sub!('%f', filename) format.sub!('%d', self.updated_at.strftime('%Y%m%d%H%M%S')) format.sub!('%v', self.version) - format.sub!('%i', self.file.id.to_s) + format.sub!('%i', self.dmsf_file.id.to_s) format.sub!('%r', self.id.to_s) format + ext end -end + def self.create_digest(path) + begin + Digest::MD5.file(path).hexdigest + rescue Exception => e + Rails.logger.error e.message + nil + end + end + + def create_digest + begin + self.digest = Digest::MD5.file(self.disk_file).hexdigest + true + rescue Exception => e + Rails.logger.error e.message + false + end + end + + def tooltip + text = '' + text = self.description if self.description.present? + if self.comment.present? + text += ' ' if text.present? + text += self.comment + end + text.html_safe + end + +end \ No newline at end of file diff --git a/app/models/dmsf_file_revision_access.rb b/app/models/dmsf_file_revision_access.rb index 44140ee4..61d8a87f 100644 --- a/app/models/dmsf_file_revision_access.rb +++ b/app/models/dmsf_file_revision_access.rb @@ -1,6 +1,9 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,22 +20,22 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class DmsfFileRevisionAccess < ActiveRecord::Base + unloadable - belongs_to :revision, :class_name => 'DmsfFileRevision', :foreign_key => 'dmsf_file_revision_id' + belongs_to :dmsf_file_revision belongs_to :user - delegate :project, :to => :revision, :allow_nil => false - delegate :file, :to => :revision, :allow_nil => false - accepts_nested_attributes_for :user, :revision + delegate :dmsf_file, :to => :dmsf_file_revision, :allow_nil => false + delegate :project, :to => :dmsf_file, :allow_nil => false DownloadAction = 0 EmailAction = 1 - acts_as_event :title => Proc.new {|o| "#{l(:label_dmsf_downloaded)}: #{o.file.dmsf_path_str}"}, - :url => Proc.new {|o| {:controller => 'dmsf_files', :action => 'show', :id => o.file}}, - :datetime => Proc.new {|o| o.updated_at }, - :description => Proc.new {|o| o.revision.comment }, - :author => Proc.new {|o| o.user } - + acts_as_event :title => Proc.new {|ra| "#{l(:label_dmsf_downloaded)}: #{ra.dmsf_file.dmsf_path_str}"}, + :url => Proc.new {|ra| {:controller => 'dmsf_files', :action => 'show', :id => ra.dmsf_file}}, + :datetime => Proc.new {|ra| ra.updated_at }, + :description => Proc.new {|ra| ra.dmsf_file_revision.comment }, + :author => Proc.new {|ra| ra.user } + acts_as_activity_provider :type => 'dmsf_file_revision_accesses', :timestamp => "#{DmsfFileRevisionAccess.table_name}.updated_at", :author_key => "#{DmsfFileRevisionAccess.table_name}.user_id", @@ -42,6 +45,5 @@ class DmsfFileRevisionAccess < ActiveRecord::Base "INNER JOIN #{DmsfFileRevision.table_name} ON #{DmsfFileRevisionAccess.table_name}.dmsf_file_revision_id = #{DmsfFileRevision.table_name}.id " + "INNER JOIN #{DmsfFile.table_name} ON #{DmsfFileRevision.table_name}.dmsf_file_id = #{DmsfFile.table_name}.id " + "INNER JOIN #{Project.table_name} ON #{DmsfFile.table_name}.project_id = #{Project.table_name}.id"). - where("#{DmsfFile.table_name}.deleted = :false", {:false => false}) - + where("#{DmsfFile.table_name}.deleted = ?", DmsfFile::STATUS_ACTIVE) end diff --git a/app/models/dmsf_file_revision_custom_field.rb b/app/models/dmsf_file_revision_custom_field.rb index 2793b424..e9a12d8f 100644 --- a/app/models/dmsf_file_revision_custom_field.rb +++ b/app/models/dmsf_file_revision_custom_field.rb @@ -1,7 +1,9 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2013 Karel Picman +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,18 +19,18 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -class DmsfFileRevisionCustomField < CustomField - +class DmsfFileRevisionCustomField < CustomField + def type_name :menu_dmsf end - + def compare_values?(x, y) if x.is_a?(Array) && y.is_a?(Array) && !y.empty? - x.include? y[0] - else + x.include? y[0] + else x == y - end + end end - + end \ No newline at end of file diff --git a/app/models/dmsf_folder.rb b/app/models/dmsf_folder.rb index a5dd2f88..b7a8ff36 100644 --- a/app/models/dmsf_folder.rb +++ b/app/models/dmsf_folder.rb @@ -3,7 +3,7 @@ # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -25,44 +25,41 @@ class DmsfFolder < ActiveRecord::Base include RedmineDmsf::Lockable cattr_reader :invalid_characters - @@invalid_characters = /\A[^\/\\\?":<>]*\z/ + @@invalid_characters = /\A[^\/\\\?":<>]*\z/ belongs_to :project - belongs_to :folder, :class_name => 'DmsfFolder', :foreign_key => 'dmsf_folder_id' + belongs_to :dmsf_folder belongs_to :deleted_by_user, :class_name => 'User', :foreign_key => 'deleted_by_user_id' belongs_to :user - has_many :subfolders, :class_name => 'DmsfFolder', :foreign_key => 'dmsf_folder_id', - :dependent => :destroy - has_many :files, :class_name => 'DmsfFile', :foreign_key => 'dmsf_folder_id', - :dependent => :destroy - has_many :folder_links, -> { where :target_type => 'DmsfFolder' }, + 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 has_many :file_links, -> { where :target_type => 'DmsfFile' }, :class_name => 'DmsfLink', :foreign_key => 'dmsf_folder_id', :dependent => :destroy has_many :url_links, -> { where :target_type => 'DmsfUrl' }, :class_name => 'DmsfLink', :foreign_key => 'dmsf_folder_id', :dependent => :destroy + has_many :dmsf_links, :dependent => :destroy has_many :referenced_links, -> { where :target_type => 'DmsfFolder' }, :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 - accepts_nested_attributes_for :user, :project, :folder, :subfolders, :files, :folder_links, :file_links, :url_links, :referenced_links, :locks - - scope :visible, lambda { |*args| - where(deleted: false) - } - scope :deleted, lambda { |*args| - where(deleted: true) - } + + STATUS_DELETED = 1 + STATUS_ACTIVE = 0 + + scope :visible, -> { where(:deleted => STATUS_ACTIVE) } + scope :deleted, -> { where(:deleted => STATUS_DELETED) } acts_as_customizable - + acts_as_searchable :columns => ["#{self.table_name}.title", "#{self.table_name}.description"], :project_key => 'project_id', :date_column => 'updated_at', :permission => :view_dmsf_files, - :scope => self.joins(:project) - + :scope => self.joins(:project) + acts_as_event :title => Proc.new {|o| o.title}, :description => Proc.new {|o| o.description }, :url => Proc.new {|o| {:controller => 'dmsf', :action => 'show', :id => o.project, :folder_id => o}}, @@ -70,8 +67,8 @@ class DmsfFolder < ActiveRecord::Base :author => Proc.new {|o| o.user } validates :title, :presence => true - validates_uniqueness_of :title, :scope => [:dmsf_folder_id, :project_id, :deleted], - conditions: -> { where.not(deleted: true) } + validates_uniqueness_of :title, :scope => [:dmsf_folder_id, :project_id, :deleted], + conditions: -> { where(:deleted => STATUS_ACTIVE) } validates_format_of :title, :with => @@invalid_characters, :message => l(:error_contains_invalid_character) validate :check_cycle @@ -88,13 +85,13 @@ class DmsfFolder < ActiveRecord::Base def check_cycle folders = [] - self.subfolders.each {|f| folders.push(f)} - folders.each do |folder| - if folder == self.folder + self.dmsf_folders.each {|f| folders.push(f)} + self.dmsf_folders.each do |folder| + if folder == self.dmsf_folder errors.add(:folder, l(:error_create_cycle_in_folder_dependency)) return false end - folder.subfolders.each {|f| folders.push(f)} + folder.dmsf_folders.each {|f| folders.push(f)} end return true end @@ -111,27 +108,29 @@ class DmsfFolder < ActiveRecord::Base if self.locked? errors[:base] << l(:error_folder_is_locked) return false - elsif !self.subfolders.visible.empty? || !self.files.visible.empty? + elsif !self.dmsf_folders.visible.empty? || !self.dmsf_files.visible.empty? errors[:base] << l(:error_folder_is_not_empty) return false end - self.referenced_links.each { |l| l.delete(commit) } if commit self.destroy else - self.deleted = true + self.deleted = STATUS_DELETED self.deleted_by_user = User.current self.save end end + def deleted? + self.deleted == STATUS_DELETED + end + def restore - if self.dmsf_folder_id && (self.folder.nil? || self.folder.deleted) + if self.dmsf_folder_id && (self.dmsf_folder.nil? || self.dmsf_folder.deleted?) errors[:base] << l(:error_parent_folder) return false end - self.referenced_links.each { |l| l.restore } - self.deleted = false + self.deleted = STATUS_ACTIVE self.deleted_by_user = nil self.save end @@ -141,7 +140,7 @@ class DmsfFolder < ActiveRecord::Base path = [] while folder path.unshift(folder) - folder = folder.folder + folder = folder.dmsf_folder end path end @@ -154,8 +153,8 @@ class DmsfFolder < ActiveRecord::Base def notify? return true if self.notification - return true if folder && folder.notify? - return true if !folder && self.project.dmsf_notification + return true if self.dmsf_folder && self.dmsf_folder.notify? + return true if !self.dmsf_folder && self.project.dmsf_notification return false end @@ -196,21 +195,21 @@ class DmsfFolder < ActiveRecord::Base end def deep_file_count - file_count = self.files.visible.count - self.subfolders.visible.each {|subfolder| file_count += subfolder.deep_file_count} - file_count + self.file_links.visible.count + file_count = self.dmsf_files.visible.count + self.dmsf_folders.visible.each { |subfolder| file_count += subfolder.deep_file_count } + file_count + self.file_links.visible.count + self.url_links.visible.count end def deep_folder_count - folder_count = self.subfolders.visible.count - self.subfolders.visible.each {|subfolder| folder_count += subfolder.deep_folder_count} + folder_count = self.dmsf_folders.visible.count + self.dmsf_folders.visible.each { |subfolder| folder_count += subfolder.deep_folder_count } folder_count + self.folder_links.visible.count end def deep_size size = 0 - self.files.visible.each {|file| size += file.size} - self.subfolders.visible.each {|subfolder| size += subfolder.deep_size} + self.dmsf_files.visible.each {|file| size += file.size} + self.dmsf_folders.visible.each {|subfolder| size += subfolder.deep_size} size end @@ -227,7 +226,7 @@ class DmsfFolder < ActiveRecord::Base def copy_to(project, folder) new_folder = DmsfFolder.new - new_folder.folder = folder ? folder : nil + new_folder.dmsf_folder = folder ? folder : nil new_folder.project = folder ? folder.project : project new_folder.title = self.title new_folder.description = self.description @@ -240,11 +239,11 @@ class DmsfFolder < ActiveRecord::Base return new_folder unless new_folder.save - self.files.visible.each do |f| + self.dmsf_files.visible.each do |f| f.copy_to project, new_folder end - self.subfolders.visible.each do |s| + self.dmsf_folders.visible.each do |s| s.copy_to project, new_folder end @@ -267,13 +266,13 @@ class DmsfFolder < ActiveRecord::Base def available_custom_fields DmsfFileRevisionCustomField.all end - + def modified last_update = updated_at - subfolders.each do |subfolder| + dmsf_folders.each do |subfolder| last_update = subfolder.updated_at if subfolder.updated_at > last_update end - files.each do |file| + dmsf_files.each do |file| last_update = file.updated_at if file.updated_at > last_update end folder_links.each do |folder_link| @@ -287,20 +286,20 @@ class DmsfFolder < ActiveRecord::Base end last_update end - + # Number of items in the folder def items - subfolders.visible.count + - files.visible.count + + dmsf_folders.visible.count + + dmsf_files.visible.count + folder_links.visible.count + file_links.visible.count + url_links.visible.count - end - + end + private def self.directory_subtree(tree, folder, level, current_folder) - folder.subfolders.visible.each do |subfolder| + folder.dmsf_folders.visible.each do |subfolder| unless subfolder == current_folder tree.push(["#{'...' * level}#{subfolder.title}", subfolder.id]) directory_subtree(tree, subfolder, level + 1, current_folder) diff --git a/app/models/dmsf_link.rb b/app/models/dmsf_link.rb index e92662be..1e67e143 100644 --- a/app/models/dmsf_link.rb +++ b/app/models/dmsf_link.rb @@ -2,7 +2,7 @@ # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -29,21 +29,28 @@ class DmsfLink < ActiveRecord::Base validates :name, :presence => true validates_length_of :name, :maximum => 255 - validates_length_of :external_url, :maximum => 255 + validates_length_of :external_url, :maximum => 255 validate :validate_url - + def validate_url if self.target_type == 'DmsfUrl' - begin - URI.parse self.external_url - rescue URI::InvalidURIError + begin + if self.external_url.present? + URI.parse self.external_url + else + errors.add :external_url, :invalid + end + rescue URI::InvalidURIError errors.add :external_url, :invalid - end + end end end - - scope :visible, -> { where(deleted: false) } - scope :deleted, -> { where(deleted: true) } + + STATUS_DELETED = 1 + STATUS_ACTIVE = 0 + + scope :visible, -> { where(:deleted => STATUS_ACTIVE) } + scope :deleted, -> { where(:deleted => STATUS_DELETED) } def target_folder_id if self.target_type == DmsfFolder.model_name.to_s @@ -90,9 +97,9 @@ class DmsfLink < ActiveRecord::Base end def path - if self.target_type == DmsfFile.model_name.to_s + if self.target_type == DmsfFile.model_name.to_s path = self.target_file.dmsf_path.map { |element| element.is_a?(DmsfFile) ? element.name : element.title }.join('/') if self.target_file - else + else path = self.target_folder ? self.target_folder.dmsf_path_str : '' end path.insert(0, "#{self.target_project.name}:") if self.project_id != self.target_project_id @@ -119,20 +126,28 @@ class DmsfLink < ActiveRecord::Base if commit self.destroy else - self.deleted = true + self.deleted = STATUS_DELETED self.deleted_by_user = User.current - save + save(:validate => false) end end def restore - if self.dmsf_folder_id && (self.folder.nil? || self.folder.deleted) + if self.dmsf_folder_id && (self.dmsf_folder.nil? || self.dmsf_folder.deleted?) errors[:base] << l(:error_parent_folder) return false end - self.deleted = false + self.deleted = STATUS_ACTIVE self.deleted_by_user = nil - save + save(:validate => false) end -end + def is_folder? + self.target_type == 'DmsfFolder' + end + + def is_file? + !is_folder? + end + +end \ No newline at end of file diff --git a/app/models/dmsf_lock.rb b/app/models/dmsf_lock.rb index e1873c7d..13bc6b8c 100644 --- a/app/models/dmsf_lock.rb +++ b/app/models/dmsf_lock.rb @@ -29,10 +29,9 @@ class DmsfLock < ActiveRecord::Base belongs_to :user # At the moment apparently we're only supporting a write lock? - as_enum :lock_type, [:type_write, :type_other] as_enum :lock_scope, [:scope_exclusive, :scope_shared] - + # We really loosely bind the value in the belongs_to above # here we just ensure the data internal to the model is correct # to ensure everything lists fine - it's the same as a join @@ -73,5 +72,5 @@ class DmsfLock < ActiveRecord::Base def self.find_by_param(*args) self.find(*args) end - + end \ No newline at end of file diff --git a/app/models/dmsf_mailer.rb b/app/models/dmsf_mailer.rb index 5b338627..70877fd8 100644 --- a/app/models/dmsf_mailer.rb +++ b/app/models/dmsf_mailer.rb @@ -3,7 +3,7 @@ # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -23,79 +23,73 @@ require 'mailer' class DmsfMailer < Mailer layout 'mailer' - + def files_updated(user, project, files) - if user && project && files.count > 0 + if user && project && files.count > 0 files = files.select { |file| file.notify? } - redmine_headers 'Project' => project.identifier if project - @files = files @project = project - + message_id project set_language_if_valid user.language - mail :to => user.mail, - :subject => l(:text_email_doc_updated_subject, :project => project.name) + mail :to => user.mail, + :subject => "[#{@project.name} - #{l(:menu_dmsf)}] #{l(:text_email_doc_updated_subject)}" end end - + def files_deleted(user, project, files) - if user && files.count > 0 + if user && files.count > 0 files = files.select { |file| file.notify? } - redmine_headers 'Project' => project.identifier if project - @files = files @project = project - + message_id project set_language_if_valid user.language - mail :to => user.mail, - :subject => l(:text_email_doc_deleted_subject, :project => project.name) + mail :to => user.mail, + :subject => "[#{@project.name} - #{l(:menu_dmsf)}] #{l(:text_email_doc_deleted_subject)}" end end - - def send_documents(project, user, email_params) - zipped_content_data = open(email_params[:zipped_content], 'rb') { |io| io.read } - - redmine_headers 'Project' => project.identifier if project + def send_documents(project, user, email_params) + zipped_content_data = open(email_params[:zipped_content], 'rb') { |io| io.read } + redmine_headers 'Project' => project.identifier if project @body = email_params[:body] @links_only = email_params[:links_only] @folders = email_params[:folders] @files = email_params[:files] - unless @links_only == '1' attachments['Documents.zip'] = { :content_type => 'application/zip', :content => zipped_content_data } end - - mail :to => email_params[:to], :cc => email_params[:cc], :subject => email_params[:subject], :from => user.mail + mail :to => email_params[:to], :cc => email_params[:cc], + :subject => email_params[:subject], :from => user.mail end - + def workflow_notification(user, workflow, revision, subject_id, text1_id, text2_id, notice = nil) if user && workflow && revision - if revision.file && revision.file.project - @project = revision.file.project + if revision.dmsf_file && revision.dmsf_file.project + @project = revision.dmsf_file.project redmine_headers 'Project' => @project.identifier end set_language_if_valid user.language @user = user + message_id workflow @workflow = workflow - @revision = revision - @text1 = l(text1_id, :name => workflow.name, :filename => revision.file.name, :notice => notice) + @revision = revision + @text1 = l(text1_id, :name => workflow.name, :filename => revision.dmsf_file.name, :notice => notice) @text2 = l(text2_id) @notice = notice - mail :to => user.mail, :subject => l(subject_id, :name => workflow.name) + mail :to => user.mail, + :subject => "[#{@project.name} - #{l(:field_label_dmsf_workflow)}] #{@workflow.name} #{l(subject_id)}" end - end - - def self.get_notify_users(project, files = nil) - if files + end + + def self.get_notify_users(project, files = []) + if files.present? notify_files = files.select { |file| file.notify? } return [] if notify_files.empty? - end - notify_members = project.members - notify_members = notify_members.select do |notify_member| - notify_user = notify_member.user + end + notify_members = project.members.select do |notify_member| + notify_user = notify_member.user if notify_user == User.current && notify_user.pref.no_self_notified false else @@ -105,18 +99,34 @@ class DmsfMailer < Mailer true when 'selected' notify_member.mail_notification? - when 'only_my_events', 'only_owner' - notify_user.allowed_to?(:file_manipulation, project) + when 'only_my_events' + author = false + files.each do |file| + if file.involved?(notify_user) + author = true + break + end + end + author + when 'only_owner', 'only_assigned' + author = false + files.each do |file| + if file.owner?(notify_user) + author = true + break + end + end + author else false end - else + else notify_member.dmsf_mail_notification end end - end + end notify_members.collect { |m| m.user }.uniq end - + end \ No newline at end of file diff --git a/app/models/dmsf_upload.rb b/app/models/dmsf_upload.rb index a151de50..ff1efd06 100644 --- a/app/models/dmsf_upload.rb +++ b/app/models/dmsf_upload.rb @@ -1,8 +1,10 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-14 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,63 +21,67 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class DmsfUpload - attr_accessor :name + attr_accessor :name attr_accessor :disk_filename attr_reader :size attr_accessor :mime_type attr_accessor :title - attr_accessor :description + attr_accessor :description attr_accessor :comment attr_accessor :major_version attr_accessor :minor_version - attr_accessor :locked + attr_accessor :locked attr_accessor :workflow - attr_accessor :custom_values - + attr_accessor :custom_values + def disk_file "#{DmsfHelper.temp_dir}/#{self.disk_filename}" - end - - def self.create_from_uploaded_attachment(project, folder, uploaded_file) - a = Attachment.find_by_token(uploaded_file[:token]) + end + + def self.create_from_uploaded_attachment(project, folder, uploaded_file) + a = Attachment.find_by_token(uploaded_file[:token]) if a uploaded = { :disk_filename => DmsfHelper.temp_filename(a.filename), :content_type => a.content_type, :original_filename => a.filename, :comment => uploaded_file[:description] - } + } FileUtils.mv(a.diskfile, "#{DmsfHelper.temp_dir}/#{uploaded[:disk_filename]}") - a.destroy + a.destroy upload = DmsfUpload.new(project, folder, uploaded) else - Rails.logger.error "An attachment not found by its token: #{uploaded_file[:token]}" + Rails.logger.error "An attachment not found by its token: #{uploaded_file[:token]}" end upload end - + def initialize(project, folder, uploaded) @name = uploaded[:original_filename] - + file = DmsfFile.find_file_by_name(project, folder, @name) unless file link = DmsfLink.find_link_by_file_name(project, folder, @name) file = link.target_file if link end - + @disk_filename = uploaded[:disk_filename] @mime_type = uploaded[:content_type] - @size = File.size(disk_file) - + @size = File.size(disk_file) + if file.nil? || file.last_revision.nil? @title = DmsfFileRevision.filename_to_title(@name) @description = uploaded[:comment] @major_version = 0 @minor_version = 0 - @workflow = nil - @custom_values = DmsfFileRevision.new(:file => DmsfFile.new(:project => @project)).custom_field_values + @workflow = nil + file = DmsfFile.new + file.project = @project + revision = DmsfFileRevision.new + revision.dmsf_file = file + @custom_values = revision.custom_field_values else - last_revision = file.last_revision + last_revision = file.last_revision @title = last_revision.title if last_revision.description.present? @description = last_revision.description @@ -86,7 +92,7 @@ class DmsfUpload @major_version = last_revision.major_version @minor_version = last_revision.minor_version @workflow = last_revision.workflow - @custom_values = Array.new(file.last_revision.custom_values) + @custom_values = Array.new(file.last_revision.custom_values) # Add default value for CFs not existing present_custom_fields = file.last_revision.custom_values.collect(&:custom_field).uniq @@ -96,8 +102,8 @@ class DmsfUpload end end end - + @locked = file && file.locked_for_user? end - + end \ No newline at end of file diff --git a/app/models/dmsf_workflow.rb b/app/models/dmsf_workflow.rb index 85f7a8f6..dd3aec4c 100644 --- a/app/models/dmsf_workflow.rb +++ b/app/models/dmsf_workflow.rb @@ -2,7 +2,7 @@ # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Picman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,49 +18,54 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -class DmsfWorkflow < ActiveRecord::Base - - has_many :dmsf_workflow_steps, -> { order 'step ASC, operator DESC' }, :dependent => :destroy - +class DmsfWorkflow < ActiveRecord::Base + has_many :dmsf_workflow_steps, -> { order 'step ASC, operator DESC' }, :dependent => :destroy + belongs_to :author, :class_name => 'User' + scope :sorted, lambda { order('name ASC') } scope :global, lambda { where('project_id IS NULL') } - + scope :active, lambda { where(:status => STATUS_ACTIVE) } + scope :status, lambda { |arg| where(arg.blank? ? nil : {:status => arg.to_i}) } + validate :name_validation validates :name, :presence => true validates_length_of :name, :maximum => 255 - + def name_validation if self.project_id if self.id - if (DmsfWorkflow.where(['(project_id IS NULL OR (project_id = ? AND id != ?)) AND name = ?', - self.project_id, self.id, self.name]).count > 0) + if (DmsfWorkflow.where(['(project_id IS NULL OR (project_id = ? AND id != ?)) AND name = ?', + self.project_id, self.id, self.name]).exists?) errors.add(:name, l('activerecord.errors.messages.taken')) end else - if (DmsfWorkflow.where(['(project_id IS NULL OR project_id = ?) AND name = ?', - self.project_id, self.name]).count > 0) + if (DmsfWorkflow.where(['(project_id IS NULL OR project_id = ?) AND name = ?', + self.project_id, self.name]).exists?) errors.add(:name, l('activerecord.errors.messages.taken')) end end - else + else if self.id - if DmsfWorkflow.where(['name = ? AND id != ?', self.name, self.id]).count > 0 + if DmsfWorkflow.where(['name = ? AND id != ?', self.name, self.id]).exists? errors.add(:name, l('activerecord.errors.messages.taken')) end else - if DmsfWorkflow.where(:name => self.name).count > 0 + if DmsfWorkflow.where(:name => self.name).exists? errors.add(:name, l('activerecord.errors.messages.taken')) end end end end - + STATE_NONE = nil STATE_ASSIGNED = 3 STATE_WAITING_FOR_APPROVAL = 1 STATE_APPROVED = 2 STATE_REJECTED = 4 - + + STATUS_LOCKED = 0 + STATUS_ACTIVE = 1 + def participiants users = Array.new self.dmsf_workflow_steps.each do |step| @@ -70,90 +75,58 @@ class DmsfWorkflow < ActiveRecord::Base end def self.workflows(project) - project ? where(:project_id => project) : where('project_id IS NULL') + project ? where(:project_id => project) : where('project_id IS NULL') end - def project + def project Project.find_by_id(project_id) if project_id end def to_s name - end + end def reorder_steps(step, move_to) DmsfWorkflow.transaction do - case move_to - when 'highest' - unless step == 1 - dmsf_workflow_steps.each do |ws| - if ws.step < step - return false unless ws.update_attribute('step', ws.step + 1) - elsif ws.step == step - return false unless ws.update_attribute('step', 1) - end - end + dmsf_workflow_steps.each do |ws| + if ws.step == step + return false unless ws.update_attribute('step', move_to) + elsif ws.step >= move_to && ws.step < step + # Move up + return false unless ws.update_attribute('step', ws.step + 1) + elsif ws.step <= move_to && ws.step > step + # Move down + return false unless ws.update_attribute('step', ws.step - 1) end - when 'higher' - unless step == 1 - dmsf_workflow_steps.each do |ws| - if ws.step == step - 1 - return false unless ws.update_attribute('step', step) - elsif ws.step == step - return false unless ws.update_attribute('step', step - 1) - end - end - end - when 'lower' - unless step == dmsf_workflow_steps.collect{|s| s.step}.uniq.count - dmsf_workflow_steps.each do |ws| - if ws.step == step + 1 - return false unless ws.update_attribute('step', step) - elsif ws.step == step - return false unless ws.update_attribute('step', step + 1) - end - end - end - when 'lowest' - size = dmsf_workflow_steps.collect{|s| s.step}.uniq.count - unless step == size - dmsf_workflow_steps.each do |ws| - if ws.step > step - return false unless ws.update_attribute('step', ws.step - 1) - elsif ws.step == step - return false unless ws.update_attribute('step', size) - end - end - end - end - end - return reload + end + end + return true end - - def delegates(q, dmsf_workflow_step_assignment_id, dmsf_file_revision_id) + + def delegates(q, dmsf_workflow_step_assignment_id, dmsf_file_revision_id) if dmsf_workflow_step_assignment_id && dmsf_file_revision_id sql = [ - 'id NOT IN (SELECT a.user_id FROM dmsf_workflow_step_assignments a WHERE id = ?) AND id IN (SELECT m.user_id FROM members m JOIN dmsf_files f ON f.project_id = m.project_id JOIN dmsf_file_revisions r ON r.dmsf_file_id = f.id WHERE r.id = ?)', - dmsf_workflow_step_assignment_id, + 'id NOT IN (SELECT a.user_id FROM dmsf_workflow_step_assignments a WHERE id = ?) AND id IN (SELECT m.user_id FROM members m JOIN dmsf_files f ON f.project_id = m.project_id JOIN dmsf_file_revisions r ON r.dmsf_file_id = f.id WHERE r.id = ?)', + dmsf_workflow_step_assignment_id, dmsf_file_revision_id] elsif project sql = ['id IN (SELECT user_id FROM members WHERE project_id = ?)', project.id] else sql = '1=1' end - + if q.present? User.active.sorted.where(sql).like(q) else User.active.sorted.where(sql) - end + end end - - def next_assignments(dmsf_file_revision_id) - results = Array.new + + def next_assignments(dmsf_file_revision_id) + results = Array.new nsteps = self.dmsf_workflow_steps.collect{|s| s.step}.uniq nsteps.each do |i| - step_is_finished = false + step_is_finished = false steps = self.dmsf_workflow_steps.collect{|s| s.step == i ? s : nil}.compact steps.each do |step| step.dmsf_workflow_step_assignments.each do |assignment| @@ -162,24 +135,25 @@ class DmsfWorkflow < ActiveRecord::Base case action.action when DmsfWorkflowStepAction::ACTION_APPROVE step_is_finished = true - # Try to find another unfinished AND step + # Try to find another unfinished AND step exists = false stps = self.dmsf_workflow_steps.collect{|s| (s.step == i && s.operator == DmsfWorkflowStep::OPERATOR_AND) ? s : nil}.compact stps.each do |s| s.dmsf_workflow_step_assignments.each do |a| - exists = a.add?(dmsf_file_revision_id) - break if exists + exists = a.add?(dmsf_file_revision_id) + break if exists end - end + break if exists + end step_is_finished = false if exists break when DmsfWorkflowStepAction::ACTION_REJECT - return Array.new + return Array.new end end end break if step_is_finished - end + end break if step_is_finished end unless step_is_finished @@ -193,29 +167,29 @@ class DmsfWorkflow < ActiveRecord::Base end results end - + def self.assignments_to_users_str(assignments) str = '' - if assignments - assignments.each_with_index do |assignment, index| + if assignments + assignments.each_with_index do |assignment, index| if index > 0 str << ', ' end - str << assignment.user.name + str << assignment.user.name end end - str + str end - + def assign(dmsf_file_revision_id) dmsf_workflow_steps.each do |ws| ws.assign(dmsf_file_revision_id) end end - - def try_finish(revision, action, user_id) + + def try_finish(revision, action, user_id) case action.action - when DmsfWorkflowStepAction::ACTION_APPROVE + when DmsfWorkflowStepAction::ACTION_APPROVE assignments = self.next_assignments revision.id return false unless assignments.empty? revision.update_attribute(:workflow, DmsfWorkflow::STATE_APPROVED) @@ -223,7 +197,7 @@ class DmsfWorkflow < ActiveRecord::Base when DmsfWorkflowStepAction::ACTION_REJECT revision.update_attribute(:workflow, DmsfWorkflow::STATE_REJECTED) return true - when DmsfWorkflowStepAction::ACTION_DELEGATE + when DmsfWorkflowStepAction::ACTION_DELEGATE self.dmsf_workflow_steps.each do |step| step.dmsf_workflow_step_assignments.each do |assignment| if assignment.id == action.dmsf_workflow_step_assignment_id @@ -234,17 +208,27 @@ class DmsfWorkflow < ActiveRecord::Base end end return false - end - + end + def copy_to(project, name = nil) - new_wf = self.dup + new_wf = self.dup new_wf.name = name if name new_wf.project_id = project ? project.id : nil + new_wf.author = User.current if new_wf.save self.dmsf_workflow_steps.each do |step| step.copy_to(new_wf) - end + end end return new_wf end + + def locked? + self.status == STATUS_LOCKED + end + + def active? + self.status == STATUS_ACTIVE + end + end diff --git a/app/models/dmsf_workflow_step.rb b/app/models/dmsf_workflow_step.rb index 8ce5c857..63e903ad 100644 --- a/app/models/dmsf_workflow_step.rb +++ b/app/models/dmsf_workflow_step.rb @@ -2,7 +2,7 @@ # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -25,8 +25,8 @@ class DmsfWorkflowStep < ActiveRecord::Base validates :step, :presence => true validates :user_id, :presence => true validates :operator, :presence => true - validates_uniqueness_of :user_id, :scope => [:dmsf_workflow_id, :step] - + validates_uniqueness_of :user_id, :scope => [:dmsf_workflow_id, :step] + OPERATOR_OR = 0 OPERATOR_AND = 1 @@ -37,7 +37,7 @@ class DmsfWorkflowStep < ActiveRecord::Base def user User.find(user_id) end - + def assign(dmsf_file_revision_id) step_assignment = DmsfWorkflowStepAssignment.new( :dmsf_workflow_step_id => id, @@ -45,20 +45,20 @@ class DmsfWorkflowStep < ActiveRecord::Base :dmsf_file_revision_id => dmsf_file_revision_id) step_assignment.save end - + def is_finished?(dmsf_file_revision_id) self.dmsf_workflow_step_assignments.each do |assignment| if assignment.dmsf_file_revision_id == dmsf_file_revision_id - if assignment.dmsf_workflow_step_actions.empty? + if assignment.dmsf_workflow_step_actions.empty? return false - end - assignment.dmsf_workflow_step_actions.each do |act| - return false unless act.is_finished? end - end - end + assignment.dmsf_workflow_step_actions.each do |act| + return false unless act.is_finished? + end + end + end end - + def copy_to(workflow) new_step = self.dup new_step.dmsf_workflow_id = workflow.id diff --git a/app/models/dmsf_workflow_step_action.rb b/app/models/dmsf_workflow_step_action.rb index f5d8c69e..0725c362 100644 --- a/app/models/dmsf_workflow_step_action.rb +++ b/app/models/dmsf_workflow_step_action.rb @@ -2,7 +2,7 @@ # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,37 +19,35 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class DmsfWorkflowStepAction < ActiveRecord::Base - - belongs_to :dmsf_workflow_step_assignment + + belongs_to :dmsf_workflow_step_assignment validates :dmsf_workflow_step_assignment_id, :presence => true validates :action, :presence => true validates :note, :presence => true, :unless => lambda { self.action == DmsfWorkflowStepAction::ACTION_APPROVE } validates :author_id, :presence => true - validates_uniqueness_of :dmsf_workflow_step_assignment_id, :scope => [:action], :unless => lambda {self.action == DmsfWorkflowStepAction::ACTION_DELEGATE} - - attr_accessible :dmsf_workflow_step_assignment_id, :action, :note - + validates_uniqueness_of :dmsf_workflow_step_assignment_id, :scope => [:action], :unless => lambda {self.action == DmsfWorkflowStepAction::ACTION_DELEGATE} + ACTION_APPROVE = 1 ACTION_REJECT = 2 ACTION_DELEGATE = 3 ACTION_ASSIGN = 4 ACTION_START = 5 - + def initialize(*args) super - self.author_id = User.current.id if User.current + self.author_id = User.current.id if User.current end - + def self.is_finished?(action) action == DmsfWorkflowStepAction::ACTION_APPROVE || action == DmsfWorkflowStepAction::ACTION_REJECT end - + def is_finished? DmsfWorkflowStepAction.is_finished? self.action end - + def self.action_str(action) if action case action.to_i @@ -66,7 +64,7 @@ class DmsfWorkflowStepAction < ActiveRecord::Base end end end - + def self.action_type_str(action) if action case action.to_i @@ -83,12 +81,12 @@ class DmsfWorkflowStepAction < ActiveRecord::Base end end end - + def self.workflow_str(action) if action - case action.to_i + case action.to_i when ACTION_REJECT - l(:title_rejected) + l(:title_rejected) when ACTION_ASSIGN l(:title_assigned) when ACTION_START, ACTION_DELEGATE, ACTION_APPROVE @@ -98,5 +96,5 @@ class DmsfWorkflowStepAction < ActiveRecord::Base end end end - + end \ No newline at end of file diff --git a/app/models/dmsf_workflow_step_assignment.rb b/app/models/dmsf_workflow_step_assignment.rb index cda7d8f3..30ffbcd5 100644 --- a/app/models/dmsf_workflow_step_assignment.rb +++ b/app/models/dmsf_workflow_step_assignment.rb @@ -2,7 +2,7 @@ # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -22,17 +22,15 @@ class DmsfWorkflowStepAssignment < ActiveRecord::Base belongs_to :dmsf_workflow_step belongs_to :user belongs_to :dmsf_file_revision - has_many :dmsf_workflow_step_actions, :dependent => :destroy - validates :dmsf_workflow_step_id, :dmsf_file_revision_id, :presence => true + has_many :dmsf_workflow_step_actions, :dependent => :destroy + validates :dmsf_workflow_step_id, :dmsf_file_revision_id, :presence => true validates_uniqueness_of :dmsf_workflow_step_id, :scope => [:dmsf_file_revision_id] - - attr_accessible :dmsf_workflow_step_id, :user_id, :dmsf_file_revision_id - + def add?(dmsf_file_revision_id) if self.dmsf_file_revision_id == dmsf_file_revision_id add = true self.dmsf_workflow_step_actions.each do |action| - if action.is_finished? + if action.is_finished? add = false break end diff --git a/app/views/dmsf/_custom_fields.html.erb b/app/views/dmsf/_custom_fields.html.erb index 35db5d05..0222e6a7 100644 --- a/app/views/dmsf/_custom_fields.html.erb +++ b/app/views/dmsf/_custom_fields.html.erb @@ -1,7 +1,11 @@ -<%# Redmine plugin for Document Management System "Features" +<% +# encoding: utf-8 # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,11 +22,12 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.%> <% if object %> -
    +
    <% object.show_custom_field_values.each do |custom_value| %> -
    - <%= label_tag('', "#{h(custom_value.custom_field.name)}:") %> <%= show_value(custom_value) %> -
    +

    + <%= label_tag('', h(custom_value.custom_field.name)) %> + <%= show_value custom_value %> +

    <% end %>
    <% end %> \ No newline at end of file diff --git a/app/views/dmsf/_dir.html.erb b/app/views/dmsf/_dir.html.erb index de2714cc..4a136309 100644 --- a/app/views/dmsf/_dir.html.erb +++ b/app/views/dmsf/_dir.html.erb @@ -1,9 +1,9 @@ -<%#= +<% # encoding: utf-8 # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -21,84 +21,96 @@ %> <% locked_for_user = subfolder && subfolder.locked_for_user? %> -<% locked = subfolder && subfolder.locked? %> +<% locked = subfolder && subfolder.locked? %> -<%= check_box_tag(name, id, false, +<%= check_box_tag(name, id, false, :title => l(:title_check_for_zip_download_or_email), :id => "subfolder_#{id}") %> - - <%= link_to(h(title), - dmsf_folder_path(:id => project, :folder_id => subfolder), - :class => 'icon icon-folder') %> - <% if link %> -
    <%= link.path %>
    - <% else %> -
    [<%= subfolder.items %>]
    + + <% if @tree_view %> + > <% end %> - - -<%= format_time(subfolder.modified) if subfolder %> + <%= link_to(h(title), + dmsf_folder_path(:id => project, :folder_id => subfolder), + :class => 'icon icon-folder', + :title => h(subfolder.description)) %> + <% if link %> +
    <%= link.path %>
    + <% else %> +
    [<%= subfolder.items %>]
    + <% end %> + + +<%= format_time(subfolder.modified) if subfolder %> <% if locked_for_user %> - <% if subfolder.lock.reverse[0].user %> + <% if subfolder.lock.reverse[0].user %> <%= link_to(image_tag(link ? 'locked_gray.png' : 'locked.png', :plugin => 'redmine_dmsf'), user_path(subfolder.lock.reverse[0].user), :title => l(:title_locked_by_user, :user => subfolder.lock.reverse[0].user)) %> - <% else %> - <%= content_tag(:span, image_tag(link ? 'locked_gray.png' : 'locked.png', :plugin => 'redmine_dmsf'), + <% else %> + <%= content_tag(:span, image_tag(link ? 'locked_gray.png' : 'locked.png', :plugin => 'redmine_dmsf'), :title => l(:notice_account_unknown_email)) %> <% end %> - <% elsif locked %> - <%= content_tag(:span, image_tag(link ? 'lockedbycurrent_gray.png' : 'lockedbycurrent.png', :plugin => 'redmine_dmsf'), + <% elsif locked %> + <%= content_tag(:span, image_tag(link ? 'lockedbycurrent_gray.png' : 'lockedbycurrent.png', :plugin => 'redmine_dmsf'), :title => l(:title_locked_by_you)) %> - <% end %> + <% end %> - - -<%= h(subfolder.user) if subfolder %> - + + +<%= h(subfolder.user) if subfolder %> + <% if @folder_manipulation_allowed %> - <% unless locked %> + <% unless locked_for_user %> <%= link_to(image_tag('edit.png'), edit_dmsf_path(:id => project, :folder_id => subfolder), :title => l(:link_edit, :title => subfolder ? h(subfolder.title) : project.name)) %> <% if subfolder %> - <%= link_to(image_tag('lock.png', :plugin => 'redmine_dmsf'), - lock_dmsf_path(:id => project, :folder_id => subfolder), - :title => l(:title_lock_file)) %> + <% if locked %> + <% if subfolder.unlockable? %> + <%= link_to(image_tag('unlock.png', :plugin => 'redmine_dmsf'), + unlock_dmsf_path(:id => project, :folder_id => subfolder), + :title => l(:title_unlock_file)) %> + <% else %> + + <% end %> + <% else %> + <%= link_to(image_tag('lock.png', :plugin => 'redmine_dmsf'), + lock_dmsf_path(:id => project, :folder_id => subfolder), + :title => l(:title_lock_file)) %> + <% end %> <% else %> <% end %> - <% if (subfolder && subfolder.notification) || (!subfolder && project.dmsf_notification) %> + <% if (subfolder && subfolder.notification) || (!subfolder && project.dmsf_notification) %> <%= link_to(image_tag('notify.png', :plugin => 'redmine_dmsf'), notify_deactivate_dmsf_path(:id => project, :folder_id => subfolder), :title => l(:title_notifications_active_deactivate)) %> - <% else %> + <% else %> <%= link_to(image_tag('notifynot.png', :plugin => 'redmine_dmsf'), notify_activate_dmsf_path(:id => project, :folder_id => subfolder), :title => l(:title_notifications_not_active_activate)) %> <% end %> <% if link %> - <%= link_to(image_tag('delete.png'), - dmsf_link_path(link), + <%= link_to(image_tag('delete.png'), dmsf_link_path(link), :data => {:confirm => l(:text_are_you_sure)}, - :method => :delete, - :title => l(:title_delete)) %> + :method => :delete, :title => l(:title_delete)) %> <% else %> <%= link_to(image_tag('delete.png'), delete_dmsf_path(:id => project, :folder_id => subfolder), - :data => {:confirm => l(:text_are_you_sure)}, - :title => l(:title_delete)) %> - <% end %> - <% else %> + :data => {:confirm => l(:text_are_you_sure)}, + :title => l(:title_delete)) %> + <% end %> + <% else %> <% if (!locked_for_user || @force_file_unlock_allowed) && subfolder.unlockable? %> <%= link_to(image_tag('unlock.png', :plugin => 'redmine_dmsf'), unlock_dmsf_path(:id => project, :folder_id => subfolder), - :title => l(:title_unlock_file)) %> - <% end %> - <% end %> + :title => l(:title_unlock_file)) %> + <% end %> + <% end %> <% end %> -0 -0 -<%= subfolder.modified.to_i if subfolder %> -0 \ No newline at end of file +<%= position %> +0 +<%= subfolder.modified.to_i if subfolder %> +0 diff --git a/app/views/dmsf/_dir_trash.html.erb b/app/views/dmsf/_dir_trash.html.erb index 3867abbf..3a742703 100644 --- a/app/views/dmsf/_dir_trash.html.erb +++ b/app/views/dmsf/_dir_trash.html.erb @@ -1,9 +1,9 @@ -<%#= +<% # encoding: utf-8 # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,37 +20,48 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. %> -<%= check_box_tag(name, id, false, +<%= check_box_tag(name, id, false, :title => l(:title_check_for_restore_or_delete), :id => "subfolder_#{id}") %> - + <%= content_tag(:span, h(title), - :title => h(title), + :title => h(subfolder.description), :class => 'icon icon-folder') %> <% if link %> -
    <%= link.path %>
    +
    <%= link.path %>
    <% else %> -
    [<%= subfolder.items %>]
    +
    [<%= subfolder.items %>]
    <% end %> - - + + <%= format_time(subfolder.modified) if subfolder %> - - -<%= h(subfolder.user) %> - - <% if @folder_manipulation_allowed %> - <%= link_to(image_tag('restore.png', :plugin => 'redmine_dmsf'), - restore_dmsf_path(:id => project, :folder_id => subfolder), - :title => l(:title_restore)) %> - <%= link_to(image_tag('rev_delete.png', :plugin => 'redmine_dmsf'), - delete_dmsf_path(:id => project, :folder_id => subfolder, :commit => 'yes'), - :data => {:confirm => l(:text_are_you_sure)}, - :title => l(:title_delete)) %> + + +<%= h(subfolder.user) %> + + <% if @folder_manipulation_allowed %> + <% if link %> + <%= link_to(image_tag('restore.png', :plugin => 'redmine_dmsf'), + restore_dmsf_link_path(:id => link), + :title => l(:title_restore)) %> + <%= link_to(image_tag('rev_delete.png', :plugin => 'redmine_dmsf'), + dmsf_link_path(:id => link, :commit => 'yes'), + :data => {:confirm => l(:text_are_you_sure)}, + :method => :delete, + :title => l(:title_delete)) %> + <% else # folder %> + <%= link_to(image_tag('restore.png', :plugin => 'redmine_dmsf'), + restore_dmsf_path(:id => project, :folder_id => subfolder), + :title => l(:title_restore)) %> + <%= link_to(image_tag('rev_delete.png', :plugin => 'redmine_dmsf'), + delete_dmsf_path(:id => project, :folder_id => subfolder, :commit => 'yes'), + :data => {:confirm => l(:text_are_you_sure)}, + :title => l(:title_delete)) %> + <% end %> <% end %> -0 -0 -<%= subfolder.modified.to_i if subfolder %> -0 \ No newline at end of file +0 +0 +<%= subfolder.modified.to_i if subfolder %> +0 \ No newline at end of file diff --git a/app/views/dmsf/_file.html.erb b/app/views/dmsf/_file.html.erb index c2f81af7..951b8e01 100644 --- a/app/views/dmsf/_file.html.erb +++ b/app/views/dmsf/_file.html.erb @@ -1,9 +1,9 @@ -<%#= +<% # encode: utf-8 # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,47 +20,49 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. %> -<% locked_for_user = file.locked_for_user? %> -<% locked = file.locked? %> <% wf = DmsfWorkflow.find_by_id(file.last_revision.dmsf_workflow_id) %> -<%= check_box_tag(name, id, false, +<%= check_box_tag(name, id, false, :title => l(:title_check_for_zip_download_or_email), :id => "file_#{id}") %> - + + <% if @tree_view %> + > + <% end %> <% file_view_url = url_for({:controller => :dmsf_files, :action => 'view', :id => file}) %> <%= link_to(h(title), file_view_url, - :target => "_blank", + :target => '_blank', :class => "icon icon-file #{DmsfHelper.filetype_css(file.name)}", - :title => l(:title_title_version_version_download, :title => h(file.title), :version => file.version), + :title => file.last_revision.try(:tooltip), 'data-downloadurl' => "#{file.last_revision.detect_content_type}:#{h(file.name)}:#{file_view_url}") %> -
    <%= h(link ? link.path : file.display_name) %>
    +
    <%= h(link ? link.path : file.display_name) %>
    + <%= ''.html_safe if @tree_view %> -<%= number_to_human_size(file.last_revision.size) %> - +<%= number_to_human_size(file.last_revision.size) %> + <%= format_time(file.last_revision.updated_at) %> - <% if locked_for_user %> - <% if file.lock.reverse[0].user %> + <% if file.locked_for_user? %> + <% if file.lock.reverse[0].user %> <%= link_to(image_tag(link ? 'locked_gray.png' : 'locked.png', :plugin => 'redmine_dmsf'), user_path(file.lock.reverse[0].user), :title => l(:title_locked_by_user, :user => file.lock.reverse[0].user)) %> - <% else %> - <%= content_tag(:span, image_tag(link ? 'locked_gray.png' : 'locked.png', :plugin => 'redmine_dmsf'), + <% else %> + <%= content_tag(:span, image_tag(link ? 'locked_gray.png' : 'locked.png', :plugin => 'redmine_dmsf'), :title => l(:notice_account_unknown_email)) %> <% end %> - <% elsif locked %> - <%= content_tag(:span, image_tag(link ? 'lockedbycurrent_gray.png' : 'lockedbycurrent.png', :plugin => 'redmine_dmsf'), + <% elsif file.locked? %> + <%= content_tag(:span, image_tag(link ? 'lockedbycurrent_gray.png' : 'lockedbycurrent.png', :plugin => 'redmine_dmsf'), :title => l(:title_locked_by_you)) %> <% end %> -<%= file.last_revision.version %> - +<%= file.last_revision.version %> + <% if wf && @file_approval_allowed %> <%= link_to( file.last_revision.workflow_str(false), log_dmsf_workflow_path( - :project_id => project.id, - :id => wf.id, + :project_id => project.id, + :id => wf.id, :dmsf_file_revision_id => file.last_revision.id), :title => DmsfWorkflow.assignments_to_users_str(wf.next_assignments(file.last_revision.id)), :remote => true) %> @@ -68,110 +70,115 @@ <%= file.last_revision.workflow_str(false) %> <% end %> -<%= h(file.last_revision.user) %> - - <% if @file_manipulation_allowed || @file_approval_allowed %> - <% if @file_manipulation_allowed %> - <% unless locked %> - <%= link_to(image_tag('filedetails.png', :plugin => 'redmine_dmsf'), - dmsf_file_path(:id => file), - :title => l(:link_details, :title => h(file.last_revision.title))) %> +<%= h(file.last_revision.user) %> + + <% if @file_manipulation_allowed %> + <%= link_to(image_tag('filedetails.png', :plugin => 'redmine_dmsf'), + dmsf_file_path(:id => file), + :title => l(:link_details, :title => h(file.last_revision.title))) %> + <% unless file.locked_for_user? %> + <% if !file.locked? %> <%= link_to(image_tag('lock.png', :plugin => 'redmine_dmsf'), lock_dmsf_files_path(:id => file), - :title => l(:title_lock_file)) %> - <% if file.notification %> - <%= link_to(image_tag('notify.png', :plugin => 'redmine_dmsf'), - notify_deactivate_dmsf_files_path(:id => file), - :title => l(:title_notifications_active_deactivate)) %> - <% else %> - <%= link_to(image_tag('notifynot.png', :plugin => 'redmine_dmsf'), - notify_activate_dmsf_files_path(:id => file), - :title => l(:title_notifications_not_active_activate)) %> - <% end %> - <% if link %> - <%= link_to(image_tag('delete.png'), - dmsf_link_path(link), - :data => {:confirm => l(:text_are_you_sure)}, - :method => :delete, - :title => l(:title_delete)) %> - <% else %> - <% if @file_delete_allowed %> - <%= link_to(image_tag('delete.png'), - dmsf_file_path(:id => file), - :data => {:confirm => l(:text_are_you_sure)}, - :method => :delete, - :title => l(:title_delete)) unless locked_for_user %> - <% else %> - - <% end %> - <% end %> + :title => l(:title_lock_file)) %> + <% elsif file.unlockable? %> + <%= link_to(image_tag('unlock.png', :plugin => 'redmine_dmsf'), + unlock_dmsf_files_path(:id => file), + :title => l(:title_unlock_file))%> <% else %> - <% if (!locked_for_user || @force_file_unlock_allowed) && file.unlockable? %> - <%= link_to(image_tag('unlock.png', :plugin => 'redmine_dmsf'), - unlock_dmsf_files_path(:id => file), - :title => l(:title_unlock_file))%> + <% end %> + <% if file.notification %> + <%= link_to(image_tag('notify.png', :plugin => 'redmine_dmsf'), + notify_deactivate_dmsf_files_path(:id => file), + :title => l(:title_notifications_active_deactivate)) %> + <% else %> + <%= link_to(image_tag('notifynot.png', :plugin => 'redmine_dmsf'), + notify_activate_dmsf_files_path(:id => file), + :title => l(:title_notifications_not_active_activate)) %> + <% end %> + <% if link %> + <%= link_to(image_tag('delete.png'), + dmsf_link_path(link), + :data => {:confirm => l(:text_are_you_sure)}, + :method => :delete, + :title => l(:title_delete)) %> + <% else %> + <% if @file_delete_allowed %> + <%= link_to(image_tag('delete.png'), + dmsf_file_path(:id => file), + :data => {:confirm => l(:text_are_you_sure)}, + :method => :delete, + :title => l(:title_delete)) unless file.locked_for_user? %> <% else %> - <% end %> - - + <% end %> <% end %> + <% else %> + <% if @force_file_unlock_allowed && file.unlockable? %> + <%= link_to(image_tag('unlock.png', :plugin => 'redmine_dmsf'), + unlock_dmsf_files_path(:id => file), + :title => l(:title_unlock_file))%> + <% else %> + + <% end %> + + <% end %> - <% if @file_approval_allowed %> - <% case file.last_revision.workflow %> - <% when DmsfWorkflow::STATE_WAITING_FOR_APPROVAL %> - <% if wf %> - <% assignments = wf.next_assignments(file.last_revision.id) %> - <% index = assignments.find_index{|assignment| assignment.user_id == User.current.id} if assignments %> - <% if index %> - <%= link_to(image_tag('waiting_for_approval.png', :plugin => 'redmine_dmsf'), - action_dmsf_workflow_path( - :project_id => project.id, - :id => wf.id, - :dmsf_workflow_step_assignment_id => assignments[index].id, - :dmsf_file_revision_id => file.last_revision.id), - :title => l(:title_waiting_for_approval), - :remote => true) %> - <% else %> - <%= content_tag(:span, image_tag('waiting_for_approval.png', :plugin => 'redmine_dmsf'), - :title => "#{l(:label_dmsf_wokflow_action_approve)} #{l(:label_dmsf_wokflow_action_reject)} #{l(:label_dmsf_wokflow_action_delegate)}") %> - <% end %> - <% else %> + <% end %> + <% if @file_approval_allowed %> + <% case file.last_revision.workflow %> + <% when DmsfWorkflow::STATE_WAITING_FOR_APPROVAL %> + <% if wf %> + <% assignments = wf.next_assignments(file.last_revision.id) %> + <% index = assignments.find_index{|assignment| assignment.user_id == User.current.id} if assignments %> + <% if index %> + <%= link_to(image_tag('waiting_for_approval.png', :plugin => 'redmine_dmsf'), + action_dmsf_workflow_path( + :project_id => project.id, + :id => wf.id, + :dmsf_workflow_step_assignment_id => assignments[index].id, + :dmsf_file_revision_id => file.last_revision.id), + :title => l(:title_waiting_for_approval), + :remote => true) %> + <% else %> <%= content_tag(:span, image_tag('waiting_for_approval.png', :plugin => 'redmine_dmsf'), :title => "#{l(:label_dmsf_wokflow_action_approve)} #{l(:label_dmsf_wokflow_action_reject)} #{l(:label_dmsf_wokflow_action_delegate)}") %> <% end %> - <% when DmsfWorkflow::STATE_APPROVED %> - <%= content_tag(:span, image_tag('approved.png', :plugin => 'redmine_dmsf'), - :title => l(:title_approved)) %> - <% when DmsfWorkflow::STATE_ASSIGNED %> - <% if User.current && (file.last_revision.dmsf_workflow_assigned_by == User.current.id) && wf %> - <%= link_to(image_tag('assigned.png', :plugin => 'redmine_dmsf'), - start_dmsf_workflow_path( - :id => file.last_revision.dmsf_workflow_id, - :dmsf_file_revision_id => file.last_revision.id), - :title => l(:label_dmsf_wokflow_action_start)) %> - <% else %> - <%= content_tag(:span, image_tag('assigned.png', :plugin => 'redmine_dmsf'), - title => l(:label_dmsf_wokflow_action_start)) %> - <% end %> - <% when DmsfWorkflow::STATE_REJECTED %> - <%= content_tag(:span, image_tag('assigned.png', :plugin => 'redmine_dmsf'), - :title => l(:title_rejected)) %> <% else %> - <% if @workflows_available %> - <%= link_to(image_tag('none.png', :plugin => 'redmine_dmsf'), - assign_dmsf_workflow_path( - :project_id => project.id, - :dmsf_file_revision_id => file.last_revision.id), - :title => l(:label_dmsf_wokflow_action_assign), - :remote => true) %> - <% end %> - <% end %> - <% end %> + <%= content_tag(:span, image_tag('waiting_for_approval.png', :plugin => 'redmine_dmsf'), + :title => "#{l(:label_dmsf_wokflow_action_approve)} #{l(:label_dmsf_wokflow_action_reject)} #{l(:label_dmsf_wokflow_action_delegate)}") %> + <% end %> + <% when DmsfWorkflow::STATE_APPROVED %> + <%= content_tag(:span, image_tag('approved.png', :plugin => 'redmine_dmsf'), + :title => l(:title_approved)) %> + <% when DmsfWorkflow::STATE_ASSIGNED %> + <% if User.current && (file.last_revision.dmsf_workflow_assigned_by == User.current.id) && wf %> + <%= link_to(image_tag('assigned.png', :plugin => 'redmine_dmsf'), + start_dmsf_workflow_path( + :id => file.last_revision.dmsf_workflow_id, + :dmsf_file_revision_id => file.last_revision.id), + :title => l(:label_dmsf_wokflow_action_start)) %> + <% else %> + <%= content_tag(:span, image_tag('assigned.png', :plugin => 'redmine_dmsf'), + title => l(:label_dmsf_wokflow_action_start)) %> + <% end %> + <% when DmsfWorkflow::STATE_REJECTED %> + <%= content_tag(:span, image_tag('rejected.png', :plugin => 'redmine_dmsf'), + :title => l(:title_rejected)) %> + <% else %> + <% if @workflows_available && !file.locked_for_user? %> + <%= link_to(image_tag('none.png', :plugin => 'redmine_dmsf'), + assign_dmsf_workflow_path( + :project_id => project.id, + :dmsf_file_revision_id => file.last_revision.id), + :title => l(:label_dmsf_wokflow_action_assign), + :remote => true) %> + <% end %> + <% end %> <% end %> -1 -<%= file.last_revision.size %> -<%= file.last_revision.updated_at.to_i %> -<%= file.last_revision.iversion %> \ No newline at end of file +<%= position %> +<%= file.last_revision.size %> +<%= file.last_revision.updated_at.to_i %> +<%= file.last_revision.iversion %> diff --git a/app/views/dmsf/_file_trash.html.erb b/app/views/dmsf/_file_trash.html.erb index 9c25af8d..bd84be1f 100644 --- a/app/views/dmsf/_file_trash.html.erb +++ b/app/views/dmsf/_file_trash.html.erb @@ -1,9 +1,9 @@ -<%#= +<% # encoding: utf-8 # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,36 +20,47 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. %> -<%= check_box_tag(name, id, false, +<%= check_box_tag(name, id, false, :title => l(:title_check_for_restore_or_delete), :id => "file_#{id}") %> - + <%= content_tag(:span, h(title), - :title => h(title), + :title => h(file.last_revision.try(:tooltip)), :class => "icon icon-file #{DmsfHelper.filetype_css(file.name)}") %> -
    <%= h(link ? link.path : file.display_name) %>
    +
    <%= h(link ? link.path : file.display_name) %>
    -<%= number_to_human_size(file.last_revision.size) %> - +<%= number_to_human_size(file.last_revision.size) %> + <%= format_time(file.last_revision.updated_at) %> -<%= file.last_revision.version %> - +<%= file.last_revision.version %> + <%= file.last_revision.workflow_str(false) %> -<%= h(file.last_revision.user) %> - - <% if @file_manipulation_allowed %> - <%= link_to(image_tag('restore.png', :plugin => 'redmine_dmsf'), - restore_dmsf_file_path(:id => file), - :title => l(:title_restore)) %> - <%= link_to(image_tag('rev_delete.png', :plugin => 'redmine_dmsf'), - dmsf_file_path(:id => file, :commit => 'yes'), - :data => {:confirm => l(:text_are_you_sure)}, - :method => :delete, - :title => l(:title_delete)) %> +<%= h(file.last_revision.user) %> + + <% if @file_manipulation_allowed %> + <% if link %> + <%= link_to(image_tag('restore.png', :plugin => 'redmine_dmsf'), + restore_dmsf_link_path(:id => link), + :title => l(:title_restore)) %> + <%= link_to(image_tag('rev_delete.png', :plugin => 'redmine_dmsf'), + dmsf_link_path(:id => link, :commit => 'yes'), + :data => {:confirm => l(:text_are_you_sure)}, + :method => :delete, + :title => l(:title_delete)) %> + <% else # file %> + <%= link_to(image_tag('restore.png', :plugin => 'redmine_dmsf'), + restore_dmsf_file_path(:id => file), + :title => l(:title_restore)) %> + <%= link_to(image_tag('rev_delete.png', :plugin => 'redmine_dmsf'), + dmsf_file_path(:id => file, :commit => 'yes'), + :data => {:confirm => l(:text_are_you_sure)}, + :method => :delete, + :title => l(:title_delete)) %> + <% end %> <% end %> -1 -<%= file.last_revision.size %> -<%= file.last_revision.updated_at.to_i %> -<%= file.last_revision.iversion %> \ No newline at end of file +1 +<%= file.last_revision.size %> +<%= file.last_revision.updated_at.to_i %> +<%= file.last_revision.iversion %> \ No newline at end of file diff --git a/app/views/dmsf/_list_view.erb b/app/views/dmsf/_list_view.erb new file mode 100644 index 00000000..c89f9113 --- /dev/null +++ b/app/views/dmsf/_list_view.erb @@ -0,0 +1,118 @@ +<% +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman +# +# 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. +%> + + + + + + + + + + + + + + + + + + + + <% @subfolders.each do |subfolder| %> + + <%= render(:partial => 'dir', + :locals => { + :project => @project, + :subfolder => subfolder, + :link => nil, + :id => subfolder.id, + :name => 'subfolders[]', + :title => subfolder.title, + :position => 0 }) %> + + <% end %> + <% @dir_links.each do |link| %> + <% unless link.target_project %> + <% Rails.logger.error "Error: dmsf_link id #{link.id} has no target!" %> + <% next %> + <% end %> + + <%= render(:partial => 'dir', + :locals => { + :project => link.target_project, + :subfolder => link.target_folder, + :link => link, + :id => link.id, + :name => 'dir_links[]', + :title => link.name, + :position => 0}) %> + + <% end %> + <% @files.each do |file| %> + <% unless file.last_revision %> + <% Rails.logger.error "Error: dmsf_file id #{file.id} has no revision!" %> + <% next %> + <% end %> + + <%= render(:partial => 'file', :locals => { + :project => @project, + :file => file, + :link => nil, + :id => file.id, + :name => 'files[]', + :title => file.title, + :position => 1 }) %> + + <% end %> + <% @file_links.each do |link| %> + <% unless link.target_file.last_revision %> + <% Rails.logger.error "Error: dmsf_file id #{link.target_id} has no revision!" %> + <% next %> + <% end %> + + <%= render(:partial => 'file', :locals => { + :project => link.target_project, + :file => link.target_file, + :link => link, + :id => link.id, + :name => 'file_links[]', + :title => link.name, + :position => 1}) %> + + <% end %> + <% @url_links.each do |link| %> + + <%= render(:partial => 'url', :locals => { + :project => link.target_project, + :file => link.target_file, + :link => link, + :id => link.id, + :name => 'file_links[]', + :title => link.name, + :position => 1}) %> + + <% end %> + +
    + + <%= l(:link_title) %><%= l(:link_size) %><%= l(:link_modified) %><%= l(:link_ver) %><%= l(:link_workflow) %><%= l(:link_author) %>
    diff --git a/app/views/dmsf/_path.html.erb b/app/views/dmsf/_path.html.erb index 1534f402..0b19ba3d 100644 --- a/app/views/dmsf/_path.html.erb +++ b/app/views/dmsf/_path.html.erb @@ -17,14 +17,15 @@ # # 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.%> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +%>

    <% if folder %> <%= link_to l(:link_documents), dmsf_folder_path(:id => @project) %> <% folder.dmsf_path.each do |path_element| %> / - <% if !filename && path_element == folder.dmsf_path.last %> + <% if filename.blank? && (path_element == folder.dmsf_path.last) %> <%= h(path_element.title) %> <% else %> <%= link_to h(path_element.title), dmsf_folder_path(:id => @project, :folder_id => path_element) %> @@ -37,4 +38,7 @@ / <%= h(filename) %> <% end %> -

    + <% if title %> + » <%= title %> + <% end %> + \ No newline at end of file diff --git a/app/views/dmsf/_tree_view.erb b/app/views/dmsf/_tree_view.erb new file mode 100644 index 00000000..6c8aed60 --- /dev/null +++ b/app/views/dmsf/_tree_view.erb @@ -0,0 +1,143 @@ +<% +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman +# +# 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. +%> + + + + + + + + + + + + + + + + + + + + <% parent = @folder ? @folder : @project %> + <% DmsfHelper.dmsf_tree(parent, parent).each do |obj, level, position| %> + <% classes = "dmsf_tree idnt-#{level}" %> + <% if obj.is_a?(DmsfFolder) && ((obj.dmsf_folders.visible.count > 0) || (obj.dmsf_files.visible.count > 0) || (obj.dmsf_links.visible.count > 0)) %> + <% classes += ' idnt dmsf_collapsed' %> + <% id = "id='#{obj.id}span'".html_safe %> + <% onclick = "onclick=\"dmsf_toggle('#{obj.id}','#{obj.id}span')\"" %> + <% else %> + <% classes += ' dmsf_child' %> + <% onclick = '' %> + <% end %> + <%if obj.dmsf_folder && obj.dmsf_folder != @folder %> + <% classes += ' dmsf_hidden' %> + <% else %> + <%# Force odd/even backgroung style for visible items %> + <% classes += " dmsf_#{cycle('odd', 'even')}" %> + <% end %> + <% parent = obj.dmsf_folder %> + <% while parent %> + <% classes += " #{parent.id}" %> + <% parent = parent.dmsf_folder %> + <% end %> + + <% if obj.is_a? DmsfFolder %> + class="dir <%= classes %>"> + <%= render(:partial => 'dir', + :locals => { + :project => @project, + :subfolder => obj, + :link => nil, + :id => obj.id, + :name => 'subfolders[]', + :title => obj.title, + :onclick => onclick, + :position => position}) %> + + <% elsif obj.is_a?(DmsfLink) && (obj.target_type == 'DmsfFolder') %> + <% unless obj.target_project %> + <% Rails.logger.error "Error: dmsf_link id #{obj.id} has no target!" %> + <% next %> + <% end %> + class="dmsf_gray <%= classes %>"> + <%= render(:partial => 'dir', + :locals => { + :project => obj.target_project, + :subfolder => obj.target_folder, + :link => obj, + :id => obj.id, + :name => 'dir_links[]', + :title => obj.name, + :onclick => onclick, + :position => position}) %> + + <% elsif obj.is_a?(DmsfFile) %> + <% unless obj.last_revision %> + <% Rails.logger.error "Error: dmsf_file id #{obj.id} has no revision!" %> + <% next %> + <% end %> + class="file <%= classes %>"> + <%= render(:partial => 'file', :locals => { + :project => @project, + :file => obj, + :link => nil, + :id => obj.id, + :name => 'files[]', + :title => obj.title, + :onclick => onclick, + :position => position}) %> + + <% elsif obj.is_a?(DmsfLink) && (obj.target_type == 'DmsfFile') %> + <% unless obj.target_file.last_revision %> + <% Rails.logger.error "Error: dmsf_file id #{obj.target_id} has no revision!" %> + <% next %> + <% end %> + class="dmsf_gray <%= classes %>"> + <%= render(:partial => 'file', :locals => { + :project => obj.target_project, + :file => obj.target_file, + :link => obj, + :id => obj.id, + :name => 'file_links[]', + :title => obj.name, + :onclick => onclick, + :position => position}) %> + + <% elsif obj.is_a?(DmsfLink) && (obj.target_type == 'DmsfUrl') %> + class="dmsf_gray <%= classes %>"> + <%= render(:partial => 'url', :locals => { + :project => obj.target_project, + :file => obj.target_file, + :link => obj, + :id => obj.id, + :name => 'file_links[]', + :title => obj.name, + :onclick => onclick, + :position => position}) %> + + <% end %> + <% end %> + +
    + + <%= l(:link_title) %><%= l(:link_size) %><%= l(:link_modified) %><%= l(:link_ver) %><%= l(:link_workflow) %><%= l(:link_author) %>
    diff --git a/app/views/dmsf/_url.html.erb b/app/views/dmsf/_url.html.erb index 9b7848c0..cafab4a4 100644 --- a/app/views/dmsf/_url.html.erb +++ b/app/views/dmsf/_url.html.erb @@ -1,9 +1,9 @@ -<%#= +<% # encoding: utf-8 # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,39 +20,40 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. %> - - - <%= link_to(h(title), - link.external_url, - :target => "_blank", - :class => 'icon icon-link') %> -
    <%= link.external_url %>
    + + + <% if @tree_view %> + > + <% end %> + <%= link_to(h(title), + link.external_url, + :target => '_blank', + :class => 'icon dmsf_icon-link') %> +
    + <%= link.external_url %> +
    + <%= ''.html_safe if @tree_view %> - -<%= format_time(link.updated_at) %> - - -<%= h(link.user) %> - - <% if @file_manipulation_allowed %> - <% if @file_manipulation_allowed %> - - - - <%= link_to(image_tag('delete.png'), - dmsf_link_path(link), - :data => {:confirm => l(:text_are_you_sure)}, - :method => :delete, - :title => l(:title_delete)) %> - <% else %> - - - - - <% end %> + +<%= format_time(link.updated_at) %> + + +<%= h(link.user) %> + + + + + <% if @file_manipulation_allowed %> + <%= link_to(image_tag('delete.png'), + dmsf_link_path(link), + :data => {:confirm => l(:text_are_you_sure)}, + :method => :delete, + :title => l(:title_delete)) %> + <% else %> + <% end %> -1 - -link.updated_at.to_i - +<%= position %> + +link.updated_at.to_i + diff --git a/app/views/dmsf/_url_trash.html.erb b/app/views/dmsf/_url_trash.html.erb index 926e9bf7..73eaf83f 100644 --- a/app/views/dmsf/_url_trash.html.erb +++ b/app/views/dmsf/_url_trash.html.erb @@ -1,9 +1,9 @@ -<%#= +<% # encoding: utf-8 # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,21 +20,21 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. %> -<%= check_box_tag(name, id, false, +<%= check_box_tag(name, id, false, :title => l(:title_check_for_restore_or_delete)) %> - + <%= link_to(h(title), link.external_url, - :target => "_blank", - :class => 'icon icon-link') %> -
    <%= link.external_url %>
    + :target => '_blank', + :class => 'icon dmsf_icon-link') %> +
    <%= link.external_url %>
    - -<%= format_time(link.updated_at) %> - - -<%= h(link.user) %> - + +<%= format_time(link.updated_at) %> + + +<%= h(link.user) %> + <% if @file_manipulation_allowed %> <%= link_to(image_tag('restore.png', :plugin => 'redmine_dmsf'), restore_dmsf_link_path(:id => link), @@ -46,7 +46,7 @@ :title => l(:title_delete)) %> <% end %> -1 - -link.updated_at.to_i - \ No newline at end of file +1 + +link.updated_at.to_i + \ No newline at end of file diff --git a/app/views/dmsf/edit.html.erb b/app/views/dmsf/edit.html.erb index d21f21de..eb3fbb44 100644 --- a/app/views/dmsf/edit.html.erb +++ b/app/views/dmsf/edit.html.erb @@ -1,7 +1,11 @@ -<%# Redmine plugin for Document Management System "Features" +<% +# encoding: utf-8 # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -15,86 +19,82 @@ # # 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.%> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +%> <% html_title(l(:dmsf)) %>
    - <% if User.current.allowed_to?(:folder_manipulation, @project) && params[:action] == 'edit' %> - <% unless @folder.locked? %> - <%= link_to(l(:button_lock), + <% if !@folder.new_record? && User.current.allowed_to?(:folder_manipulation, @project) %> + <% if !@folder.locked_for_user? %> + <% unless @folder.locked? %> + <%= link_to(l(:button_lock), lock_dmsf_path(:id => @project, :folder_id => @folder), - :title => l(:title_lock_file), - :class => 'icon icon-dmsf-lock') %> + :title => l(:title_lock_file), :class => 'icon dmsf_icon-lock') %> + <% else %> + <%= link_to_if(@folder.unlockable?, l(:button_unlock), + unlock_dmsf_path(:id => @project, :folder_id => @folder), + :title => l(:title_unlock_file), :class => 'icon dmsf_icon-unlock')%> + <% end %> <% if @folder.notification %> <%= link_to(l(:label_notifications_off), notify_deactivate_dmsf_path(:id => @project, :folder_id => @folder), :title => l(:title_notifications_active_deactivate), - :class => 'icon icon-notification-on') %> + :class => 'icon dmsf_icon-notification-on') %> <% else %> <%= link_to(l(:label_notifications_on), notify_activate_dmsf_path(:id => @project, :folder_id => @folder), :title => l(:title_notifications_not_active_activate), - :class => 'icon icon-notification-off') %> + :class => 'icon dmsf_icon-notification-off') %> <% end %> <%= link_to(l(:label_link_to), new_dmsf_link_path(:project_id => @project.id, :dmsf_folder_id => @folder.id, :type => 'link_to'), - :title => l(:title_create_link), - :class => 'icon icon-link') %> + :title => l(:title_create_link), :class => 'icon dmsf_icon-link') %> <%= link_to(l(:button_copy), copy_folder_path(:id => @folder), :title => l(:title_copy), :class => 'icon icon-copy') %> <%= 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') %> - <% else %> - <% if (!@folder.locked_for_user? || User.current.allowed_to?(:force_file_unlock, @project)) && @folder.unlockable? %> - <%= link_to(l(:button_unlock), - unlock_dmsf_path(:id => @project, :folder_id => @folder), - :title => l(:title_unlock_file), - :class => 'icon icon-dmsf-unlock')%> - <% end %> + :title => l(:title_delete), :class => 'icon icon-del') %> + <% elsif @force_file_unlock_allowed %> + <%= link_to_if(@folder.unlockable?, l(:button_unlock), + unlock_dmsf_path(:id => @project, :folder_id => @folder), + :title => l(:title_unlock_file), :class => 'icon dmsf_icon-unlock')%> <% end %> <% end %>
    <% create = @pathfolder == @parent %> -<%= render(:partial => 'path', :locals => {:folder => @pathfolder, :filename => create ? l(:heading_new_folder) : nil}) %> +<%= render(:partial => 'path', + :locals => {:folder => @pathfolder, :filename => create ? l(:heading_new_folder) : nil, :title => nil}) %> -<%= form_for(@folder, :url => {:action => create ? 'create' : 'save', :id => @project, :folder_id => @folder, :parent_id => @parent}, +<%= labelled_form_for(@folder, :url => {:action => create ? 'create' : 'save', :id => @project, :folder_id => @folder, :parent_id => @parent}, :html => {:method=>:post}) do |f| %> <%= error_messages_for(@folder) %> -
    -
    -
    -

    - <%= label_tag('dmsf_folder_title', "#{l(:label_title)}:") %> - <%= f.text_field(:title, :size => '32', :required => true) %> -

    -
    -
    -

    - <%= label_tag('', "#{l(:field_folder)}:") %> - <%= f.select(:dmsf_folder_id, - options_for_select(DmsfFolder.directory_tree(@project, @folder), - :selected => @parent? @parent.id : (@pathfolder.id if @pathfolder))) - %> -

    -
    -
    -

    - <%= label_tag('dmsf_folder_description', "#{l(:label_description)}:") %> +

    +

    + <%= f.text_field(:title, :required => true) %> +

    +

    + <%= f.select(:dmsf_folder_id, + options_for_select(DmsfFolder.directory_tree(@project, @folder), + :selected => @parent? @parent.id : (@pathfolder.id if @pathfolder)), + :label => l(:field_folder)) + %> +

    +

    + <%= f.text_area :description, :rows => 8, :class => 'wiki-edit' %>

    -
    - <%= f.text_area(:description, :rows => 15, :class => 'wiki-edit') %> -
    <% values = @folder ? @folder.custom_field_values : @parent ? @parent.custom_field_values : DmsfFolder.new(:project => @project).custom_field_values %> <% values.each do |value| %>

    <%= custom_field_tag_with_label(:dmsf_folder, value) %>

    - <% end %> -
    - <%= submit_tag(create ? l(:submit_create) : l(:submit_save)) %> + <% end %> +
    +

    <%= submit_tag(create ? l(:submit_create) : l(:submit_save)) %>

    <% end %> <%= wikitoolbar_for 'dmsf_folder_description' %> + + \ No newline at end of file diff --git a/app/views/dmsf/edit_root.html.erb b/app/views/dmsf/edit_root.html.erb index c16163be..7c33f1aa 100644 --- a/app/views/dmsf/edit_root.html.erb +++ b/app/views/dmsf/edit_root.html.erb @@ -1,7 +1,11 @@ -<%# Redmine plugin for Document Management System "Features" +<% +# encoding: utf-8 # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -15,7 +19,8 @@ # # 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.%> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +%> <% html_title(l(:dmsf)) %> @@ -25,29 +30,28 @@ <%= link_to(l(:label_notifications_off), notify_deactivate_dmsf_path(:id => @project), :title => l(:title_notifications_active_deactivate), - :class => 'icon icon-notification-on') %> + :class => 'icon dmsf_icon-notification-on') %> <% else %> <%= link_to(l(:label_notifications_on), notify_activate_dmsf_path(:id => @project), :title => l(:title_notifications_active_deactivate), - :class => 'icon icon-notification-off') %> + :class => 'icon dmsf_icon-notification-off') %> <% end %> <% end %>
    -<%= render(:partial => 'path', :locals => {:folder => nil, :filename => nil}) %> +<%= render(:partial => 'path', + :locals => {:folder => nil, :filename => nil, :title => nil}) %> -<%= form_for(@project, :url => {:action => 'save_root', :id => @project}, - :html => {:method=>:post}) do |f| %> -
    -

    - <%= label_tag('project_dmsf_description', "#{l(:label_description)}:") %> +<%= labelled_form_for(@project, :url => {:action => 'save_root', :id => @project}, + :html => {:method => :post}) do |f| %> +

    +

    + <%= f.text_area(:dmsf_description, :rows => 8, :class => 'wiki-edit', + :label => l(:field_description)) %>

    -
    - <%= f.text_area(:dmsf_description, :rows => 15, :class => 'wiki-edit') %> -
    - <%= submit_tag(l(:submit_save)) %> + <%= f.submit l(:submit_save) %> <% end %> <%= wikitoolbar_for 'project_dmsf_description' %> \ No newline at end of file diff --git a/app/views/dmsf/email_entries.html.erb b/app/views/dmsf/email_entries.html.erb index a4b180e0..ada3a4b9 100644 --- a/app/views/dmsf/email_entries.html.erb +++ b/app/views/dmsf/email_entries.html.erb @@ -1,8 +1,11 @@ -<%# Redmine plugin for Document Management System "Features" +<% +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-14 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,50 +19,50 @@ # # 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.%> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +%> <% html_title(l(:dmsf)) %> -
    -
    +<%= render(:partial => 'path', + :locals => {:folder => @folder, :filename => nil, :title => l(:heading_send_documents_by_email)}) %> -<%= render(:partial => 'path', :locals => {:folder => @folder, :filename => nil}) %> - -

    <%= l(:heading_send_documents_by_email) %>

    - -<%= form_tag({:action => 'entries_email', :id => @project, :folder_id => @folder}, - { :method=>:post, :class => 'tabular'}) do %> +<%= form_tag({ :action => 'entries_email', :id => @project, :folder_id => @folder }, + { :method => :post }) do %> <%= hidden_field_tag('email[zipped_content]', @email_params[:zipped_content]) %> <%= hidden_field_tag('email[folders]', @email_params[:folders].to_json) %> <%= hidden_field_tag('email[files]', @email_params[:files].to_json) %> -
    +

    - <%= label_tag('', "#{l(:label_email_from)}:") %> - <%= h(Setting.mail_from) %> + <%= label_tag('', l(:label_email_from)) %> + <%= text_field_tag('email[from]', h(Setting.mail_from), :style => 'width: 90%;', :disabled => true) %>

    - <%= label_tag('email[to]', "#{l(:label_email_to)}:") %> + <%= label_tag('email[to]', l(:label_email_to)) %> <%= text_field_tag('email[to]', @email_params['to'], :style => 'width: 90%;') %>

    - <%= label_tag('email[cc]', "#{l(:label_email_cc)}:") %> + <%= label_tag('email[cc]', l(:label_email_cc)) %> <%= text_field_tag('email[cc]', @email_params['cc'], :style => 'width: 90%;') %>

    - <%= label_tag('email[subject]', "#{l(:label_email_subject)}:") %> + <%= label_tag('email[subject]', l(:label_email_subject)) %> <%= text_field_tag('email[subject]', @email_params['subject'], :style => 'width: 90%;') %>

    - <%= label_tag('', "#{l(:label_email_documents)}:") %> - <%= link_to 'Documents.zip', download_email_entries_path(:id => @project, :folder_id => @folder, :path => @email_params[:zipped_content]) %> - <%= l(:label_or) %> <%= check_box_tag('email[links_only]') %> <%= l(:label_links_only) %> + <%= label_tag('', l(:label_email_documents)) %> + + <%= link_to 'Documents.zip', download_email_entries_path(:id => @project, :folder_id => @folder, :path => @email_params[:zipped_content]) %> + <%= l(:label_or) %> + <%= check_box_tag('email[links_only]') %> <%= l(:label_links_only) %> +

    - <%= label_tag('email[body]', "#{l(:label_email_body)}:") %> - <%= text_area_tag('email[body]', @email_params['body'], :rows=> '20', :style => 'width: 90%;') %> -

    -

    <%= submit_tag(l(:label_email_send)) %>

    + <%= label_tag('email[body]', l(:label_email_body)) %> + <%= text_area_tag('email[body]', @email_params['body'], :rows => '20', :style => 'width: 90%;') %> +

    +

    <%= submit_tag(l(:label_email_send)) %>

    <% end %> -<%= wikitoolbar_for 'email_body' %> +<%= wikitoolbar_for 'email_body' %> \ No newline at end of file diff --git a/app/views/dmsf/show.html.erb b/app/views/dmsf/show.html.erb index 8bf99cff..cdf73cb4 100644 --- a/app/views/dmsf/show.html.erb +++ b/app/views/dmsf/show.html.erb @@ -1,11 +1,11 @@ -<%#= +<% # encoding: utf-8 # # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -22,93 +22,70 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. %> -<% html_title(l(:dmsf)) %> +<% html_title l(:dmsf) %>
    - <% if @folder_manipulation_allowed %> - <% if @folder.nil? %> - <%= link_to(l(:button_edit), - edit_root_dmsf_path(:id => @project), - :title => l(:link_edit, :title => l(:link_documents)), + <% if @folder_manipulation_allowed %> + <% if @folder.nil? %> + <%= link_to(l(:button_edit), edit_root_dmsf_path(:id => @project), + :title => l(:link_edit, :title => l(:link_documents)), :class => 'icon icon-edit') %> - <% elsif !@locked_for_user %> - <%= link_to(l(:button_edit), + <% elsif !@locked_for_user %> + <%= link_to(l(:button_edit), edit_dmsf_path(:id => @project, :folder_id => @folder), - :title => l(:link_edit, :title => h(@folder.title)), + :title => l(:link_edit, :title => h(@folder.title)), :class => 'icon icon-edit') %> <% end %> <% if @folder && (!@locked_for_user || User.current.allowed_to?(:force_file_unlock, @project)) %> <% if @folder.locked? %> - <% unless @folder.unlockable? %> - <%= link_to(l(:button_unlock), - :title => l(:title_folder_parent_locked, :name => @folder.folder.lock.reverse[0].folder.title), - :class => 'icon icon-dmsf-unlock') if @folder %> - <% else %> - <%= link_to(l(:button_unlock), - unlock_dmsf_path(:id => @project, :folder_id => @folder, :current => request.url), - :title => l(:title_unlock_folder), - :class => 'icon icon-dmsf-unlock') if @folder %> - <% end %> + <%= link_to_if(@folder.unlockable?, l(:button_unlock), + unlock_dmsf_path(:id => @project, :folder_id => @folder, :current => request.url), + :title => l(:title_unlock_folder), :class => 'icon dmsf_icon-unlock') %> <% else %> <%= link_to(l(:button_lock), lock_dmsf_path(:id => @project, :folder_id => @folder, :current => request.url), - :title => l(:title_lock_folder), - :class => 'icon icon-dmsf-lock') if @folder %> + :title => l(:title_lock_folder), :class => 'icon dmsf_icon-lock') %> <% end %> <% end %> - <% unless @folder %> - <% if @project.dmsf_notification %> - <%= link_to(l(:label_notifications_off), - notify_deactivate_dmsf_path(:id => @project), - :title => l(:title_notifications_active_deactivate), - :class => 'icon icon-notification-on') %> - <% else %> - <%= link_to(l(:label_notifications_on), - notify_activate_dmsf_path(:id => @project), - :title => l(:title_notifications_active_deactivate), - :class => 'icon icon-notification-off') %> - <% end %> + <% if !@locked_for_user && ((@folder && @folder.notification) || (!@folder && @project.dmsf_notification)) %> + <%= link_to(l(:label_notifications_off), + notify_deactivate_dmsf_path(:id => @project, :folder_id => @folder), + :title => l(:title_notifications_active_deactivate), + :class => 'icon dmsf_icon-notification-on') %> <% else %> - <% if @folder.notification %> - <%= link_to(l(:label_notifications_off), - notify_deactivate_dmsf_path(:id => @project, :folder_id => @folder), - :title => l(:title_notifications_active_deactivate), - :class => 'icon icon-notification-on') %> - <% else %> - <%= link_to(l(:label_notifications_on), - notify_activate_dmsf_path(:id => @project, :folder_id => @folder), - :title => l(:title_notifications_not_active_activate), - :class => 'icon icon-notification-off') %> - <% end %> - <% end %> - <% if @file_manipulation_allowed %> - <%= link_to(l(:label_link_from), + <%= link_to(l(:label_notifications_on), + notify_activate_dmsf_path(:id => @project, :folder_id => @folder), + :title => l(:title_notifications_not_active_activate), + :class => 'icon dmsf_icon-notification-off') %> + <% end %> + <% if @file_manipulation_allowed && !@locked_for_user %> + <%= link_to(l(:label_link_from), new_dmsf_link_path(:project_id => @project.id, :dmsf_folder_id => @folder ? @folder.id : @folder, :type => 'link_from'), - :title => l(:title_create_link), - :class => 'icon icon-link') unless @locked_for_user %> - <% end %> - <%= link_to(l(:link_create_folder), + :title => l(:title_create_link), :class => 'icon dmsf_icon-link') %> + <% end %> + <%= link_to(l(:link_create_folder), new_dmsf_path(:id => @project, :parent_id => @folder), - :title => l(:link_create_folder), + :title => l(:link_create_folder), :class => 'icon icon-add') unless @locked_for_user %> <% end %> <%= link_to_if(@trash_enabled, l(:link_trash_bin), trash_dmsf_path(@project), :title => l(:link_trash_bin), :class => 'icon icon-del') if @trash_visible %>
    -<%= render(:partial => 'path', :locals => {:folder => @folder, :filename => nil}) %> +<%= render(:partial => 'path', + :locals => {:folder => @folder, :filename => nil, :title => nil}) %>
    <%= textilizable(@folder ? @folder.description : @project.dmsf_description) %> -
    +
    <%= error_messages_for('dmsf_workflow') %> <%= form_tag({:action => :entries_operation, :id => @project, :folder_id => @folder}, :method => :post, - :class => 'dmfs_entries', :id => 'entries_form') do %> + :class => 'dmsf_entries', :id => 'entries_form') do %> <%= hidden_field_tag('action') %> -
    +
    <%= submit_tag(l(:button_download), :title => l(:title_download_checked), :name => 'download_entries') if @file_view_allowed %> <%= submit_tag(l(:field_mail), :title => l(:title_send_checked_by_email), :name => 'email_entries') if (@file_view_allowed && User.current.allowed_to?(:email_documents, @project)) %> <% if @file_delete_allowed%> @@ -117,156 +94,80 @@
    <% values = @folder ? @folder.custom_field_values : @parent ? @parent.custom_field_values : DmsfFolder.new(:project => @project).custom_field_values %> <% unless values.empty? %> -
    +
    <%= custom_field_tag_with_label( - :dmsf_folder, + :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])) %> -
    - <% end %> - - - - - - - - - - - - - - - - - - - <% @subfolders.each do |subfolder| %> - - <%= render(:partial => 'dir', - :locals => { - :project => @project, - :subfolder => subfolder, - :link => nil, - :id => subfolder.id, - :name => 'subfolders[]', - :title => subfolder.title }) %> - - <% end %> - <% @dir_links.each do |link| %> - <% unless link.target_project %> - <% Rails.logger.error "Error: dmsf_link id #{link.id} has no target!" %> - <% next %> - <% end %> - - <%= render(:partial => 'dir', - :locals => { - :project => link.target_project, - :subfolder => link.target_folder, - :link => link, - :id => link.id, - :name => 'dir_links[]', - :title => link.name }) %> - - <% end %> - <% @files.each do |file| %> - <% unless file.last_revision %> - <% Rails.logger.error "Error: dmsf_file id #{file.id} has no revision!" %> - <% next %> - <% end %> - - <%= render(:partial => 'file', :locals => { - :project => @project, - :file => file, - :link => nil, - :id => file.id, - :name => 'files[]', - :title => file.title }) %> - + <% end %> - <% @file_links.each do |link| %> - <% unless link.target_file.last_revision %> - <% Rails.logger.error "Error: dmsf_file id #{link.target_id} has no revision!" %> - <% next %> - <% end %> - - <%= render(:partial => 'file', :locals => { - :project => link.target_project, - :file => link.target_file, - :link => link, - :id => link.id, - :name => 'file_links[]', - :title => link.name }) %> - +
    + <% if @tree_view %> + <%= render(:partial => 'tree_view') %> + <% else %> + <%= render(:partial => 'list_view') %> <% end %> - <% @url_links.each do |link| %> -
    - <%= render(:partial => 'url', :locals => { - :project => link.target_project, - :file => link.target_file, - :link => link, - :id => link.id, - :name => 'file_links[]', - :title => link.name }) %> - - <% end %> - -
    - - <%= l(:link_title) %><%= l(:link_size) %><%= l(:link_modified) %><%= l(:link_ver) %><%= l(:link_workflow) %><%= l(:link_author) %>
    -<% end %> +
    +<% end %> -<% - sUrl = "jquery.dataTables/#{I18n.locale.to_s.downcase}.json" - sUrl = 'jquery.dataTables/en.json' unless File.exist?(sUrl) +<% + sUrl = 'jquery.dataTables/en.json' + sUrl = "jquery.dataTables/#{I18n.locale.to_s.downcase}.json" if I18n.locale && !I18n.locale.to_s.match(/^en.*/) %> -<% content_for :header_tags do %> +<% content_for :header_tags do %> <%= javascript_include_tag 'bowser.min.js', :plugin => 'redmine_dmsf' %> - <%= stylesheet_link_tag 'jquery.dataTables/jquery-ui.dataTables.css', :plugin => 'redmine_dmsf' %> + <%= stylesheet_link_tag 'jquery.dataTables/jquery-ui.dataTables.css', :plugin => 'redmine_dmsf' %> <%= javascript_include_tag 'jquery.dataTables/jquery.dataTables.min.js', :plugin => 'redmine_dmsf' %> - <% end %> -<%= render(:partial => 'dmsf_upload/multi_upload') if (@file_manipulation_allowed && !@locked_for_user) %> +<% if (@file_manipulation_allowed && !@locked_for_user) %> + <%= render(:partial => 'dmsf_upload/multi_upload') %> +<% end %> \ No newline at end of file diff --git a/app/views/dmsf/trash.html.erb b/app/views/dmsf/trash.html.erb index 3e457f87..9d1af75e 100644 --- a/app/views/dmsf/trash.html.erb +++ b/app/views/dmsf/trash.html.erb @@ -1,9 +1,9 @@ -<%#= +<% # encoding: utf-8 # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -21,83 +21,82 @@ %> <% html_title(l(:dmsf)) %> -
    -
    -

    <%= l(:link_trash_bin) %>

    +

    <%= l(:link_trash_bin) %>

    <%= textilizable(@folder ? @folder.description : @project.dmsf_description) %> -
    +
    <%= error_messages_for('dmsf_workflow') %> <%= form_tag({:action => :entries_operation, :id => @project, :folder_id => @folder}, :method => :post, - :class => 'dmfs_entries', :id => 'entries_form') do %> + :class => 'dmsf_entries', :id => 'entries_form') do %> <%= hidden_field_tag('action') %> -
    +
    <% if @file_manipulation_allowed && @folder_manipulation_allowed %> <%= submit_tag(l(:title_restore), :title => l(:title_restore_checked), :name => 'restore_entries') %> <% if @file_delete_allowed%> <%= submit_tag(l(:button_delete), :title => l(:title_delete_checked), :name => 'destroy_entries') %> <% end %> - <% end %> -
    - + <% end %> + +
    +
    - - + - - - - - - - - - - - + + + + + + + + + + + - - <% @subfolders.each do |subfolder| %> + + <% @subfolders.each do |subfolder| %> - <%= render(:partial => 'dir_trash', - :locals => { - :project => @project, - :subfolder => subfolder, + <%= render(:partial => 'dir_trash', + :locals => { + :project => @project, + :subfolder => subfolder, :link => nil, - :id => subfolder.id, - :name => 'subfolders[]', + :id => subfolder.id, + :name => 'subfolders[]', :title => subfolder.title }) %> - <% end %> - <% @dir_links.each do |link| %> - - <%= render(:partial => 'dir_trash', - :locals => { - :project => link.target_project, - :subfolder => link.target_folder, + <% end %> + <% @dir_links.each do |link| %> + + <%= render(:partial => 'dir_trash', + :locals => { + :project => link.target_project, + :subfolder => link.target_folder, :link => link, - :id => link.id, - :name => 'dir_links[]', + :id => link.id, + :name => 'dir_links[]', :title => link.name }) %> - <% end %> + <% end %> <% @files.each do |file| %> - <% unless file.last_revision %> + <% unless file.last_revision %> <% Rails.logger.error "Error: dmsf_file id #{file.id} has no revision!" %> <% next %> - <% end %> + <% end %> - <%= render(:partial => 'file_trash', :locals => { - :project => @project, - :file => file, + <%= render(:partial => 'file_trash', :locals => { + :project => @project, + :file => file, :link => nil, :id => file.id, :name => 'files[]', @@ -105,14 +104,14 @@ <% end %> <% @file_links.each do |link| %> - <% unless link.target_file.last_revision %> + <% unless link.target_file.last_revision %> <% Rails.logger.error "Error: dmsf_file id #{link.target_id} has no revision!" %> <% next %> - <% end %> - - <%= render(:partial => 'file_trash', :locals => { - :project => link.target_project, - :file => link.target_file, + <% end %> + + <%= render(:partial => 'file_trash', :locals => { + :project => link.target_project, + :file => link.target_file, :link => link, :id => link.id, :name => 'file_links[]', @@ -120,7 +119,7 @@ <% end %> <% @url_links.each do |link| %> - + <%= render(:partial => 'url_trash', :locals => { :project => link.target_project, :file => link.target_file, @@ -131,25 +130,26 @@ <% end %> -
    +
    <%= l(:link_title) %><%= l(:link_size) %><%= l(:link_modified) %><%= l(:link_ver) %><%= l(:link_workflow) %><%= l(:link_author) %><%= l(:link_title) %><%= l(:link_size) %><%= l(:link_modified) %><%= l(:link_ver) %><%= l(:link_workflow) %><%= l(:link_author) %>
    -<% end %> + +
    +<% end %> <% @@ -157,11 +157,11 @@ sUrl = 'jquery.dataTables/en.json' unless File.exist?(sUrl) %> -<% content_for :header_tags do %> - <%= stylesheet_link_tag 'jquery.dataTables/jquery-ui.dataTables.css', :plugin => 'redmine_dmsf' %> +<% content_for :header_tags do %> + <%= stylesheet_link_tag 'jquery.dataTables/jquery-ui.dataTables.css', :plugin => 'redmine_dmsf' %> <%= javascript_include_tag 'jquery.dataTables/jquery.dataTables.min.js', :plugin => 'redmine_dmsf' %> - <% end %> \ No newline at end of file diff --git a/app/views/dmsf_files/_file_new_revision.html.erb b/app/views/dmsf_files/_file_new_revision.html.erb index 5470c50d..27b38162 100644 --- a/app/views/dmsf_files/_file_new_revision.html.erb +++ b/app/views/dmsf_files/_file_new_revision.html.erb @@ -1,11 +1,11 @@ -<%#= +<% # encoding: utf-8 # # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -22,96 +22,81 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. %> -
    +
    <%= l(:heading_new_revision) %> [-]
    <% if @file.locked_for_user? %>

    <%= l(:info_file_locked) %>

    <% else %> - <%= form_for(@revision, :url => { :action => 'create_revision', :id => @file }, + <%= labelled_form_for(@revision, :url => { :action => 'create_revision', :id => @file }, :html => { :method => :post, :multipart => true, :id => 'new_revision_form' }) do |f| %> -
    +
    -

    - <%= label_tag('dmsf_file_revision_title', "#{l(:label_title)}:") %> - <%= f.text_field(:title, :size => 32) %> +

    + <%= f.text_field(:title) %>

    -
    -

    - <%= label_tag('', "#{l(:label_file)}:") %> - <%= f.text_field(:name, :size => 22) %> -

    -
    -
    -

    - <%= label_tag('dmsf_file_revision_description', "#{l(:label_description)}:") %> -

    -
    - <%= f.text_area(:description, :rows => 6, :class => 'wiki-edit') %> -
    -
    -

    - <%= label_tag('version_0', "#{l(:label_version)}:") %> -

    -
    - <%= radio_button_tag('version', 0, - @revision.version == @file.last_revision.version) %> - <%= @file.last_revision.major_version %>.<%= @file.last_revision.minor_version %> - <%= l(:option_version_same) %>
    - <%= radio_button_tag('version', 1, - @revision.major_version == @file.last_revision.major_version && - @revision.minor_version != @file.last_revision.minor_version) %> - <%= @file.last_revision.major_version %>.<%= @file.last_revision.minor_version + 1 %> - <%= l(:option_version_minor) %>
    - <%= radio_button_tag('version', 2, - @revision.major_version != @file.last_revision.major_version) %> - <%= @file.last_revision.major_version + 1 %>.0 - <%= l(:option_version_major) %>
    - <%= radio_button_tag('version', 3) %> - <%= select_tag 'custom_version_major', options_for_select(0..99, @file.last_revision.major_version + 2) %>.<%= select_tag 'custom_version_minor', options_for_select(0..99, @file.last_revision.minor_version + 1) %> - <%= l(:option_version_custom) %> -
    -
    -
    -

    - <%= label_tag('file_upload', "#{l(:label_new_content)}:") %> -

    - - <%= file_field_tag 'file_upload', - :id => 'file_upload', - :class => 'file_selector', - :multiple => false, - :onchange => "$('#dmsf_file_revision_name').val(this.files[0].name)", - :data => { - :max_file_size => Setting.attachment_max_size.to_i.kilobytes, - :max_file_size_message => l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)), - :max_concurrent_uploads => 1, - :upload_path => uploads_path(:format => 'js') - } - %> - (<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>) - -
    -
    +
    +

    + <%= f.text_field(:name, :label => l(:label_file)) %> +

    +
    +
    +

    + <%= f.text_area(:description, :rows => 6, :class => 'wiki-edit') %> +

    +
    +
    +

    + <%= label_tag('version_0', l(:label_dmsf_version)) %> + <%= radio_button_tag('version', 0, @revision.version == @file.last_revision.version) %> + <%= @file.last_revision.major_version %>.<%= @file.last_revision.minor_version %> + <%= l(:option_version_same) %>
    + <%= radio_button_tag('version', 1, @revision.major_version == @file.last_revision.major_version && @revision.minor_version != @file.last_revision.minor_version) %> + <%= @file.last_revision.major_version %>.<%= @file.last_revision.minor_version + 1 %> + <%= l(:option_version_minor) %>
    + <%= radio_button_tag('version', 2, @revision.major_version != @file.last_revision.major_version) %> + <%= @file.last_revision.major_version + 1 %>.0 + <%= l(:option_version_major) %>
    + <%= radio_button_tag('version', 3) %> + <%= select_tag 'custom_version_major', options_for_select(0..99, @file.last_revision.major_version + 2), :onchange => '$("#version_3").prop("checked", true)' %>. + <%= select_tag 'custom_version_minor', options_for_select(0..99, @file.last_revision.minor_version + 1), :onchange => '$("#version_3").prop("checked", true)' %> + <%= l(:option_version_custom) %> +

    +
    +
    +

    + <%= label_tag('file_upload', l(:label_new_content)) %> + + <%= file_field_tag 'file_upload', + :id => 'file_upload', + :class => 'file_selector', + :multiple => false, + :onchange => "$('#dmsf_file_revision_name').val(this.files[0].name)", + :data => { + :max_file_size => Setting.attachment_max_size.to_i.kilobytes, + :max_file_size_message => l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)), + :max_concurrent_uploads => 1, + :upload_path => uploads_path(:format => 'js') + } + %> + (<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>) + +

    +
    +
    <% @revision.custom_field_values.each do |value| %>

    <%= custom_field_tag_with_label(:dmsf_file_revision, value) %>

    <% end %> -
    -
    -

    - <%= label_tag('dmsf_file_revision_comment', "#{l(:label_comment)}:") %> -

    -
    - <%= f.text_area(:comment, :rows=> 2, :style => 'width: 99%;') %> -
    -
    -
    - <%= submit_tag(l(:submit_create)) %> +
    +

    + <%= f.text_area(:comment, :rows => 2, :label => l(:label_comment)) %> +

    + <%= f.submit l(:submit_create) %> <% end %> - <% end %> -
    + <% end %> +
    <%= wikitoolbar_for 'dmsf_file_revision_description' %> \ No newline at end of file diff --git a/app/views/dmsf_files/_revision_access.html.erb b/app/views/dmsf_files/_revision_access.html.erb index a13a736c..e2dd74f4 100644 --- a/app/views/dmsf_files/_revision_access.html.erb +++ b/app/views/dmsf_files/_revision_access.html.erb @@ -1,9 +1,11 @@ -<%#= +<% +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn -# Copyright (C) 2013 Karel Pičman +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,24 +21,24 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. %> -
    - + +
    - - - - + + + + <% revision.access_grouped.each do |access| %> - - - - + + + + <% end %> -
    <%= l(:field_user) %><%= l(:heading_access_downloads_emails) %><%= l(:heading_access_first) %><%= l(:heading_access_last) %><%= l(:field_user) %><%= l(:heading_access_downloads_emails) %><%= l(:heading_access_first) %><%= l(:heading_access_last) %>
    <%= link_to_user(access.user) %><%= access['count'] %><%= format_time(DmsfHelper::to_time(access.first_at)) %><%= format_time(DmsfHelper::to_time(access.last_at)) %><%= link_to_user(access.user) %><%= access.count %><%= format_time(access.first_at) %><%= format_time(access.last_at) %>
    \ No newline at end of file + diff --git a/app/views/dmsf_files/show.html.erb b/app/views/dmsf_files/show.html.erb index c7512dce..191d1d9d 100644 --- a/app/views/dmsf_files/show.html.erb +++ b/app/views/dmsf_files/show.html.erb @@ -1,4 +1,4 @@ -<%#= +<% # encoding: utf-8 # # Redmine plugin for Document Management System "Features" @@ -26,54 +26,63 @@
    <% if User.current.allowed_to?(:file_manipulation, @project) %> - <% unless @file.locked? %> - <%= link_to(l(:button_lock), - lock_dmsf_files_path(:id => @file), - :title => l(:title_lock_file), - :class => 'icon icon-dmsf-lock') %> - <% if @file.notification %> + <% unless @file.locked_for_user? %> + <% unless @file.locked? %> + <%= link_to(l(:button_lock), lock_dmsf_files_path(:id => @file), + :title => l(:title_lock_file), :class => 'icon dmsf_icon-lock') %> + <% else %> + <%= link_to_if(@file.unlockable?, l(:button_unlock), unlock_dmsf_files_path(:id => @file), + :title => l(:title_unlock_file), :class => 'icon dmsf_icon-unlock') %> + <% end %> + <% if @file.notification %> <%= link_to(l(:label_notifications_off), notify_deactivate_dmsf_files_path(:id => @file), :title => l(:title_notifications_active_deactivate), - :class => 'icon icon-notification-on') %> - <% else %> + :class => 'icon dmsf_icon-notification-on') %> + <% else %> <%= link_to(l(:label_notifications_on), notify_activate_dmsf_files_path(:id => @file), :title => l(:title_notifications_not_active_activate), - :class => 'icon icon-notification-off') %> + :class => 'icon dmsf_icon-notification-off') %> <% end %> - <%= link_to(l(:label_link_to), - new_dmsf_link_path(:project_id => @project.id, :dmsf_folder_id => @file.folder ? @file.folder.id : nil, :dmsf_file_id => @file.id, :type => 'link_to'), - :title => l(:title_create_link), - :class => 'icon icon-link') %> + <%= link_to(l(:label_link_to), + new_dmsf_link_path(:project_id => @project.id, :dmsf_folder_id => @file.dmsf_folder ? @file.dmsf_folder.id : nil, :dmsf_file_id => @file.id, :type => 'link_to'), + :title => l(:title_create_link), + :class => 'icon dmsf_icon-link') %> <%= link_to("#{l(:button_copy)}/#{l(:button_move)}", copy_file_path(:id => @file), :title => l(:title_copy), :class => 'icon icon-copy') %> <%= delete_link @file if @file_delete_allowed %> <% else %> - <% if (!@file.locked_for_user? || User.current.allowed_to?(:force_file_unlock, @project)) && @file.unlockable? %> - <%= link_to(l(:button_unlock), - unlock_dmsf_files_path(:id => @file), - :title => l(:title_unlock_file), - :class => 'icon icon-dmsf-unlock')%> - <% end %> - <% end %> + <% if User.current.allowed_to?(:force_file_unlock, @project) %> + <%= link_to_if(@file.unlockable?, l(:button_unlock), unlock_dmsf_files_path(:id => @file), + :title => l(:title_unlock_file), :class => 'icon dmsf_icon-unlock')%> + <% end %> + <% end %> <% end %>
    -<%= render(:partial => '/dmsf/path', :locals => {:folder => @file.folder, :filename => @file.title}) %> +<%= render(:partial => '/dmsf/path', + :locals => {:folder => @file.dmsf_folder, :filename => @file.title, :title => nil}) %> -<%= error_messages_for('file') %> -<%= error_messages_for('revision') %> -<%= render(:partial => 'file_new_revision') if User.current.allowed_to?(:file_manipulation, @file.project) %> +<% if User.current.allowed_to?(:file_manipulation, @file.project) && !@file.locked_for_user? %> + <%= error_messages_for('file') %> + <%= error_messages_for('revision') %> + <%= render(:partial => 'file_new_revision') %> +<% end %> + +
    + <%= label_tag('', l(:label_document)) %> + <%= "##{@file.id}" %> +

    <%= l(:heading_revisions) %>

    -<% @file.revisions.visible[@revision_pages.offset,@revision_pages.per_page].each do |revision| %> -
    -
    +<% @file.dmsf_file_revisions.visible[@revision_pages.offset, @revision_pages.per_page].each do |revision| %> +
    +
    -
    - <%= link_to_function image_tag('rev_downloads.png', :plugin => 'redmine_dmsf'), - "$('#revision_access-#{revision.id}').toggle()", +
    + <%= link_to_function image_tag('rev_downloads.png', :plugin => 'redmine_dmsf'), + "$('#revision_access-#{revision.id}').toggle()", :title => l(:title_download_entries) %> <%= link_to image_tag('rev_download.png', :plugin => 'redmine_dmsf'), dmsf_file_path(@file, :download => revision), @@ -81,76 +90,85 @@ <%= link_to image_tag('rev_delete.png', :plugin => 'redmine_dmsf'), delete_revision_path(revision), :data => {:confirm => l(:text_are_you_sure)}, - :title => l(:title_delete_revision) if @file_delete_allowed && (@file.revisions.visible.count > 1) %> -
    + :title => l(:title_delete_revision) if @file_delete_allowed && (@file.dmsf_file_revisions.visible.count > 1) %> +
    <%= l(:info_revision, :rev => revision.id) %> <%= (revision.source_revision.nil? ? l(:label_created) : l(:label_changed)).downcase %> <%= l(:info_changed_by_user, :changed => format_time(revision.updated_at)) %> <%= link_to(revision.user.name, user_path(revision.user)) if revision.user %>
    -
    -
    -
    - <%= label_tag('', "#{l(:label_title)}:") %> - <%= h(revision.title) %> +
    +
    +
    +

    + <%= label_tag('', l(:label_title)) %> + <%= h(revision.title) %> +

    + <% if revision.description.present? %> +

    + <%= label_tag('', l(:label_description)) %> +

    + <%= textilizable(revision.description) %> +
    +

    + <% end %> +

    + <%= label_tag('', l(:label_dmsf_version)) %> + <%= revision.major_version %>.<%= revision.minor_version %> +

    +

    + <%= label_tag('', l(:label_size)) %> + <%= number_to_human_size(revision.size) %> +

    + <% wf = DmsfWorkflow.find_by_id(revision.dmsf_workflow_id) %> + <% if wf %> +

    + <%= label_tag('', l(:link_workflow)) %> + <%= "#{wf.name} - " %> + <%= link_to(revision.workflow_str(false), + log_dmsf_workflow_path(:project_id => @project.id, + :id => wf.id, :dmsf_file_revision_id => revision.id), + :title => DmsfWorkflow.assignments_to_users_str(wf.next_assignments(revision.id)), + :remote => true) %> +

    + <% end %> + <% if revision.comment.present? %> +

    + <%= label_tag('', l(:label_comment)) %> +

    + <%= textilizable(revision.comment) %> +
    +

    + <% end %>
    -
    - <%= label_tag('', "#{l(:label_file)}:") %> - <%= ("#{h(revision.file.folder.dmsf_path_str)}/") if revision.file.folder %><%= h(revision.name) %> +
    +

    + <%= label_tag('', l(:label_file)) %> + <%= ("#{h(revision.dmsf_file.dmsf_folder.dmsf_path_str)}/") if revision.dmsf_file.dmsf_folder %><%= h(revision.name) %> +

    +

    + <%= label_tag('', l(:label_mime)) %> + <%= h(revision.mime_type) %> +

    + <% if revision.digest.present? %> +

    + <%= label_tag('', 'MD5') %> + <%= revision.digest %> +

    + <% end %> + <%= render 'dmsf/custom_fields', :object => revision %>
    -

    - <%= label_tag('', "#{l(:label_description)}:") %> -

    -
    - <%= textilizable(revision.description) %> -
    -
    - <%= label_tag('', "#{l(:label_version)}:") %> - <%= revision.major_version %>.<%= revision.minor_version %> -
    - <%= label_tag('', "#{l(:link_workflow)}:") %> - <% wf = DmsfWorkflow.find_by_id(revision.dmsf_workflow_id) %> - <% if wf %> - <%= "#{wf.name} - " %> - <%= link_to( - revision.workflow_str(false), - log_dmsf_workflow_path( - :project_id => @project.id, - :id => wf.id, - :dmsf_file_revision_id => revision.id), - :title => DmsfWorkflow.assignments_to_users_str(wf.next_assignments(revision.id)), - :remote => true) %> - <% else %> - <%= revision.workflow_str(true) %> - <% end %> +
    " style="display:none"> + <%= render(:partial => 'revision_access', :locals => {:revision => revision}) if User.current.allowed_to?(:file_manipulation, @file.project) %>
    -
    - <%= label_tag('', "#{l(:label_mime)}:") %> - <%= h(revision.mime_type) %>  -
    - <%= label_tag('', "#{l(:label_size)}:") %> - <%= number_to_human_size(revision.size) %> -
    -
    - <%= render 'dmsf/custom_fields', :object => revision %> -
    -

    - <%= label_tag('', "#{l(:label_comment)}:") %> -

    -
    - <%= textilizable(revision.comment) %> -
    -
    " style="display:none"> - <%= render(:partial => 'revision_access', :locals => {:revision => revision}) if User.current.allowed_to?(:file_manipulation, @file.project) %> -
    -
    -
    +
    +
    <% end %> -<%= pagination_links_full @revision_pages, @file.revisions.visible.count %> +<%= pagination_links_full @revision_pages, @file.dmsf_file_revisions.visible.count %> <% url = 'jquery.dataTables/en.json' @@ -166,16 +184,16 @@ $('a.delete-entry').click(function(event) { if(!window.confirm('<%= l(:text_are_you_sure) %>')) { - event.preventDefault(); + event.preventDefault(); } }); $('#file_upload').change(function() { if($("input[name='version']:checked").val() == '0') { - $('#fileMinorVersionRadio').prop('checked', true); + $('#fileMinorVersionRadio').prop('checked', true); } - $('#fileSameVersionRadio').prop('disabled', true); - }); + $('#fileSameVersionRadio').prop('disabled', true); + }); $('#newRevisionFormContentToggle').click(function() { if($('#newRevisionFormContent').is(':visible')) { @@ -185,24 +203,24 @@ else { $(this).text('[-]'); $('#newRevisionFormContent').show(); - } + } }); - $('.access-table').dataTable({ + $('.dmsf_list').dataTable({ 'bJQueryUI': true, 'oLanguage': { - 'sUrl': '/plugin_assets/<%= :redmine_dmsf %>/javascripts/<%= url %>' + 'sUrl': '/plugin_assets/<%= :redmine_dmsf %>/javascripts/<%= url %>' } }); <% if @revision.valid? && @file.valid? %> <% end %> -<% content_for :header_tags do %> - <%= stylesheet_link_tag 'jquery.dataTables/jquery-ui.dataTables.css', :plugin => 'redmine_dmsf' %> - <%= javascript_include_tag 'jquery.dataTables/jquery.dataTables.min.js', :plugin => 'redmine_dmsf' %> +<% content_for :header_tags do %> + <%= stylesheet_link_tag 'jquery.dataTables/jquery-ui.dataTables.css', :plugin => 'redmine_dmsf' %> + <%= javascript_include_tag 'jquery.dataTables/jquery.dataTables.min.js', :plugin => 'redmine_dmsf' %> <% end %> \ No newline at end of file diff --git a/app/views/dmsf_files_copy/new.html.erb b/app/views/dmsf_files_copy/new.html.erb index 62d356a6..09c80ea0 100644 --- a/app/views/dmsf_files_copy/new.html.erb +++ b/app/views/dmsf_files_copy/new.html.erb @@ -1,7 +1,11 @@ -<%# Redmine plugin for Document Management System "Features" +<% +# encoding: utf-8 # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -15,53 +19,50 @@ # # 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.%> - - - -<%= stylesheet_link_tag 'select2.min.css', :plugin => 'redmine_dmsf' %> -<%= javascript_include_tag 'select2.min.js', :plugin => 'redmine_dmsf' %> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +%> <% html_title(l(:dmsf)) %> -
    -
    - -<%= render(:partial => '/dmsf/path', :locals => {:folder => @file.folder, :filename => @file.title}) %> +<%= render(:partial => '/dmsf/path', + :locals => {:folder => @file.dmsf_folder, :filename => @file.title, + :title => "#{l(:button_copy)}/#{l(:button_move)}"}) %> <% if DmsfFile.allowed_target_projects_on_copy.present? %> <%= form_tag({:action => 'create', :id => @file}, :id => 'copyForm') do |f| %> -
    +

    - + <%= label_tag('target_project_id', l(:field_target_project)) %> <%= select_tag('target_project_id', - project_tree_options_for_select(DmsfFile.allowed_target_projects_on_copy, :selected => @target_project)) %> + project_tree_options_for_select(DmsfFile.allowed_target_projects_on_copy, + :selected => @target_project)) %>

    - <%= label_tag('target_folder_id', "#{l(:field_target_folder)}:") %> + <%= label_tag('target_folder_id', l(:field_target_folder)) %> <%= select_tag('target_folder_id', options_for_select(DmsfFolder.directory_tree(@target_project), :selected => (@target_folder.id if @target_folder))) %>

    - <%= submit_tag(l(:button_copy)) %> - <% if User.current.allowed_to?(:file_manipulation, @project) %> - - <% end %> +

    + <%= submit_tag(l(:button_copy)) %> + <% if User.current.allowed_to?(:file_manipulation, @project) %> + <%= submit_tag(l(:button_move), :id => 'move_button') %> + <% end %> +

    <% end %> <% end %> \ No newline at end of file diff --git a/app/views/dmsf_folders_copy/new.html.erb b/app/views/dmsf_folders_copy/new.html.erb index 2b216576..57e6f3f9 100644 --- a/app/views/dmsf_folders_copy/new.html.erb +++ b/app/views/dmsf_folders_copy/new.html.erb @@ -1,7 +1,11 @@ -<%# Redmine plugin for Document Management System "Features" +<% +# encoding: utf-8 # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -15,29 +19,24 @@ # # 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.%> - - - -<%= stylesheet_link_tag 'select2.min.css', :plugin => 'redmine_dmsf' %> -<%= javascript_include_tag 'select2.min.js', :plugin => 'redmine_dmsf' %> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +%> <% html_title(l(:dmsf)) %> -
    - -<%= render(:partial => '/dmsf/path', :locals => {:folder => @folder, :filename => nil}) %> +<%= render(:partial => '/dmsf/path', + :locals => {:folder => @folder, :filename => nil, :title => l(:button_copy)}) %> <% unless DmsfFolder.allowed_target_projects_on_copy.blank? %> <%= form_tag({:action => 'copy_to', :id => @folder}, :id => 'copyForm') do |f| %> -
    +

    <%= select_tag('target_project_id', project_tree_options_for_select(DmsfFolder.allowed_target_projects_on_copy, :selected => @target_project)) %>

    - <%= label_tag('target_folder_id', "#{l(:field_target_folder)}:") %> + <%= label_tag('target_folder_id', l(:field_target_folder)) %> <%= select_tag('target_folder_id', options_for_select(DmsfFolder.directory_tree(@target_project, @folder), :selected => (@target_folder.id if @target_folder))) %> @@ -48,11 +47,10 @@ <% end %> \ No newline at end of file diff --git a/app/views/dmsf_links/_form.html.erb b/app/views/dmsf_links/_form.html.erb index f35a6df1..de487e25 100644 --- a/app/views/dmsf_links/_form.html.erb +++ b/app/views/dmsf_links/_form.html.erb @@ -1,6 +1,9 @@ -<%# Redmine plugin for Document Management System "Features" +<% +# encoding: utf-8 # -# Copyright (C) 2011-15 Karel Pičman +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -14,19 +17,19 @@ # # 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.%> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +%> <% html_title l(:dmsf) %> -

    -
    - <% if @dmsf_file_id %> <% file = DmsfFile.find_by_id @dmsf_file_id%> - <% title = file.title if file %> + <% title = file.title if file %> <% end %> -<%= render(:partial => '/dmsf/path', :locals => {:folder => @dmsf_link.folder, :filename => title}) %> +<%= render(:partial => '/dmsf/path', + :locals => {:folder => @dmsf_link.folder, :filename => nil, + :title => (@type == 'link_from') ? l(:label_link_from) : l(:label_link_to) }) %> <%= labelled_form_for @dmsf_link do |f| %> <%= error_messages_for @dmsf_link %> @@ -34,19 +37,19 @@ <%= f.hidden_field :dmsf_folder_id, :value => @dmsf_link.dmsf_folder_id if @dmsf_link.dmsf_folder_id %> <%= f.hidden_field :type, :value => @type %> <%= f.hidden_field :dmsf_file_id, :value => @dmsf_file_id %> -
    +
    <% if @type == 'link_from' %>

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

    - <% end %> + <% end %> - - <%= label_tag('dmsf_link[name]', l(:label_link_name), :class => 'required') %> - <%= text_field_tag 'dmsf_link[name]', @dmsf_link.name, :size => 40, :maxlength => 255 %> - + <% if @target_folder_id %> + <% folder = DmsfFolder.find_by_id @target_folder_id %> + <% files = folder.dmsf_files.visible if folder %> + <% else %> + <% files = @dmsf_link.target_project.dmsf_files.visible if @dmsf_link.target_project %> + <% end %> + <%= f.select(:target_file_id, + options_for_select(DmsfFolder.file_list(files), @target_file_id)) %> +

    + <% end %> +
    + +

    + <%= f.text_field :name, :required => true, :label => l(:label_link_name) %> +

    - <%= f.submit l(:button_create) %> +

    <%= f.submit l(:button_create) %>

    <% end %> - + + $('#dmsf_link_target_project_id').select2(); + $('#dmsf_link_target_folder_id').select2(); + $('#dmsf_link_target_file_id').select2(); + \ No newline at end of file diff --git a/app/views/dmsf_links/new.html.erb b/app/views/dmsf_links/new.html.erb index 011e1b52..82146124 100644 --- a/app/views/dmsf_links/new.html.erb +++ b/app/views/dmsf_links/new.html.erb @@ -1,6 +1,9 @@ -<%# Redmine plugin for Document Management System "Features" +<% +# encoding: utf-8 # -# Copyright (C) 2014 Karel Pičman +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -14,6 +17,7 @@ # # 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.%> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +%> <%= render 'form' %> \ No newline at end of file diff --git a/app/views/dmsf_mailer/files_deleted.html.erb b/app/views/dmsf_mailer/files_deleted.html.erb index 6000d864..7e780433 100644 --- a/app/views/dmsf_mailer/files_deleted.html.erb +++ b/app/views/dmsf_mailer/files_deleted.html.erb @@ -24,7 +24,7 @@

    <%= h(file.dmsf_path_str) %> (<%= file.name %>) <% if file.last_revision %> - , <%= number_to_human_size(file.last_revision.size) %>, <%= l(:label_version) %> <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %> + , <%= number_to_human_size(file.last_revision.size) %>, <%= l(:label_dmsf_version) %> <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %> <% end %>

    <% end %> \ No newline at end of file diff --git a/app/views/dmsf_mailer/files_deleted.text.erb b/app/views/dmsf_mailer/files_deleted.text.erb index a6cd02a0..bfd77ff6 100644 --- a/app/views/dmsf_mailer/files_deleted.text.erb +++ b/app/views/dmsf_mailer/files_deleted.text.erb @@ -22,6 +22,6 @@ <% @files.each do |file| %> <%= h(file.dmsf_path_str) %> (<%= file.name %>) <% if file.last_revision %> - , <%= number_to_human_size(file.last_revision.size) %>, <%= l(:label_version) %> <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %> + , <%= number_to_human_size(file.last_revision.size) %>, <%= l(:label_dmsf_version) %> <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %> <% end %> <% end %> \ No newline at end of file diff --git a/app/views/dmsf_mailer/files_updated.html.erb b/app/views/dmsf_mailer/files_updated.html.erb index d3e2a999..cd366281 100644 --- a/app/views/dmsf_mailer/files_updated.html.erb +++ b/app/views/dmsf_mailer/files_updated.html.erb @@ -26,7 +26,7 @@ dmsf_file_url(file, :download => '')) %> (<%= file.name %>), <%= number_to_human_size(file.last_revision.size) %>, - <%= l(:label_version) %> <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %>, + <%= l(:label_dmsf_version) %> <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %>, <%= "#{file.last_revision.workflow_str(true)}," if file.last_revision.workflow_str(true) != l(:title_none) %> <%= link_to(l(:link_details, :title => h(file.title)), dmsf_file_url(file)) %> diff --git a/app/views/dmsf_mailer/files_updated.text.erb b/app/views/dmsf_mailer/files_updated.text.erb index 899d3b71..de0d8d9f 100644 --- a/app/views/dmsf_mailer/files_updated.text.erb +++ b/app/views/dmsf_mailer/files_updated.text.erb @@ -23,7 +23,7 @@ <% @files.each do |file| %> <%= h(file.dmsf_path_str) %> (<%= file.name %>), <%= number_to_human_size(file.last_revision.size) %>, - <%= l(:label_version) %> <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %>, + <%= l(:label_dmsf_version) %> <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %>, <%= "#{file.last_revision.workflow_str(true)}," if file.last_revision.workflow_str(true) != l(:title_none) %> <%= dmsf_file_url(file) %> <% if file.last_revision.comment.present? %> diff --git a/app/views/dmsf_mailer/send_documents.html.erb b/app/views/dmsf_mailer/send_documents.html.erb index ef98e05a..ac8f1eeb 100644 --- a/app/views/dmsf_mailer/send_documents.html.erb +++ b/app/views/dmsf_mailer/send_documents.html.erb @@ -1,8 +1,11 @@ -<%# Redmine plugin for Document Management System "Features" +<% +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,24 +23,30 @@ <%= textilizable(@body) %> -<% if @links_only == '1' %> +<% if @links_only == '1' %> + <% folders = [] %> + <% files = [] %> <% if @folders.present? %> <% JSON.parse(@folders).each do |id| %> <% folder = DmsfFolder.find_by_id id %> - <% if folder %> - <% folder.folder_tree.each do |name, i| %> + <% if folder %> + <% folder.folder_tree.each do |name, i| %> <% dir = DmsfFolder.find_by_id i %> - <% if dir %> + <% if dir && !folders.include?(dir) %>
    <%= link_to(h(dir.dmsf_path_str), dmsf_folder_path(:id => dir.project_id, :folder_id => dir.id, :only_path => false)) %>

    - <% dir.files.each do |file| %> - <%= link_to(h(file.title), dmsf_file_url(file)) %> -  (<%= link_to(h(file.name), dmsf_file_url(file, :download => '')) %>) -
    + <% dir.dmsf_files.each do |file| %> + <% unless files.include?(file) %> + <%= link_to(h(file.title), dmsf_file_url(file)) %> +  (<%= link_to(h(file.name), dmsf_file_url(file, :download => '')) %>) +
    + <% files << file %> + <% end %> <% end %> + <% folders << dir %> <% end %> - <% end %> + <% end %> <% end %> <% end %> <% end %> @@ -45,11 +54,13 @@
    <% JSON.parse(@files).each do |id| %> <% file = DmsfFile.find_by_id id %> - <% if file %> + <% if file && !files.include?(file) %> <%= link_to(h(file.title), dmsf_file_url(file)) %>  (<%= link_to(h(file.name), dmsf_file_url(file, :download => '')) %>)
    + <% files << file %> <% end %> <% end %> <% end %> -<% end %> \ No newline at end of file +<% end %> + \ No newline at end of file diff --git a/app/views/dmsf_mailer/send_documents.text.erb b/app/views/dmsf_mailer/send_documents.text.erb index 62f4baec..b4f4f23d 100644 --- a/app/views/dmsf_mailer/send_documents.text.erb +++ b/app/views/dmsf_mailer/send_documents.text.erb @@ -1,8 +1,11 @@ -<%# Redmine plugin for Document Management System "Features" +<% +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,29 +23,35 @@ <%= @body %> -<% if @links_only == '1' %> +<% if @links_only == '1' %> + <% folders = [] %> + <% files = [] %> <% if @folders.present? %> <% JSON.parse(@folders).each do |id| %> <% folder = DmsfFolder.find_by_id id %> - <% if folder %> - <% folder.folder_tree.each do |name, i| %> + <% if folder && !folders.include?(folder) %> + <% folder.folder_tree.each do |name, i| %> <% dir = DmsfFolder.find_by_id i %> <% if dir %> <%= dir.dmsf_path_str %> - <% dir.files.each do |file| %> - <%= dmsf_file_url(file, :download => '') %> + <% dir.dmsf_files.each do |file| %> + <% unless files.include?(file) %> + <%= dmsf_file_url(file, :download => '') %> + <% files << file %> + <% end %> <% end %> <% end %> - <% end %> + <% end %> <% end %> <% end %> <% end %> <% if @files.present? %> <% JSON.parse(@files).each do |id| %> <% file = DmsfFile.find_by_id id %> - <% if file %> + <% if file && !files.include?(file) %> <%= dmsf_file_url(file, :download => '') %> + <% files << file %> <% end %> <% end %> <% end %> -<% end %> \ No newline at end of file +<% end %> diff --git a/app/views/dmsf_mailer/workflow_notification.html.erb b/app/views/dmsf_mailer/workflow_notification.html.erb index 911c9ee1..47078947 100644 --- a/app/views/dmsf_mailer/workflow_notification.html.erb +++ b/app/views/dmsf_mailer/workflow_notification.html.erb @@ -17,16 +17,16 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.%>

    <%= @user.name %>,

    -

    +

    <%= @text1 %>

    - <%= @text2 %> - <% unless @revision.file.folder %> - <%= link_to l(:link_documents), - dmsf_folder_url(:id => @revision.file.project) %> + <%= @text2 %> + <% unless @revision.dmsf_file.dmsf_folder %> + <%= link_to l(:link_documents), + dmsf_folder_url(:id => @revision.dmsf_file.project) %> <% else %> - <%= link_to @revision.file.folder.title, - dmsf_folder_url(:id => @revision.file.project, :folder_id => @revision.file.folder) %> + <%= link_to @revision.dmsf_file.dmsf_folder.title, + dmsf_folder_url(:id => @revision.dmsf_file.project, :folder_id => @revision.dmsf_file.dmsf_folder) %> <% end %>.

    \ No newline at end of file diff --git a/app/views/dmsf_mailer/workflow_notification.text.erb b/app/views/dmsf_mailer/workflow_notification.text.erb index f2e104bd..9a36d147 100644 --- a/app/views/dmsf_mailer/workflow_notification.text.erb +++ b/app/views/dmsf_mailer/workflow_notification.text.erb @@ -18,8 +18,8 @@ <%= @user.name %>, <%= @text1 %> -<% unless @revision.file.folder %> - <%= @text2 %> <%= dmsf_folder_url(:id => @revision.file.project) %>. +<% unless @revision.dmsf_file.dmsf_folder %> + <%= @text2 %> <%= dmsf_folder_url(:id => @revision.dmsf_file.project) %>. <% else %> - <%= @text2 %> <%= dmsf_folder_url(:id => @revision.file.project, :folder_id => @revision.file.folder) %>. + <%= @text2 %> <%= dmsf_folder_url(:id => @revision.dmsf_file.project, :folder_id => @revision.dmsf_file.dmsf_folder) %>. <% end %> \ No newline at end of file diff --git a/app/views/dmsf_upload/_multi_upload.html.erb b/app/views/dmsf_upload/_multi_upload.html.erb index 85a32ac8..2512754b 100644 --- a/app/views/dmsf_upload/_multi_upload.html.erb +++ b/app/views/dmsf_upload/_multi_upload.html.erb @@ -1,10 +1,11 @@ <% # encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -23,46 +24,36 @@
    <%= form_tag({:controller => 'dmsf_upload', :action => 'upload_files', :id => @project, :folder_id => @folder}, - :id => 'uploadform', :method=>:post, :multipart => true) do %> - <% if Setting.attachment_max_size.to_i >= 102400 %> -
    - <%= l(:label_file_size) %>: - -
    - <% end %> -

    <%= l(:heading_file_upload) %>

    -
    - - <% max_file_upload = Setting.plugin_redmine_dmsf['dmsf_max_file_upload'].to_i %> - <%= l(:note_uploaded_maximum_files_at_once, :number => max_file_upload) if max_file_upload > 0 %> - <%= l(:note_upload_files_greater_than_two_gb) if Setting.attachment_max_size.to_i >= 2097151 %> - -
    + :id => 'uploadform', :method => :post, :multipart => true) do %> +
    + +
    +

    <%= l(:label_upload) %>

    - + <%= render :partial => 'attachments/form' %>

    - <%= submit_tag(l(:submit_upload_files)) %> + <%= submit_tag l(:label_upload) %>
    <% end %>
    \ No newline at end of file + $('#dmsf_workflow_id').select2(); + diff --git a/app/views/dmsf_workflows/new_step.html.erb b/app/views/dmsf_workflows/new_step.html.erb deleted file mode 100644 index 8baa7cf3..00000000 --- a/app/views/dmsf_workflows/new_step.html.erb +++ /dev/null @@ -1,19 +0,0 @@ -<% -# encoding: utf-8 -# -# Redmine plugin for Kontron customisation -# Copyright (c) 2011-15 Kontron -# Karel Pičman -# -# Repository folders -%> - -

    <%= l(:label_repository_folders) %>

    - -<%= form_for(:repository_folder, {:url => edit_dmsf_workflow_path(@dmsf_workflow), :method => :post}) do |f| %> - <%= render :partial => 'new_step_form' %> -

    - <%= submit_tag l(:dmsf_and), :id => 'add-step-and' %> - <%= submit_tag l(:dmsf_or), :id => 'add-step-or' %> -

    -<% end %> \ No newline at end of file diff --git a/app/views/dmsf_workflows/new_step.js.erb b/app/views/dmsf_workflows/new_step.js.erb index 56600c73..13ea0d1b 100644 --- a/app/views/dmsf_workflows/new_step.js.erb +++ b/app/views/dmsf_workflows/new_step.js.erb @@ -1,6 +1,9 @@ -<%# Redmine plugin for Document Management System "Features" +<% +# encoding: utf-8 # -# Copyright (C) 2011-15 Karel Pičman +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine_dmsf/patches.rb b/app/views/hooks/redmine_dmsf/_view_my_account.html.erb similarity index 66% rename from lib/redmine_dmsf/patches.rb rename to app/views/hooks/redmine_dmsf/_view_my_account.html.erb index db91a040..c179abbb 100644 --- a/lib/redmine_dmsf/patches.rb +++ b/app/views/hooks/redmine_dmsf/_view_my_account.html.erb @@ -1,8 +1,9 @@ +<%# +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-14 Karel Picman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,8 +18,8 @@ # 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 'redmine_dmsf/patches/custom_fields_helper_patch' -require 'redmine_dmsf/patches/acts_as_customizable' -require 'redmine_dmsf/patches/project_patch' -require 'redmine_dmsf/patches/project_tabs_extended' \ No newline at end of file +%> + +<%= labelled_fields_for :pref, @user.pref do |pref_fields| %> +

    <%= pref_fields.check_box :dmsf_tree_view %>

    +<% end %> \ No newline at end of file diff --git a/app/views/hooks/redmine_dmsf/_view_projects_form.html.erb b/app/views/hooks/redmine_dmsf/_view_projects_form.html.erb index f5112d14..a9b28fc9 100644 --- a/app/views/hooks/redmine_dmsf/_view_projects_form.html.erb +++ b/app/views/hooks/redmine_dmsf/_view_projects_form.html.erb @@ -1,8 +1,11 @@ -<%# Redmine plugin for Document Management System "Features" +<%# +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-14 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,11 +19,12 @@ # # 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.%> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +%> <% if @project.new_record? && @source_project %>

    - +

    <% end %> diff --git a/app/views/my/blocks/_locked_documents.html.erb b/app/views/my/blocks/_locked_documents.html.erb index 3615e774..30762435 100644 --- a/app/views/my/blocks/_locked_documents.html.erb +++ b/app/views/my/blocks/_locked_documents.html.erb @@ -1,7 +1,9 @@ -<%# +<% +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2013 Karel Picman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -24,15 +26,15 @@ <% files = DmsfFile.joins( 'JOIN dmsf_locks ON dmsf_files.id = dmsf_locks.entity_id').where( 'dmsf_locks.entity_type' => 0, 'dmsf_locks.user_id' => @user.id).all %> -

    <%= l(:label_my_locked_documents)%> (<%= folders.count %>/<%= files.count %>)

    +

    <%= l(:locked_documents)%> (<%= folders.count %>/<%= files.count %>)

    <% if folders.any? || files.any?%> <%= form_tag({}) do %> - - - - + + + + @@ -40,19 +42,19 @@ + + :class => 'icon icon-folder') %> + <% end %> @@ -60,19 +62,19 @@ - + + <% end %> diff --git a/app/views/my/blocks/_open_approvals.html.erb b/app/views/my/blocks/_open_approvals.html.erb index 58763291..83874721 100644 --- a/app/views/my/blocks/_open_approvals.html.erb +++ b/app/views/my/blocks/_open_approvals.html.erb @@ -1,7 +1,9 @@ -<%# +<% +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Picman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -22,20 +24,20 @@ 'LEFT JOIN dmsf_workflow_step_actions ON dmsf_workflow_step_assignments.id = dmsf_workflow_step_actions.dmsf_workflow_step_assignment_id').where( 'dmsf_workflow_step_assignments.user_id = ? AND dmsf_workflow_step_actions.id IS NULL', @user.id).all %> <% assignments = Array.new %> -<% all_assignments.each do |assignment| %> - <% if assignment.dmsf_file_revision.file.last_revision && - !assignment.dmsf_file_revision.file.last_revision.deleted && - (assignment.dmsf_file_revision.workflow == DmsfWorkflow::STATE_WAITING_FOR_APPROVAL) && - (assignment.dmsf_file_revision == assignment.dmsf_file_revision.file.last_revision) %> +<% all_assignments.each do |assignment| %> + <% if assignment.dmsf_file_revision.dmsf_file.last_revision && + !assignment.dmsf_file_revision.dmsf_file.last_revision.deleted? && + (assignment.dmsf_file_revision.workflow == DmsfWorkflow::STATE_WAITING_FOR_APPROVAL) && + (assignment.dmsf_file_revision == assignment.dmsf_file_revision.dmsf_file.last_revision) %> <% assignments << assignment %> <% end %> <% end %> -

    <%= l(:label_my_open_approvals)%> (<%= assignments.count %>)

    +

    <%= l(:open_approvals)%> (<%= assignments.count %>)

    <% if assignments.any? %> <%= form_tag({}) do %>
    <%=l(:field_project)%><%=l(:label_document)%>/<%=l(:field_folder)%><%=l(:field_folder)%>
    <%= l(:field_project) %><%= l(:label_document) %>/<%= l(:field_folder) %><%= l(:field_folder) %>
    <%= link_to_project(folder.project) %> - <%= link_to(h(folder.title), {:controller => 'dmsf', :action => 'show', :id => folder.project, :folder_id => folder}, - :class => 'icon icon-folder') %> - - <% if folder.folder %> - <%= link_to(h(folder.folder.title), - {:controller => 'dmsf', :action => 'show', :id => folder.project, :folder_id => folder.folder}) %> + <% if folder.dmsf_folder %> + <%= link_to(h(folder.dmsf_folder.title), + {:controller => 'dmsf', :action => 'show', :id => folder.project, :folder_id => folder.dmsf_folder}) %> <% else %> - <%= link_to(l(:link_documents), {:controller => 'dmsf', :action => 'show', :id=> folder.project }) %> - <% end %> + <%= link_to(l(:link_documents), {:controller => 'dmsf', :action => 'show', :id=> folder.project }) %> + <% end %>
    <%= link_to_project(file.project) %> - - <%= link_to(h(file.title), + + <%= link_to(h(file.title), {:controller => 'dmsf_files', :action => :show, :id => file }, :class => "icon icon-file #{DmsfHelper.filetype_css(file.name)}") %> - - <% if file.folder %> - <%= link_to(h(file.folder.title), - {:controller => 'dmsf', :action => 'show', :id => file.project, :folder_id => file.folder}) %> + <% if file.dmsf_folder %> + <%= link_to(h(file.dmsf_folder.title), + {:controller => 'dmsf', :action => 'show', :id => file.project, :folder_id => file.dmsf_folder}) %> <% else %> - <%= link_to(l(:link_documents), {:controller => 'dmsf', :action => 'show', :id=> file.project }) %> - <% end %> + <%= link_to(l(:link_documents), {:controller => 'dmsf', :action => 'show', :id=> file.project }) %> + <% end %>
    - + @@ -46,29 +48,29 @@ <% assignments.each do |assignment| %> diff --git a/assets/images/bullet_arrow_down.png b/assets/images/bullet_arrow_down.png new file mode 100644 index 00000000..cb819a7d Binary files /dev/null and b/assets/images/bullet_arrow_down.png differ diff --git a/assets/images/filedetails.png b/assets/images/filedetails.png index 0ef642fa..f7df57a3 100644 Binary files a/assets/images/filedetails.png and b/assets/images/filedetails.png differ diff --git a/assets/images/operations.png b/assets/images/operations.png deleted file mode 100644 index 8bf76fbb..00000000 Binary files a/assets/images/operations.png and /dev/null differ diff --git a/assets/images/ticket_go.png b/assets/images/ticket_go.png deleted file mode 100644 index 2aaec381..00000000 Binary files a/assets/images/ticket_go.png and /dev/null differ diff --git a/assets/javascripts/jquery.dataTables/it.json b/assets/javascripts/jquery.dataTables/it.json new file mode 100644 index 00000000..fc342725 --- /dev/null +++ b/assets/javascripts/jquery.dataTables/it.json @@ -0,0 +1,17 @@ +{ + "sProcessing": "Elaborazione...", + "sLengthMenu": "Visualizza _MENU_ elementi", + "sZeroRecords": "La ricerca non ha portato alcun risultato o nessun elemento è presente", + "sInfo": "Vista da _START_ a _END_ di _TOTAL_ elementi", + "sInfoEmpty": "Vista da 0 a 0 di 0 elementi", + "sInfoFiltered": "(filtrati da _MAX_ elementi totali)", + "sInfoPostFix": "", + "sSearch": "Cerca:", + "sUrl": "", + "oPaginate": { + "sFirst": "Inizio", + "sPrevious": "Precedente", + "sNext": "Successivo", + "sLast": "Fine" + } +} diff --git a/assets/javascripts/redmine_dmsf.js b/assets/javascripts/redmine_dmsf.js new file mode 100644 index 00000000..21cf3308 --- /dev/null +++ b/assets/javascripts/redmine_dmsf.js @@ -0,0 +1,117 @@ +/* encoding: utf-8 +* +* Redmine plugin for Document Management System "Features" +* +* Copyright (C) 2011-16 Karel Pičman +* +* 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. +*/ + +/* Function to allow the projects to show up as a tree */ +function dmsf_toggle(EL, PM) +{ + var els = document.getElementsByTagName('tr'); + var elsLen = els.length; + var pattern = new RegExp("(^|\\s)" + EL + "(\\s|$)"); + var cpattern = new RegExp('span'); + var expand = new RegExp('dmsf_expanded'); + var collapse = new RegExp('dmsf_collapsed'); + var hide = new RegExp('dmsf_hidden'); + var spanid = PM; + var classid = new RegExp('junk'); + var oddeventoggle = 0; + + for(i = 0; i < elsLen; i++) + { + if(cpattern.test(els[i].id)) + { + var tmpspanid = spanid; + var tmpclassid = classid; + + spanid = els[i].id; + classid = spanid; + classid = classid.match(/(\w+)span/)[1]; + classid = new RegExp(classid); + + if(tmpclassid.test(els[i].className) && (tmpspanid.toString() !== PM.toString())) + { + if(collapse.test(document.getElementById(tmpspanid).className)) + { + spanid = tmpspanid; + classid = tmpclassid; + } + } + } + + if(pattern.test(els[i].className)) + { + var cnames = els[i].className; + + cnames = cnames.replace(/dmsf_hidden/g,''); + + if(expand.test(document.getElementById(PM).className)) + { + cnames += ' dmsf_hidden'; + } + else + { + if((spanid.toString() !== PM.toString()) && (classid.test(els[i].className))) + { + if(collapse.test(document.getElementById(spanid).className)) + { + cnames += ' dmsf_hidden'; + } + } + } + + els[i].className = cnames; + } + + if(!(hide.test(els[i].className))) + { + var cnames = els[i].className; + + cnames = cnames.replace(/odd/g,''); + cnames = cnames.replace(/even/g,''); + + if(oddeventoggle === 0) + { + cnames += ' odd'; + } + else + { + cnames += ' even'; + } + + oddeventoggle ^= 1; + els[i].className = cnames; + } + } + + if (collapse.test(document.getElementById(PM).className)) + { + var cnames = document.getElementById(PM).className; + + cnames = cnames.replace(/dmsf_collapsed/,'dmsf_expanded'); + document.getElementById(PM).className = cnames; + } + else + { + var cnames = document.getElementById(PM).className; + + cnames = cnames.replace(/dmsf_expanded/,'dmsf_collapsed'); + document.getElementById(PM).className = cnames; + } +} diff --git a/assets/javascripts/select2.min.js b/assets/javascripts/select2.min.js index cda366f3..1fefa0d1 100644 --- a/assets/javascripts/select2.min.js +++ b/assets/javascripts/select2.min.js @@ -1,2 +1,2 @@ -!function(){if(window.define)var a=window.define;if(window.require)var b=window.require;if(window.jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var a=jQuery.fn.select2.amd.define,b=jQuery.fn.select2.amd.require;var c,b,a;!function(d){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(n=n.slice(0,n.length-1),a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.concat(a),k=0;k0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,b){return function(){return n.apply(d,v.call(arguments,0).concat([a,b]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var b=r[a];delete r[a],t[a]=!0,m.apply(d,b)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,b,c,f){var h,k,l,m,n,s,u=[],v=typeof c;if(f=f||a,"undefined"===v||"function"===v){for(b=!b.length&&c.length?["require","exports","module"]:b,n=0;n0&&(a.call(arguments,b.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=a(c),g=a(b);c.displayName=b.displayName,d.prototype=new e;for(var h=0;hc;c++)a[c].apply(this,b)},b.Observable=c,b.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(36)}return b},b.bind=function(a,b){return function(){a.apply(b,arguments)}},b._convertData=function(a){for(var b in a){var c=b.split("-"),d=a;if(1!==c.length){for(var e=0;e');return this.options.get("multiple")&&b.attr("aria-multiselectable","true"),this.$results=b,b},c.prototype.clear=function(){this.$results.empty()},c.prototype.displayMessage=function(b){this.clear(),this.hideLoading();var c=a('
  • '),d=this.options.get("translations").get(b.message);c.text(d(b.args)),this.$results.append(c)},c.prototype.append=function(a){this.hideLoading();var b=[];if(null==a.results||0===a.results.length)return void(0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"}));a.results=this.sort(a.results);for(var c=0;c-1?b.attr("aria-selected","true"):b.attr("aria-selected","false")});var f=e.filter("[aria-selected=true]");f.length>0?f.first().trigger("mouseenter"):e.first().trigger("mouseenter")})},c.prototype.showLoading=function(a){this.hideLoading();var b=this.options.get("translations").get("searching"),c={disabled:!0,loading:!0,text:b(a)},d=this.option(c);d.className+=" loading-results",this.$results.prepend(d)},c.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},c.prototype.option=function(b){var c=document.createElement("li");c.className="select2-results__option";var d={role:"treeitem","aria-selected":"false"};b.disabled&&(delete d["aria-selected"],d["aria-disabled"]="true"),null==b.id&&delete d["aria-selected"],null!=b._resultId&&(c.id=b._resultId),b.children&&(d.role="group",d["aria-label"]=b.text,delete d["aria-selected"]);for(var e in d){var f=d[e];c.setAttribute(e,f)}if(b.children){var g=a(c),h=document.createElement("strong");h.className="select2-results__group";{a(h)}this.template(b,h);for(var i=[],j=0;j",{"class":"select2-results__options select2-results__options--nested"});m.append(i),g.append(h),g.append(m)}else this.template(b,c);return a.data(c,"data",b),c},c.prototype.bind=function(b){var c=this,d=b.id+"-results";this.$results.attr("id",d),b.on("results:all",function(a){c.clear(),c.append(a.data),b.isOpen()&&c.setClasses()}),b.on("results:append",function(a){c.append(a.data),b.isOpen()&&c.setClasses()}),b.on("query",function(a){c.showLoading(a)}),b.on("select",function(){b.isOpen()&&c.setClasses()}),b.on("unselect",function(){b.isOpen()&&c.setClasses()}),b.on("open",function(){c.$results.attr("aria-expanded","true"),c.$results.attr("aria-hidden","false"),c.setClasses(),c.ensureHighlightVisible()}),b.on("close",function(){c.$results.attr("aria-expanded","false"),c.$results.attr("aria-hidden","true"),c.$results.removeAttr("aria-activedescendant")}),b.on("results:select",function(){var a=c.getHighlightedResults();if(0!==a.length){var b=a.data("data");"true"==a.attr("aria-selected")?c.options.get("multiple")?c.trigger("unselect",{data:b}):c.trigger("close"):c.trigger("select",{data:b})}}),b.on("results:previous",function(){var a=c.getHighlightedResults(),b=c.$results.find("[aria-selected]"),d=b.index(a);if(0!==d){var e=d-1;0===a.length&&(e=0);var f=b.eq(e);f.trigger("mouseenter");var g=c.$results.offset().top,h=f.offset().top,i=c.$results.scrollTop()+(h-g);0===e?c.$results.scrollTop(0):0>h-g&&c.$results.scrollTop(i)}}),b.on("results:next",function(){var a=c.getHighlightedResults(),b=c.$results.find("[aria-selected]"),d=b.index(a),e=d+1;if(!(e>=b.length)){var f=b.eq(e);f.trigger("mouseenter");var g=c.$results.offset().top+c.$results.outerHeight(!1),h=f.offset().top+f.outerHeight(!1),i=c.$results.scrollTop()+h-g;0===e?c.$results.scrollTop(0):h>g&&c.$results.scrollTop(i)}}),b.on("results:focus",function(a){a.element.addClass("select2-results__option--highlighted")}),b.on("results:message",function(a){c.displayMessage(a)}),a.fn.mousewheel&&this.$results.on("mousewheel",function(a){var b=c.$results.scrollTop(),d=c.$results.get(0).scrollHeight-c.$results.scrollTop()+a.deltaY,e=a.deltaY>0&&b-a.deltaY<=0,f=a.deltaY<0&&d<=c.$results.height();e?(c.$results.scrollTop(0),a.preventDefault(),a.stopPropagation()):f&&(c.$results.scrollTop(c.$results.get(0).scrollHeight-c.$results.height()),a.preventDefault(),a.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(b){var d=a(this),e=d.data("data");return"true"===d.attr("aria-selected")?void(c.options.get("multiple")?c.trigger("unselect",{originalEvent:b,data:e}):c.trigger("close")):void c.trigger("select",{originalEvent:b,data:e})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(){var b=a(this).data("data");c.getHighlightedResults().removeClass("select2-results__option--highlighted"),c.trigger("results:focus",{data:b,element:a(this)})})},c.prototype.getHighlightedResults=function(){var a=this.$results.find(".select2-results__option--highlighted");return a},c.prototype.destroy=function(){this.$results.remove()},c.prototype.ensureHighlightVisible=function(){var a=this.getHighlightedResults();if(0!==a.length){var b=this.$results.find("[aria-selected]"),c=b.index(a),d=this.$results.offset().top,e=a.offset().top,f=this.$results.scrollTop()+(e-d),g=e-d;f-=2*a.outerHeight(!1),2>=c?this.$results.scrollTop(0):(g>this.$results.outerHeight()||0>g)&&this.$results.scrollTop(f)}},c.prototype.template=function(a,b){var c=this.options.get("templateResult"),d=c(a);null==d?b.style.display="none":b.innerHTML=d},c}),a("select2/keys",[],function(){var a={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46,isArrow:function(a){switch(a=a.which?a.which:a){case KEY.LEFT:case KEY.RIGHT:case KEY.UP:case KEY.DOWN:return!0}return!1}};return a}),a("select2/selection/base",["jquery","../utils","../keys"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,b.Observable),d.prototype.render=function(){var b=a('');return b.attr("title",this.$element.attr("title")),this.$selection=b,b},d.prototype.bind=function(a){var b=this,d=(a.id+"-container",a.id+"-results");this.container=a,this.$selection.attr("aria-owns",d),this.$selection.on("keydown",function(a){b.trigger("keypress",a),a.which===c.SPACE&&a.preventDefault()}),a.on("results:focus",function(a){b.$selection.attr("aria-activedescendant",a.data._resultId)}),a.on("selection:update",function(a){b.update(a.data)}),a.on("open",function(){b.$selection.attr("aria-expanded","true"),b._attachCloseHandler(a)}),a.on("close",function(){b.$selection.attr("aria-expanded","false"),b.$selection.removeAttr("aria-activedescendant"),b.$selection.focus(),b._detachCloseHandler(a)}),a.on("enable",function(){b.$selection.attr("tabindex","0")}),a.on("disable",function(){b.$selection.attr("tabindex","-1")})},d.prototype._attachCloseHandler=function(b){a(document.body).on("mousedown.select2."+b.id,function(b){var c=a(b.target),d=c.closest(".select2"),e=a(".select2.select2-container--open");e.each(function(){var b=a(this);if(this!=d[0]){var c=b.data("element");c.select2("close")}})})},d.prototype._detachCloseHandler=function(b){a(document.body).off("mousedown.select2."+b.id)},d.prototype.position=function(a,b){var c=b.find(".selection");c.append(a)},d.prototype.destroy=function(){this._detachCloseHandler(this.container)},d.prototype.update=function(){throw new Error("The `update` method must be defined in child classes.")},d}),a("select2/selection/single",["jquery","./base","../utils","../keys"],function(a,b,c){function d(){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--single"),a.html(''),a},d.prototype.bind=function(a){var b=this;d.__super__.bind.apply(this,arguments);var c=a.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",c),this.$selection.attr("aria-labelledby",c),this.$selection.on("mousedown",function(a){1===a.which&&b.trigger("toggle",{originalEvent:a})}),this.$selection.on("focus",function(){}),this.$selection.on("blur",function(){}),a.on("selection:update",function(a){b.update(a.data)})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a){var b=this.options.get("templateSelection");return b(a)},d.prototype.selectionContainer=function(){return a("")},d.prototype.update=function(a){if(0===a.length)return void this.clear();var b=a[0],c=this.display(b);this.$selection.find(".select2-selection__rendered").html(c)},d}),a("select2/selection/multiple",["jquery","./base","../utils"],function(a,b,c){function d(){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--multiple"),a.html('
      '),a},d.prototype.bind=function(){var b=this;d.__super__.bind.apply(this,arguments),this.$selection.on("click",function(a){b.trigger("toggle",{originalEvent:a})}),this.$selection.on("click",".select2-selection__choice__remove",function(c){var d=a(this),e=d.parent(),f=e.data("data");b.trigger("unselect",{originalEvent:c,data:f})})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a){var b=this.options.get("templateSelection");return b(a)},d.prototype.selectionContainer=function(){var b=a('
    • ×
    • ');return b},d.prototype.update=function(a){if(this.clear(),0!==a.length){for(var b=[],c=0;c1;if(d||c)return a.call(this,b);this.clear();var e=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(e)},a}),a("select2/selection/allowClear",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$selection.on("mousedown",".select2-selection__clear",function(b){if(!e.options.get("disabled")){b.stopPropagation();for(var c=a(this).data("data"),d=0;d0||0===c.length)){var d=a('×');d.data("data",c),this.$selection.find(".select2-selection__rendered").append(d)}},b}),a("select2/selection/search",["jquery","../utils","../keys"],function(a,b,c){function d(a,b,c){a.call(this,b,c)}return d.prototype.render=function(b){var c=a('');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus()}),b.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),b.on("enable",function(){e.$search.prop("disabled",!1)}),b.on("disable",function(){e.$search.prop("disabled",!0)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f)}}}),this.$selection.on("keyup",".select2-search--inline",function(a){e.handleSearch(a)})},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.trigger("open"),this.$search.val(b.text+" ")},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),a("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==f.indexOf(b)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==g.indexOf(b)&&(c.prevented=d.isDefaultPrevented())}})},b}),a("select2/translation",["jquery"],function(a){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),a("select2/diacritics",[],function(){var a={"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","B":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","C":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","E":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","M":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","N":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","P":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Q":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","R":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","S":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","T":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","U":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","V":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","W":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Y":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","h":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","v":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ω":"ω","ς":"σ"};return a}),a("select2/data/base",["../utils"],function(a){function b(){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),a("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change");if(this.$element.prop("multiple"))this.current(function(c){var d=[];a=[a],a.push.apply(a,c);for(var e=0;e=0){var j=e.filter(b(i)),k=this.item(j),l=(c.extend(!0,{},k,i),this.option(k));j.replaceWith(l)}else{var m=this.option(i);if(i.children){var n=this.convertToOptions(i.children);m.append(n)}g.push(m)}}return g},d}),a("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(b,c){this.ajaxOptions=c.get("ajax"),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),a.__super__.constructor.call(this,b,c)}return b.Extend(d,a),d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=c.ajax(f);d.success(function(d){var f=e.processResults(d,a);console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)}),e._request=d}var e=this; -this._request&&(this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url(a)),"function"==typeof f.data&&(f.data=f.data(a)),this.ajaxOptions.delay&&""!==a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),a("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");if(void 0!==f&&(this.createTag=f),b.call(this,c,d),a.isArray(e))for(var g=0;g0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{minimum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),a("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),a("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.position=function(){},c.prototype.destroy=function(){this.$dropdown.remove()},c.prototype.bind=function(a){var b=this;a.on("select",function(a){b._onSelect(a)}),a.on("unselect",function(a){b._onUnSelect(a)})},c.prototype._onSelect=function(){this.trigger("close")},c.prototype._onUnSelect=function(){this.trigger("close")},c}),a("select2/dropdown/search",["jquery","../utils"],function(a){function b(){}return b.prototype.render=function(b){var c=b.call(this),d=a('');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},b.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),this.$search.on("keydown",function(a){d.trigger("keypress",a),d._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("keyup",function(a){d.handleSearch(a)}),b.on("open",function(){d.$search.attr("tabindex",0),d.$search.focus(),window.setTimeout(function(){d.$search.focus()},0)}),b.on("close",function(){d.$search.attr("tabindex",-1),d.$search.val("")}),b.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=d.showSearch(a);b?d.$searchContainer.removeClass("select2-search--hide"):d.$searchContainer.addClass("select2-search--hide")}})},b.prototype.handleSearch=function(){if(!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},b.prototype.showSearch=function(){return!0},b}),a("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),a("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a('
    • '),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),a("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(a,b,c){this.$dropdownParent=c.get("dropdownParent")||document.body,a.call(this,b,c)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a(""),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c){var d=this,e="scroll.select2."+c.id,f="resize.select2."+c.id,g="orientationchange.select2."+c.id;$watchers=this.$container.parents().filter(b.hasScroll),$watchers.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),$watchers.on(e,function(){var b=a(this).data("select2-scroll-position");a(this).scrollTop(b.y)}),a(window).on(e+" "+f+" "+g,function(){d._positionDropdown(),d._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c){var d="scroll.select2."+c.id,e="resize.select2."+c.id,f="orientationchange.select2."+c.id;$watchers=this.$container.parents().filter(b.hasScroll),$watchers.off(d),a(window).off(d+" "+e+" "+f)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=(this.$container.position(),this.$container.offset());f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.topf.bottom+h.height,l={left:f.left,top:g.bottom};c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){this.$dropdownContainer.width(),this.$dropdown.css({width:this.$container.outerWidth(!1)+"px"})},c.prototype._showDropdown=function(){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),a("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){count=0;for(var c=0;c0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),null!=l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var B=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,B)}if(null!=l.initSelection){var C=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,C)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var D=j.Decorate(u,v);l.dropdownAdapter=D}l.minimumResultsForSearch>0&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter&&(l.selectionAdapter=l.multiple?e:d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g))),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),l.selectionAdapter=j.Decorate(l.selectionAdapter,i)),"string"==typeof l.language)if(l.language.indexOf("-")>0){var E=l.language.split("-"),F=E[0];l.language=[l.language,F]}else l.language=[l.language];if(a.isArray(l.language)){var G=new k;l.language.push("en");for(var H=l.language,I=0;I0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"select2/",amdLanguageBase:"select2/i18n/",language:B,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},C.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var D=new C;return D}),a("select2/options",["jquery","./defaults","./utils"],function(a,b,c){function d(a,c){this.options=a,null!=c&&this.fromElement(c),this.options=b.apply(this.options)}return d.prototype.fromElement=function(b){var d=["select2"];null==this.options.multiple&&(this.options.multiple=b.prop("multiple")),null==this.options.disabled&&(this.options.disabled=b.prop("disabled")),null==this.options.language&&(b.prop("lang")?this.options.language=b.prop("lang").toLowerCase():b.closest("[lang]").prop("lang")&&(this.options.language=b.closest("[lang]").prop("lang"))),null==this.options.dir&&(this.options.dir=b.prop("dir")?b.prop("dir"):b.closest("[dir]").prop("dir")?b.closest("[dir]").prop("dir"):"ltr"),b.prop("disabled",this.options.disabled),b.prop("multiple",this.options.multiple),b.data("select2-tags")&&(console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),b.data("data",b.data("select2-tags")),b.data("tags",!0)),b.data("ajax-url")&&(console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),b.data("ajax--url",b.data("ajax-url")));var e=b.data();e=c._convertData(e);for(var f in e)d.indexOf(f)>-1||(a.isPlainObject(this.options[f])?a.extend(this.options[f],e[f]):this.options[f]=e[f]);return this},d.prototype.get=function(a){return this.options[a]},d.prototype.set=function(a,b){this.options[a]=b},d}),a("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=this.options.get("dataAdapter");this.data=new d(a,this.options);var f=this.render();this._placeContainer(f);var g=this.options.get("selectionAdapter");this.selection=new g(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,f);var h=this.options.get("dropdownAdapter");this.dropdown=new h(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,f);var i=this.options.get("resultsAdapter");this.results=new i(a,this.options,this.data),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var j=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.data.current(function(a){j.trigger("selection:update",{data:a})}),a.hide(),this._syncAttributes(),this._tabindex=a.attr("tabindex")||0,a.attr("tabindex","-1"),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;var g=f.split(";");for(i=0,l=g.length;l>i;i+=1){attr=g[i].replace(/\s/g,"");var h=attr.match(c);if(null!==h&&h.length>=1)return h[1]}return null}return b},e.prototype._bindAdapters=function(){this.data.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.data.current(function(a){b.trigger("selection:update",{data:a})})}),this._sync=c.bind(this._syncAttributes,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._sync);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d&&(this._observer=new d(function(c){a.each(c,b._sync)}),this._observer.observe(this.$element[0],{attributes:!0,subtree:!1}))},e.prototype._registerDataEvents=function(){var a=this;this.data.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var a=this,b=["toggle"];this.selection.on("toggle",function(){a.toggleDropdown()}),this.selection.on("*",function(c,d){-1===b.indexOf(c)&&a.trigger(c,d)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("query",function(b){this.data.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.data.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ENTER?(a.trigger("results:select"),b.preventDefault()):c===d.UP?(a.trigger("results:previous"),b.preventDefault()):c===d.DOWN?(a.trigger("results:next"),b.preventDefault()):(c===d.ESC||c===d.TAB)&&(a.close(),b.preventDefault()):(c===d.ENTER||c===d.SPACE||(c===d.DOWN||c===d.UP)&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable")):this.trigger("enable")},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||(this.trigger("query",{}),this.trigger("open"))},e.prototype.close=function(){this.isOpen()&&this.trigger("close")},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.enable=function(a){console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),0===a.length&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.val=function(b){if(console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._sync),null!=this._observer&&(this._observer.disconnect(),this._observer=null),this._sync=null,this.$element.off(".select2"),this.$element.attr("tabindex",this._tabindex),this.$element.show(),this.$element.removeData("select2"),this.data.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.data=null,this.selection=null,this.dropdown=null,this.results=null},e.prototype.render=function(){var b=a('');return b.attr("dir",this.options.get("dir")),this.$container=b,this.$container.addClass("select2-container--"+this.options.get("theme")),b.data("element",this.$element),b},e}),a("jquery.select2",["jquery","./select2/core","./select2/defaults"],function(a,c,d){try{b("jquery.mousewheel")}catch(e){}return null==a.fn.select2&&(a.fn.select2=function(b){if(b=b||{},"object"==typeof b)return this.each(function(){{var d=a.extend({},b,!0);new c(a(this),d)}}),this;if("string"==typeof b){var d=this.data("select2"),e=Array.prototype.slice.call(arguments,1);return d[b](e)}throw new Error("Invalid arguments for Select2: "+b)}),null==a.fn.select2.defaults&&(a.fn.select2.defaults=d),c}),b("jquery.select2"),jQuery.fn.select2.amd={define:a,require:b}}(); \ No newline at end of file +/*! Select2 4.0.2 | https://github.com/select2/select2/blob/master/LICENSE.md */!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b=function(){if(a&&a.fn&&a.fn.select2&&a.fn.select2.amd)var b=a.fn.select2.amd;var b;return function(){if(!b||!b.requirejs){b?c=b:b={};var a,c,d;!function(b){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.slice(0,n.length-1).concat(a),k=0;k0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,c){return function(){var d=v.call(arguments,0);return"string"!=typeof d[0]&&1===d.length&&d.push(null),n.apply(b,d.concat([a,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var c=r[a];delete r[a],t[a]=!0,m.apply(b,c)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||a,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n0&&(b.call(arguments,a.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=b(c),g=b(a);c.displayName=a.displayName,d.prototype=new e;for(var h=0;hc;c++)a[c].apply(this,b)},c.Observable=d,c.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(36)}return b},c.bind=function(a,b){return function(){a.apply(b,arguments)}},c._convertData=function(a){for(var b in a){var c=b.split("-"),d=a;if(1!==c.length){for(var e=0;e":">",'"':""","'":"'","/":"/"};return"string"!=typeof a?a:String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})},c.appendMany=function(b,c){if("1.7"===a.fn.jquery.substr(0,3)){var d=a();a.map(c,function(a){d=d.add(a)}),c=d}b.append(c)},c}),b.define("select2/results",["jquery","./utils"],function(a,b){function c(a,b,d){this.$element=a,this.data=d,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('
        ');return this.options.get("multiple")&&b.attr("aria-multiselectable","true"),this.$results=b,b},c.prototype.clear=function(){this.$results.empty()},c.prototype.displayMessage=function(b){var c=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var d=a('
      • '),e=this.options.get("translations").get(b.message);d.append(c(e(b.args))),d[0].className+=" select2-results__message",this.$results.append(d)},c.prototype.hideMessages=function(){this.$results.find(".select2-results__message").remove()},c.prototype.append=function(a){this.hideLoading();var b=[];if(null==a.results||0===a.results.length)return void(0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"}));a.results=this.sort(a.results);for(var c=0;c-1?b.attr("aria-selected","true"):b.attr("aria-selected","false")});var f=e.filter("[aria-selected=true]");f.length>0?f.first().trigger("mouseenter"):e.first().trigger("mouseenter")})},c.prototype.showLoading=function(a){this.hideLoading();var b=this.options.get("translations").get("searching"),c={disabled:!0,loading:!0,text:b(a)},d=this.option(c);d.className+=" loading-results",this.$results.prepend(d)},c.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},c.prototype.option=function(b){var c=document.createElement("li");c.className="select2-results__option";var d={role:"treeitem","aria-selected":"false"};b.disabled&&(delete d["aria-selected"],d["aria-disabled"]="true"),null==b.id&&delete d["aria-selected"],null!=b._resultId&&(c.id=b._resultId),b.title&&(c.title=b.title),b.children&&(d.role="group",d["aria-label"]=b.text,delete d["aria-selected"]);for(var e in d){var f=d[e];c.setAttribute(e,f)}if(b.children){var g=a(c),h=document.createElement("strong");h.className="select2-results__group";a(h);this.template(b,h);for(var i=[],j=0;j",{"class":"select2-results__options select2-results__options--nested"});m.append(i),g.append(h),g.append(m)}else this.template(b,c);return a.data(c,"data",b),c},c.prototype.bind=function(b,c){var d=this,e=b.id+"-results";this.$results.attr("id",e),b.on("results:all",function(a){d.clear(),d.append(a.data),b.isOpen()&&d.setClasses()}),b.on("results:append",function(a){d.append(a.data),b.isOpen()&&d.setClasses()}),b.on("query",function(a){d.hideMessages(),d.showLoading(a)}),b.on("select",function(){b.isOpen()&&d.setClasses()}),b.on("unselect",function(){b.isOpen()&&d.setClasses()}),b.on("open",function(){d.$results.attr("aria-expanded","true"),d.$results.attr("aria-hidden","false"),d.setClasses(),d.ensureHighlightVisible()}),b.on("close",function(){d.$results.attr("aria-expanded","false"),d.$results.attr("aria-hidden","true"),d.$results.removeAttr("aria-activedescendant")}),b.on("results:toggle",function(){var a=d.getHighlightedResults();0!==a.length&&a.trigger("mouseup")}),b.on("results:select",function(){var a=d.getHighlightedResults();if(0!==a.length){var b=a.data("data");"true"==a.attr("aria-selected")?d.trigger("close",{}):d.trigger("select",{data:b})}}),b.on("results:previous",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a);if(0!==c){var e=c-1;0===a.length&&(e=0);var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top,h=f.offset().top,i=d.$results.scrollTop()+(h-g);0===e?d.$results.scrollTop(0):0>h-g&&d.$results.scrollTop(i)}}),b.on("results:next",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a),e=c+1;if(!(e>=b.length)){var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top+d.$results.outerHeight(!1),h=f.offset().top+f.outerHeight(!1),i=d.$results.scrollTop()+h-g;0===e?d.$results.scrollTop(0):h>g&&d.$results.scrollTop(i)}}),b.on("results:focus",function(a){a.element.addClass("select2-results__option--highlighted")}),b.on("results:message",function(a){d.displayMessage(a)}),a.fn.mousewheel&&this.$results.on("mousewheel",function(a){var b=d.$results.scrollTop(),c=d.$results.get(0).scrollHeight-b+a.deltaY,e=a.deltaY>0&&b-a.deltaY<=0,f=a.deltaY<0&&c<=d.$results.height();e?(d.$results.scrollTop(0),a.preventDefault(),a.stopPropagation()):f&&(d.$results.scrollTop(d.$results.get(0).scrollHeight-d.$results.height()),a.preventDefault(),a.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(b){var c=a(this),e=c.data("data");return"true"===c.attr("aria-selected")?void(d.options.get("multiple")?d.trigger("unselect",{originalEvent:b,data:e}):d.trigger("close",{})):void d.trigger("select",{originalEvent:b,data:e})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(b){var c=a(this).data("data");d.getHighlightedResults().removeClass("select2-results__option--highlighted"),d.trigger("results:focus",{data:c,element:a(this)})})},c.prototype.getHighlightedResults=function(){var a=this.$results.find(".select2-results__option--highlighted");return a},c.prototype.destroy=function(){this.$results.remove()},c.prototype.ensureHighlightVisible=function(){var a=this.getHighlightedResults();if(0!==a.length){var b=this.$results.find("[aria-selected]"),c=b.index(a),d=this.$results.offset().top,e=a.offset().top,f=this.$results.scrollTop()+(e-d),g=e-d;f-=2*a.outerHeight(!1),2>=c?this.$results.scrollTop(0):(g>this.$results.outerHeight()||0>g)&&this.$results.scrollTop(f)}},c.prototype.template=function(b,c){var d=this.options.get("templateResult"),e=this.options.get("escapeMarkup"),f=d(b,c);null==f?c.style.display="none":"string"==typeof f?c.innerHTML=e(f):a(c).append(f)},c}),b.define("select2/keys",[],function(){var a={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46};return a}),b.define("select2/selection/base",["jquery","../utils","../keys"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,b.Observable),d.prototype.render=function(){var b=a('');return this._tabindex=0,null!=this.$element.data("old-tabindex")?this._tabindex=this.$element.data("old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),b.attr("title",this.$element.attr("title")),b.attr("tabindex",this._tabindex),this.$selection=b,b},d.prototype.bind=function(a,b){var d=this,e=(a.id+"-container",a.id+"-results");this.container=a,this.$selection.on("focus",function(a){d.trigger("focus",a)}),this.$selection.on("blur",function(a){d._handleBlur(a)}),this.$selection.on("keydown",function(a){d.trigger("keypress",a),a.which===c.SPACE&&a.preventDefault()}),a.on("results:focus",function(a){d.$selection.attr("aria-activedescendant",a.data._resultId)}),a.on("selection:update",function(a){d.update(a.data)}),a.on("open",function(){d.$selection.attr("aria-expanded","true"),d.$selection.attr("aria-owns",e),d._attachCloseHandler(a)}),a.on("close",function(){d.$selection.attr("aria-expanded","false"),d.$selection.removeAttr("aria-activedescendant"),d.$selection.removeAttr("aria-owns"),d.$selection.focus(),d._detachCloseHandler(a)}),a.on("enable",function(){d.$selection.attr("tabindex",d._tabindex)}),a.on("disable",function(){d.$selection.attr("tabindex","-1")})},d.prototype._handleBlur=function(b){var c=this;window.setTimeout(function(){document.activeElement==c.$selection[0]||a.contains(c.$selection[0],document.activeElement)||c.trigger("blur",b)},1)},d.prototype._attachCloseHandler=function(b){a(document.body).on("mousedown.select2."+b.id,function(b){var c=a(b.target),d=c.closest(".select2"),e=a(".select2.select2-container--open");e.each(function(){var b=a(this);if(this!=d[0]){var c=b.data("element");c.select2("close")}})})},d.prototype._detachCloseHandler=function(b){a(document.body).off("mousedown.select2."+b.id)},d.prototype.position=function(a,b){var c=b.find(".selection");c.append(a)},d.prototype.destroy=function(){this._detachCloseHandler(this.container)},d.prototype.update=function(a){throw new Error("The `update` method must be defined in child classes.")},d}),b.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(a,b,c,d){function e(){e.__super__.constructor.apply(this,arguments)}return c.Extend(e,b),e.prototype.render=function(){var a=e.__super__.render.call(this);return a.addClass("select2-selection--single"),a.html(''),a},e.prototype.bind=function(a,b){var c=this;e.__super__.bind.apply(this,arguments);var d=a.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",d),this.$selection.attr("aria-labelledby",d),this.$selection.on("mousedown",function(a){1===a.which&&c.trigger("toggle",{originalEvent:a})}),this.$selection.on("focus",function(a){}),this.$selection.on("blur",function(a){}),a.on("selection:update",function(a){c.update(a.data)})},e.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},e.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},e.prototype.selectionContainer=function(){return a("")},e.prototype.update=function(a){if(0===a.length)return void this.clear();var b=a[0],c=this.$selection.find(".select2-selection__rendered"),d=this.display(b,c);c.empty().append(d),c.prop("title",b.title||b.text)},e}),b.define("select2/selection/multiple",["jquery","./base","../utils"],function(a,b,c){function d(a,b){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--multiple"),a.html('
          '),a},d.prototype.bind=function(b,c){var e=this;d.__super__.bind.apply(this,arguments),this.$selection.on("click",function(a){e.trigger("toggle",{originalEvent:a})}),this.$selection.on("click",".select2-selection__choice__remove",function(b){if(!e.options.get("disabled")){var c=a(this),d=c.parent(),f=d.data("data");e.trigger("unselect",{originalEvent:b,data:f})}})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},d.prototype.selectionContainer=function(){var b=a('
        • ×
        • ');return b},d.prototype.update=function(a){if(this.clear(),0!==a.length){for(var b=[],d=0;d1;if(d||c)return a.call(this,b);this.clear();var e=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(e)},b}),b.define("select2/selection/allowClear",["jquery","../keys"],function(a,b){function c(){}return c.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(a){d._handleClear(a)}),b.on("keypress",function(a){d._handleKeyboardClear(a,b)})},c.prototype._handleClear=function(a,b){if(!this.options.get("disabled")){var c=this.$selection.find(".select2-selection__clear");if(0!==c.length){b.stopPropagation();for(var d=c.data("data"),e=0;e0||0===c.length)){var d=a('×');d.data("data",c),this.$selection.find(".select2-selection__rendered").prepend(d)}},c}),b.define("select2/selection/search",["jquery","../utils","../keys"],function(a,b,c){function d(a,b,c){a.call(this,b,c)}return d.prototype.render=function(b){var c=a('');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return this._transferTabIndex(),d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.trigger("focus")}),b.on("close",function(){e.$search.val(""),e.$search.removeAttr("aria-activedescendant"),e.$search.trigger("focus")}),b.on("enable",function(){e.$search.prop("disabled",!1),e._transferTabIndex()}),b.on("disable",function(){e.$search.prop("disabled",!0)}),b.on("focus",function(a){e.$search.trigger("focus")}),b.on("results:focus",function(a){e.$search.attr("aria-activedescendant",a.id)}),this.$selection.on("focusin",".select2-search--inline",function(a){e.trigger("focus",a)}),this.$selection.on("focusout",".select2-search--inline",function(a){e._handleBlur(a)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f),a.preventDefault()}}});var f=document.documentMode,g=f&&11>=f;this.$selection.on("input.searchcheck",".select2-search--inline",function(a){return g?void e.$selection.off("input.search input.searchcheck"):void e.$selection.off("keyup.search")}),this.$selection.on("keyup.search input.search",".select2-search--inline",function(a){if(g&&"input"===a.type)return void e.$selection.off("input.search input.searchcheck");var b=a.which;b!=c.SHIFT&&b!=c.CTRL&&b!=c.ALT&&b!=c.TAB&&e.handleSearch(a)})},d.prototype._transferTabIndex=function(a){this.$search.attr("tabindex",this.$selection.attr("tabindex")),this.$selection.attr("tabindex","-1")},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){var c=this.$search[0]==document.activeElement;this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch(),c&&this.$search.focus()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.$search.val(b.text),this.handleSearch()},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),b.define("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==a.inArray(b,f)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==a.inArray(b,g)&&(c.prevented=d.isDefaultPrevented())}})},b}),b.define("select2/translation",["jquery","require"],function(a,b){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),b.define("select2/diacritics",[],function(){var a={"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","B":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","C":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","E":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","M":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","N":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","P":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Q":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","R":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","S":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","T":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","U":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","V":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","W":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Y":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","h":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","v":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ω":"ω","ς":"σ"};return a}),b.define("select2/data/base",["../utils"],function(a){function b(a,c){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(a){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(a,b){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(a,b){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),b.define("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(a.selected=!0,c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change");if(this.$element.prop("multiple"))this.current(function(d){var e=[];a=[a],a.push.apply(a,d);for(var f=0;f=0){var k=f.filter(d(j)),l=this.item(k),m=c.extend(!0,{},j,l),n=this.option(m);k.replaceWith(n)}else{var o=this.option(j);if(j.children){var p=this.convertToOptions(j.children);b.appendMany(o,p)}h.push(o)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(a,b){this.ajaxOptions=this._applyDefaults(b.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),d.__super__.constructor.call(this,a,b)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return c.extend({},a,{q:a.term})},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){e.trigger("results:message",{message:"errorLoading"})});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url.call(this.$element,a)),"function"==typeof f.data&&(f.data=f.data.call(this.$element,a)),this.ajaxOptions.delay&&""!==a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");void 0!==f&&(this.createTag=f);var g=d.get("insertTag");if(void 0!==g&&(this.insertTag=g),b.call(this,c,d),a.isArray(e))for(var h=0;h0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),b.define("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.bind=function(){},c.prototype.position=function(a,b){},c.prototype.destroy=function(){this.$dropdown.remove()},c}),b.define("select2/dropdown/search",["jquery","../utils"],function(a,b){function c(){}return c.prototype.render=function(b){var c=b.call(this),d=a('');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},c.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$search.on("keydown",function(a){e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("input",function(b){a(this).off("keyup")}),this.$search.on("keyup input",function(a){e.handleSearch(a)}),c.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus(),window.setTimeout(function(){e.$search.focus()},0)}),c.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),c.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=e.showSearch(a);b?e.$searchContainer.removeClass("select2-search--hide"):e.$searchContainer.addClass("select2-search--hide")}})},c.prototype.handleSearch=function(a){if(!this._keyUpPrevented){var b=this.$search.val();this.trigger("query",{term:b})}this._keyUpPrevented=!1},c.prototype.showSearch=function(a,b){return!0},c}),b.define("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),b.define("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a('
        • '),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),b.define("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(b,c,d){this.$dropdownParent=d.get("dropdownParent")||a(document.body),b.call(this,c,d)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.destroy=function(a){a.call(this),this.$dropdownContainer.remove()},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a(""),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(a){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c,d){var e=this,f="scroll.select2."+d.id,g="resize.select2."+d.id,h="orientationchange.select2."+d.id,i=this.$container.parents().filter(b.hasScroll);i.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),i.on(f,function(b){var c=a(this).data("select2-scroll-position");a(this).scrollTop(c.y)}),a(window).on(f+" "+g+" "+h,function(a){e._positionDropdown(),e._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c,d){var e="scroll.select2."+d.id,f="resize.select2."+d.id,g="orientationchange.select2."+d.id,h=this.$container.parents().filter(b.hasScroll);h.off(e),a(window).off(e+" "+f+" "+g)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=this.$container.offset();f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.topf.bottom+h.height,l={left:f.left,top:g.bottom},m=this.$dropdownParent;"static"===m.css("position")&&(m=m.offsetParent());var n=m.offset();l.top-=n.top,l.left-=n.left,c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){var a={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(a.minWidth=a.width,a.width="auto"),this.$dropdown.css(a)},c.prototype._showDropdown=function(a){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),b.define("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){for(var c=0,d=0;d0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var C=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,C)}if(null!=l.initSelection){var D=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,D)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var E=j.Decorate(u,v);l.dropdownAdapter=E}if(0!==l.minimumResultsForSearch&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.closeOnSelect&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,B)),null!=l.dropdownCssClass||null!=l.dropdownCss||null!=l.adaptDropdownCssClass){var F=b(l.amdBase+"compat/dropdownCss");l.dropdownAdapter=j.Decorate(l.dropdownAdapter,F)}l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter){if(l.multiple?l.selectionAdapter=e:l.selectionAdapter=d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f)),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g)),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),null!=l.containerCssClass||null!=l.containerCss||null!=l.adaptContainerCssClass){var G=b(l.amdBase+"compat/containerCss");l.selectionAdapter=j.Decorate(l.selectionAdapter,G)}l.selectionAdapter=j.Decorate(l.selectionAdapter,i)}if("string"==typeof l.language)if(l.language.indexOf("-")>0){var H=l.language.split("-"),I=H[0];l.language=[l.language,I]}else l.language=[l.language];if(a.isArray(l.language)){var J=new k;l.language.push("en");for(var K=l.language,L=0;L0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:j.escapeMarkup,language:C,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},D.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var E=new D;return E}),b.define("select2/options",["require","jquery","./defaults","./utils"],function(a,b,c,d){function e(b,e){if(this.options=b,null!=e&&this.fromElement(e),this.options=c.apply(this.options),e&&e.is("input")){var f=a(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=d.Decorate(this.options.dataAdapter,f)}}return e.prototype.fromElement=function(a){var c=["select2"];null==this.options.multiple&&(this.options.multiple=a.prop("multiple")),null==this.options.disabled&&(this.options.disabled=a.prop("disabled")),null==this.options.language&&(a.prop("lang")?this.options.language=a.prop("lang").toLowerCase():a.closest("[lang]").prop("lang")&&(this.options.language=a.closest("[lang]").prop("lang"))),null==this.options.dir&&(a.prop("dir")?this.options.dir=a.prop("dir"):a.closest("[dir]").prop("dir")?this.options.dir=a.closest("[dir]").prop("dir"):this.options.dir="ltr"),a.prop("disabled",this.options.disabled),a.prop("multiple",this.options.multiple),a.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),a.data("data",a.data("select2Tags")),a.data("tags",!0)),a.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),a.attr("ajax--url",a.data("ajaxUrl")),a.data("ajax--url",a.data("ajaxUrl")));var e={};e=b.fn.jquery&&"1."==b.fn.jquery.substr(0,2)&&a[0].dataset?b.extend(!0,{},a[0].dataset,a.data()):a.data();var f=b.extend(!0,{},e);f=d._convertData(f);for(var g in f)b.inArray(g,c)>-1||(b.isPlainObject(this.options[g])?b.extend(this.options[g],f[g]):this.options[g]=f[g]);return this},e.prototype.get=function(a){return this.options[a]},e.prototype.set=function(a,b){this.options[a]=b},e}),b.define("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=a.attr("tabindex")||0;a.data("old-tabindex",d),a.attr("tabindex","-1");var f=this.options.get("dataAdapter");this.dataAdapter=new f(a,this.options);var g=this.render();this._placeContainer(g);var h=this.options.get("selectionAdapter");this.selection=new h(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,g);var i=this.options.get("dropdownAdapter");this.dropdown=new i(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,g);var j=this.options.get("resultsAdapter");this.results=new j(a,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var k=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(a){k.trigger("selection:update",{data:a})}),a.addClass("select2-hidden-accessible"),a.attr("aria-hidden","true"),this._syncAttributes(),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b=b.replace(/(:|\.|\[|\]|,)/g,""),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;for(var g=f.split(";"),h=0,i=g.length;i>h;h+=1){var j=g[h].replace(/\s/g,""),k=j.match(c);if(null!==k&&k.length>=1)return k[1]}return null}return b},e.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.dataAdapter.current(function(a){b.trigger("selection:update",{data:a})})}),this._sync=c.bind(this._syncAttributes,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._sync);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d?(this._observer=new d(function(c){a.each(c,b._sync)}),this._observer.observe(this.$element[0],{attributes:!0,subtree:!1})):this.$element[0].addEventListener&&this.$element[0].addEventListener("DOMAttrModified",b._sync,!1)},e.prototype._registerDataEvents=function(){var a=this;this.dataAdapter.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var b=this,c=["toggle","focus"];this.selection.on("toggle",function(){b.toggleDropdown()}),this.selection.on("focus",function(a){b.focus(a)}),this.selection.on("*",function(d,e){-1===a.inArray(d,c)&&b.trigger(d,e)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("blur",function(){a.$container.removeClass("select2-container--focus")}),this.on("query",function(b){a.isOpen()||a.trigger("open",{}),this.dataAdapter.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.dataAdapter.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ESC||c===d.TAB||c===d.UP&&b.altKey?(a.close(),b.preventDefault()):c===d.ENTER?(a.trigger("results:select",{}),b.preventDefault()):c===d.SPACE&&b.ctrlKey?(a.trigger("results:toggle",{}),b.preventDefault()):c===d.UP?(a.trigger("results:previous",{}),b.preventDefault()):c===d.DOWN&&(a.trigger("results:next",{}),b.preventDefault()):(c===d.ENTER||c===d.SPACE||c===d.DOWN&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable",{})):this.trigger("enable",{})},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(void 0===b&&(b={}),a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||this.trigger("query",{})},e.prototype.close=function(){this.isOpen()&&this.trigger("close",{})},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.hasFocus=function(){return this.$container.hasClass("select2-container--focus")},e.prototype.focus=function(a){this.hasFocus()||(this.$container.addClass("select2-container--focus"),this.trigger("focus",{}))},e.prototype.enable=function(a){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),(null==a||0===a.length)&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var a=[];return this.dataAdapter.current(function(b){a=b}),a},e.prototype.val=function(b){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==b||0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._sync),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&this.$element[0].removeEventListener("DOMAttrModified",this._sync,!1),this._sync=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null},e.prototype.render=function(){var b=a('');return b.attr("dir",this.options.get("dir")),this.$container=b,this.$container.addClass("select2-container--"+this.options.get("theme")),b.data("element",this.$element),b},e}),b.define("jquery-mousewheel",["jquery"],function(a){return a}),b.define("jquery.select2",["jquery","jquery-mousewheel","./select2/core","./select2/defaults"],function(a,b,c,d){if(null==a.fn.select2){var e=["open","close","destroy"];a.fn.select2=function(b){if(b=b||{},"object"==typeof b)return this.each(function(){var d=a.extend(!0,{},b);new c(a(this),d)}),this;if("string"==typeof b){var d;return this.each(function(){var c=a(this).data("select2");null==c&&window.console&&console.error&&console.error("The select2('"+b+"') method was called on an element that is not using Select2.");var e=Array.prototype.slice.call(arguments,1);d=c[b].apply(c,e)}),a.inArray(b,e)>-1?this:d}throw new Error("Invalid arguments for Select2: "+b)}}return null==a.fn.select2.defaults&&(a.fn.select2.defaults=d),c}),{define:b.define,require:b.require}}(),c=b.require("jquery.select2");return a.fn.select2.amd=b,c}); \ No newline at end of file diff --git a/assets/stylesheets/dmsf.css b/assets/stylesheets/dmsf.css deleted file mode 100644 index 459cb512..00000000 --- a/assets/stylesheets/dmsf.css +++ /dev/null @@ -1,332 +0,0 @@ -/* -* Redmine plugin for Document Management System "Features" -* -* Copyright (C) 2011 Vít Jonáš -* Copyright (C) 2011-15 Karel Pičman -* -* 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. -*/ - -button { - vertical-align: middle; - margin-top: 1px; - margin-bottom: 1px; -} - -/* DMSF entries list */ -table.entries tbody td, table.entries tbody tr:hover td { - border-left: solid 1px #D7D7D7; - border-right: solid 1px #D7D7D7; -} - -table.entries td.modified { - min-width: 127px; - width: 127px; -} - -table.entries td.actions { - min-width: 96px; - width: 96px; -} - -table.entries td.title { - width: 40%; -} - -table.entries th.check, table.entries td.check { - width: 17px; - padding: 2px; - text-align: left; -} - -table.entries th.check div.DataTables_sort_wrapper { - padding: 0; -} - -table.display th { - font-weight: bold; -} - -table.display tbody tr.even { - background-color: #F8F8F8; -} - -table.display tbody tr.odd { - background-color: #FFFFFF; -} - -table.display tbody tr:hover { - background-color:#ffffdd; -} - -form.dmfs_entries { - margin-bottom: 10px; - display: block; -} - -div.dataTables_wrapper div.fg-toolbar { - font-size: 0.9em; -} - -div.dataTables_wrapper div.fg-toolbar input, div.dataTables_wrapper div.fg-toolbar button, -div.dataTables_wrapper div.fg-toolbar select { - font-size: 0.9em; -} - -input[type="checkbox"] { - margin: 1px; -} - -div.filename { - padding: 0 10px 0 10px; float: right; font-size: 0.8em; - white-space: nowrap; -} - -td.size { - font-size: 0.8em; - white-space: nowrap; -} - -td.modified { - font-size: 0.8em; -} - -td.modified img { - vertical-align:text-top; -} - -td.author { - font-size: 0.8em; -} - -td.version { - font-size: 0.8em; - white-space: nowrap; -} - -td.version img { - vertical-align:text-top; -} - -td.workflow { - font-size: 0.8em; - white-space: nowrap; -} - -/* DMSF entries upload control */ - -.dmsf_upload textarea { - width: 90%; -} - -.dmsf_upload legend { - font-weight: bold; -} - -.dmsf_upload .warning { - padding: 4px 4px 4px 30px; -} - -#uploader div.flash { - border: none; -} - -.invisible { - display: none; -} - -div.upload_select { - float: right; - white-space: nowrap; - line-height: 1.4em; - padding-left: 10px; - font-size: 0.9em; -} - -div.upload_select input, div.upload_select select { - font-size: 0.9em; -} - -.plupload_scroll { - max-height: 1000px; - min-height: 175px; -} - -.ui-resizable-s { - bottom: 0px; -} - -/* DMSF detail/commit page */ - -.dmsf_detail .warning { - padding: 4px 4px 4px 30px; -} - -.dmsf_detail div.data { - margin-left: 110px; -} - -.dmsf_detail label { - font-weight: bold; - width: 100px; - float: left; - text-align: right; - padding-right: 10px; -} - -p.no-ident { - margin-bottom: 0; - margin-top: 0; -} - -table.access-table tbody td, table.access-table tbody tr:hover td { - border: solid 1px #D7D7D7; -} - -/* Custom field */ -.dmsf-customfields { - margin: 5px 0px 5px 0px; -} - -.dmsf-customfields .customfield label { - font-weight: bold; - width: 100px; - display: block; - float: left; -} - -/* Approval workflow */ -#admin-menu a.approvalworkflows { background-image: url(../images/ticket_go.png); } -#users_for_delegate {height: 200px; overflow:auto;} -#users_for_delegate label {display: block;} - -.revision_box{ - padding: 0px 0px 0px 0px; - margin-bottom: 10px; - background-color:#f6f6f6; - color:#505050; - line-height:1.5em; -} - -div.revision_box .ui-widget-header { - font-weight: normal; -} - -table.list th { - text-align: left; -} - -table.list td.note { - width: 20%; -} - -table.list td.reorder { - width: 15%; -} - -table.list td.step { - text-align: center; - width: 16px; -} - -.log_header_box{ - padding:6px; - margin-bottom: 10px; -} -.log_header_left { - width: 50%; - float: left; -} - -.log_header_box label{ - font-weight: bold; - margin-left: 0px; - margin-right: 3px; - padding: 3px 0 3px 0; -} - -.modal a, .modal a:link, .modal a:visited{ color: #169; text-decoration: none; } -.modal a:hover, .modal a:active{ color: #c61a1a; text-decoration: underline;} -.modal{ font-size: 12px} - -/* Command icons */ -.dmsf_icon-narrow { padding-left: 16px } -.icon-link { background-image: url(../images/link.png); } -.icon-notification-on { background-image: url(../images/notify.png); margin-left: 3px; } -.icon-notification-off { background-image: url(../images/notifynot.png); margin-left: 3px; } -.icon-dmsf-lock { background-image: url(../images/lock.png); } -.icon-dmsf-unlock { background-image: url(../images/unlock.png); } -.icon-dmsf-locked { background-image: url(../images/locked.png); margin-left: 2px } - -/* File types */ -tr.gray .icon-folder { background-image: url(../images/folder_gray.png); } - -.icon-file.filetype-doc, .icon-file.filetype-docx { background-image: url(../images/filetypes/doc.png); } -.icon-file.filetype-xls, .icon-file.filetype-xlsx { background-image: url(../images/filetypes/xls.png); } -.icon-file.filetype-ppt, .icon-file.filetype-pptx { background-image: url(../images/filetypes/ppt.png); } -.icon-file.filetype-vsd, .icon-file.filetype-vsdx { background-image: url(../images/filetypes/vsd.png); } -.icon-file.filetype-mpp { background-image: url(../images/filetypes/mpp.png); } -.icon-file.filetype-odt { background-image: url(../images/filetypes/odt.png); } -.icon-file.filetype-ods { background-image: url(../images/filetypes/ods.png); } -.icon-file.filetype-odp { background-image: url(../images/filetypes/odp.png); } -.icon-file.filetype-odg { background-image: url(../images/filetypes/odg.png); } - -tr.gray .icon-file.filetype-doc { background-image: url(../images/filetypes/doc_gray.png); } -tr.gray .icon-file.filetype-docx { background-image: url(../images/filetypes/doc_gray.png); } -tr.gray .icon-file.filetype-xls { background-image: url(../images/filetypes/xls_gray.png); } -tr.gray .icon-file.filetype-xlsx { background-image: url(../images/filetypes/xls_gray.png); } -tr.gray .icon-file.filetype-ppt { background-image: url(../images/filetypes/ppt_gray.png); } -tr.gray .icon-file.filetype-pptx { background-image: url(../images/filetypes/ppt_gray.png); } -tr.gray .icon-file.filetype-vsd { background-image: url(../images/filetypes/vsd_gray.png); } -tr.gray .icon-file.filetype-vsdx { background-image: url(../images/filetypes/vsd_gray.png); } -tr.gray .icon-file.filetype-mpp { background-image: url(../images/filetypes/mpp_gray.png); } -tr.gray .icon-file.filetype-odt { background-image: url(../images/filetypes/odt_gray.png); } -tr.gray .icon-file.filetype-ods { background-image: url(../images/filetypes/ods_gray.png); } -tr.gray .icon-file.filetype-odp { background-image: url(../images/filetypes/odp_gray.png); } -tr.gray .icon-file.filetype-odg { background-image: url(../images/filetypes/odg_gray.png); } - -tr.gray .icon-file.text-x-c { background-image: url(../images/filetypes/c_gray.png); } -tr.gray .icon-file.text-x-csharp { background-image: url(../images/filetypes/csharp_gray.png); } -tr.gray .icon-file.text-x-java { background-image: url(../images/files/filetypes/java_gray.png); } -tr.gray .icon-file.text-x-javascript { background-image: url(../images/filetypes/js_gray.png); } -tr.gray .icon-file.text-x-php { background-image: url(../images/filetypes/php_gray.png); } -tr.gray .icon-file.text-x-ruby { background-image: url(../images/filetypes/ruby_gray.png); } -tr.gray .icon-file.text-xml { background-image: url(../images/filetypes/xml_gray.png); } -tr.gray .icon-file.text-css { background-image: url(../images/filetypes/css_gray.png); } -tr.gray .icon-file.text-html { background-image: url(../images/filetypes/html_gray.png); } -tr.gray .icon-file.image-gif { background-image: url(../images/filetypes/image_gray.png); } -tr.gray .icon-file.image-jpeg { background-image: url(../images/filetypes/image_gray.png); } -tr.gray .icon-file.image-png { background-image: url(../images/filetypes/image_gray.png); } -tr.gray .icon-file.image-tiff { background-image: url(../images/filetypes/image_gray.png); } -tr.gray .icon-file.application-pdf { background-image: url(../images/filetypes/pdf_gray.png); } -tr.gray .icon-file.application-zip { background-image: url(../images/filetypes/zip_gray.png); } -tr.gray .icon-file.application-x-gzip { background-image: url(../images/filetypes/zip_gray.png); } - -/* Links */ -.gray { color: #AAA } -.gray a, .gray a:link, .gray a:visited{ color: #484848; text-decoration: none; } - -label.required:after { - content: " *"; - color: #bb0000; -} - -/* Search results */ -dt.dmsf-file { background-image: url(../../../images/document.png); } -dt.dmsf-folder { background-image: url(../../../images/folder.png); } - -.select2-selection{height: 100% !important; min-height: 30px;} -.select2-selection__rendered{overflow: visible !important; min-height: 30px;} -.select2-results__options {max-height: 600px !important;} \ No newline at end of file diff --git a/assets/stylesheets/img/loading.gif b/assets/stylesheets/images/loading.gif similarity index 100% rename from assets/stylesheets/img/loading.gif rename to assets/stylesheets/images/loading.gif diff --git a/assets/stylesheets/img/plupload.png b/assets/stylesheets/images/plupload.png similarity index 100% rename from assets/stylesheets/img/plupload.png rename to assets/stylesheets/images/plupload.png diff --git a/assets/stylesheets/plupload/jquery.ui.plupload.css b/assets/stylesheets/plupload/jquery.ui.plupload.css index e46a3f17..3b9cd1bd 100644 --- a/assets/stylesheets/plupload/jquery.ui.plupload.css +++ b/assets/stylesheets/plupload/jquery.ui.plupload.css @@ -43,7 +43,7 @@ .plupload_logo { width: 40px; height: 40px; - background: url('../img/plupload.png') no-repeat 0 0; + background: url('../images/plupload.png') no-repeat 0 0; position: absolute; top: 8px; left: 8px; @@ -119,7 +119,7 @@ } .plupload_thumb_loading { - background: #eee url(../img/loading.gif) center no-repeat; + background: #eee url(../images/loading.gif) center no-repeat; } .plupload_thumb_loading .plupload_file_dummy, diff --git a/assets/stylesheets/redmine_dmsf.css b/assets/stylesheets/redmine_dmsf.css new file mode 100644 index 00000000..38dbb2e8 --- /dev/null +++ b/assets/stylesheets/redmine_dmsf.css @@ -0,0 +1,248 @@ +/* +* Redmine plugin for Document Management System "Features" +* +* Copyright (C) 2011 Vít Jonáš +* Copyright (C) 2011-16 Karel Pičman +* +* 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. +*/ + +/* DMSF table.list modifications */ +table.dmsf_list th.dmsf_th { + border: none; +} + +table.list td.dmsf_modified { + min-width: 127px; + width: 127px; + font-size: 0.8em; + text-align: left; +} + +td.dmsf_modified img { + vertical-align:text-top; +} + +table.list td.dmsf_title { + width: 40%; + text-align: left; +} + +table.list td.dmsf_buttons { + min-width: 96px; + width: 96px; + text-align: left; +} + +table.list th.dmsf_checkbox, +table.list td.dmsf_checkbox { + width: 15px; + padding: 2px 0 0 0; +} + +table.list th.dmsf_checkbox input { + padding:0px; +} + +table.list th.dmsf_checkbox div.DataTables_sort_wrapper { + padding: 0; +} + +form.dmsf_entries { + margin-bottom: 10px; + display: block; +} + +div.dmsf_controls, +div.dmsf_controls input, +div.dmsf_controls select, +form.dmsf_entries #browser_filter.dataTables_filter, +form.dmsf_entries #browser_filter.dataTables_filter input{ + font-size: 0.9em; +} + +div.dmsf_filename { + padding: 0 10px 0 10px; + float: right; + font-size: 0.8em; + white-space: nowrap; +} + +td.dmsf_size { + font-size: 0.8em; + white-space: nowrap; +} + +td.dmsf_author { + font-size: 0.8em; + white-space: nowrap; +} + +td.dmsf_version { + font-size: 0.8em; + white-space: nowrap; +} + +td.dmsf_version img { + vertical-align:text-top; +} + +td.dmsf_workflow { + font-size: 0.8em; + white-space: nowrap; +} + +.dmsf_invisible { + display: none; +} + +div.dmsf_upload_select { + float: right; + font-size: 0.9em; +} + +div.dmsf_upload_select input, div.dmsf_upload_select select { + font-size: 0.9em; +} + +/* Approval workflow */ +#admin-menu a.dmsf-approvalworkflows { background-image: url(../../../images/ticket_go.png); } +#dmsf_users_for_delegate { height: 200px; overflow:auto; } +#dmsf_users_for_delegate label { display: block; } +tr.dmsf_workflow.locked a { color: #aaa; } + +.dmsf_revision_box { + padding: 0px 0px 0px 0px; + margin-bottom: 10px; + background-color:#f6f6f6; + color:#505050; + line-height:1.5em; +} + +.dmsf_revision_inner_box { + border: 1px solid #e4e4e4; +} + +.dmsf_id_box { + float: right; + white-space: nowrap; + line-height: 1.5em; + color: #505050; + margin-top: 5px; + padding-left: 10px; +} + +div.dmsf_revision_box .ui-widget-header { + font-weight: normal; +} + +.dmsf_log_header_box{ + padding:6px; + margin-bottom: 10px; +} +.dmsf_log_header_left { + width: 50%; + float: left; +} + +.dmsf_log_header_box label{ + font-weight: bold; + margin-left: 0px; + margin-right: 3px; + padding: 3px 0 3px 0; +} + +/* Command icons */ +.dmsf_icon-link { background-image: url(../images/link.png); } +.dmsf_icon-notification-on { background-image: url(../images/notify.png); margin-left: 3px; } +.dmsf_icon-notification-off { background-image: url(../images/notifynot.png); margin-left: 3px; } +.dmsf_icon-lock { background-image: url(../images/lock.png); } +.dmsf_icon-unlock { background-image: url(../images/unlock.png); } + +/* File types */ +tr.dmsf_gray .icon-folder { background-image: url(../images/folder_gray.png); } + +.icon-file.filetype-doc, .icon-file.filetype-docx { background-image: url(../images/filetypes/doc.png); } +.icon-file.filetype-xls, .icon-file.filetype-xlsx { background-image: url(../images/filetypes/xls.png); } +.icon-file.filetype-ppt, .icon-file.filetype-pptx { background-image: url(../images/filetypes/ppt.png); } +.icon-file.filetype-vsd, .icon-file.filetype-vsdx { background-image: url(../images/filetypes/vsd.png); } +.icon-file.filetype-mpp { background-image: url(../images/filetypes/mpp.png); } +.icon-file.filetype-odt { background-image: url(../images/filetypes/odt.png); } +.icon-file.filetype-ods { background-image: url(../images/filetypes/ods.png); } +.icon-file.filetype-odp { background-image: url(../images/filetypes/odp.png); } +.icon-file.filetype-odg { background-image: url(../images/filetypes/odg.png); } + +tr.dmsf_gray .icon-file.filetype-doc { background-image: url(../images/filetypes/doc_gray.png); } +tr.dmsf_gray .icon-file.filetype-docx { background-image: url(../images/filetypes/doc_gray.png); } +tr.dmsf_gray .icon-file.filetype-xls { background-image: url(../images/filetypes/xls_gray.png); } +tr.dmsf_gray .icon-file.filetype-xlsx { background-image: url(../images/filetypes/xls_gray.png); } +tr.dmsf_gray .icon-file.filetype-ppt { background-image: url(../images/filetypes/ppt_gray.png); } +tr.dmsf_gray .icon-file.filetype-pptx { background-image: url(../images/filetypes/ppt_gray.png); } +tr.dmsf_gray .icon-file.filetype-vsd { background-image: url(../images/filetypes/vsd_gray.png); } +tr.dmsf_gray .icon-file.filetype-vsdx { background-image: url(../images/filetypes/vsd_gray.png); } +tr.dmsf_gray .icon-file.filetype-mpp { background-image: url(../images/filetypes/mpp_gray.png); } +tr.dmsf_gray .icon-file.filetype-odt { background-image: url(../images/filetypes/odt_gray.png); } +tr.dmsf_gray .icon-file.filetype-ods { background-image: url(../images/filetypes/ods_gray.png); } +tr.dmsf_gray .icon-file.filetype-odp { background-image: url(../images/filetypes/odp_gray.png); } +tr.dmsf_gray .icon-file.filetype-odg { background-image: url(../images/filetypes/odg_gray.png); } + +tr.dmsf_gray .icon-file.text-x-c { background-image: url(../images/filetypes/c_gray.png); } +tr.dmsf_gray .icon-file.text-x-csharp { background-image: url(../images/filetypes/csharp_gray.png); } +tr.dmsf_gray .icon-file.text-x-java { background-image: url(../images/files/filetypes/java_gray.png); } +tr.dmsf_gray .icon-file.text-x-javascript { background-image: url(../images/filetypes/js_gray.png); } +tr.dmsf_gray .icon-file.text-x-php { background-image: url(../images/filetypes/php_gray.png); } +tr.dmsf_gray .icon-file.text-x-ruby { background-image: url(../images/filetypes/ruby_gray.png); } +tr.dmsf_gray .icon-file.text-xml { background-image: url(../images/filetypes/xml_gray.png); } +tr.dmsf_gray .icon-file.text-css { background-image: url(../images/filetypes/css_gray.png); } +tr.dmsf_gray .icon-file.text-html { background-image: url(../images/filetypes/html_gray.png); } +tr.dmsf_gray .icon-file.image-gif { background-image: url(../images/filetypes/image_gray.png); } +tr.dmsf_gray .icon-file.image-jpeg { background-image: url(../images/filetypes/image_gray.png); } +tr.dmsf_gray .icon-file.image-png { background-image: url(../images/filetypes/image_gray.png); } +tr.dmsf_gray .icon-file.image-tiff { background-image: url(../images/filetypes/image_gray.png); } +tr.dmsf_gray .icon-file.application-pdf { background-image: url(../images/filetypes/pdf_gray.png); } +tr.dmsf_gray .icon-file.application-zip { background-image: url(../images/filetypes/zip_gray.png); } +tr.dmsf_gray .icon-file.application-x-gzip { background-image: url(../images/filetypes/zip_gray.png); } + +/* Links */ +.dmsf_gray { color: #AAA } +.dmsf_gray a, .dmsf_gray a:link, .dmsf_gray a:visited{ color: #484848; text-decoration: none; } + +/* Search results */ +dt.dmsf-file { background-image: url(../../../images/document.png); } +dt.dmsf-folder { background-image: url(../../../images/folder.png); } + +/* DMSF tree view */ +tr.dmsf_hidden { display:none; } +tr.dmsf_tree span.dmsf_expander { cursor: pointer; } +tr.dmsf_tree.dmsf_expanded td.dmsf_title span { + background: url(../images/bullet_arrow_down.png) no-repeat 0 50%; + padding-left: 16px; +} +tr.dmsf_tree.dmsf_child td.dmsf_title span { padding-left: 16px; } +tr.dmsf_tree.dmsf_collapsed td.dmsf_title span { + background: url(../../../images/bullet_arrow_right.png) no-repeat 0 50%; + padding-left: 16px; +} +tr.dmsf_tree.idnt-1 td.dmsf_title {padding-left: 1.5em;} +tr.dmsf_tree.idnt-2 td.dmsf_title {padding-left: 2em;} +tr.dmsf_tree.idnt-3 td.dmsf_title {padding-left: 2.5em;} +tr.dmsf_tree.idnt-4 td.dmsf_title {padding-left: 3em;} +tr.dmsf_tree.idnt-5 td.dmsf_title {padding-left: 3.5em;} +tr.dmsf_tree.idnt-6 td.dmsf_title {padding-left: 4em;} +tr.dmsf_tree.idnt-7 td.dmsf_title {padding-left: 4.5em;} +tr.dmsf_tree.idnt-8 td.dmsf_title {padding-left: 5em;} +tr.dmsf_tree.idnt-9 td.dmsf_title {padding-left: 5.5em;} +.dmsf_odd {background-color:#f6f7f8;} +.dmsf_even {background-color: #fff;} \ No newline at end of file diff --git a/assets/stylesheets/select2.min.css b/assets/stylesheets/select2.min.css index c3a0fe95..a170ecdd 100644 --- a/assets/stylesheets/select2.min.css +++ b/assets/stylesheets/select2.min.css @@ -1 +1 @@ -.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle;}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none;}.select2-container .select2-selection--single .select2-selection__rendered{display:block;overflow:hidden;padding-left:8px;padding-right:20px;text-overflow:ellipsis;}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px;}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none;}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;}.select2-container .select2-search--inline{float:left;}.select2-container .select2-search--inline .select2-search__field{border:none;font-size:100%;margin-top:5px;}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051;}.select2-results{display:block;}.select2-results__options{list-style:none;margin:0;padding:0;}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none;}.select2-results__option[aria-selected]{cursor:pointer;}.select2-container--open .select2-dropdown{left:0;}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0;}.select2-search--dropdown{display:block;padding:4px;}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box;}.select2-search--dropdown.select2-search--hide{display:none;}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0);}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px;}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px;}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999;}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px;}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0;}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left;}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto;}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default;}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none;}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px;}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;}.select2-container--default .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px;width:100%;}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left;}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px;}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px;}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px;}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333;}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder{float:right;}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto;}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto;}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default;}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none;}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0;}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0;}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa;}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto;}.select2-container--default .select2-results__option[role=group]{padding:0;}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999;}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd;}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em;}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white;}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px;}.select2-container--classic .select2-selection--single{background-color:#f6f6f6;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #ffffff 50%, #eeeeee 100%);background-image:-o-linear-gradient(top, #ffffff 50%, #eeeeee 100%);background-image:linear-gradient(to bottom, #ffffff 50%, #eeeeee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb;}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px;}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px;}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999;}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);background-image:-o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);background-image:linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#cccccc', GradientType=0);}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0;}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left;}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto;}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb;}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none;}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px;}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #ffffff 0%, #eeeeee 50%);background-image:-o-linear-gradient(top, #ffffff 0%, #eeeeee 50%);background-image:linear-gradient(to bottom, #ffffff 0%, #eeeeee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eeeeee 50%, #ffffff 100%);background-image:-o-linear-gradient(top, #eeeeee 50%, #ffffff 100%);background-image:linear-gradient(to bottom, #eeeeee 50%, #ffffff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0;}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb;}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px;}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none;}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px;}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px;}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555;}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right;}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto;}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto;}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb;}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0;}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0;}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;}.select2-container--classic .select2-dropdown{background-color:white;border:1px solid transparent;}.select2-container--classic .select2-dropdown--above{border-bottom:none;}.select2-container--classic .select2-dropdown--below{border-top:none;}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto;}.select2-container--classic .select2-results__option[role=group]{padding:0;}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey;}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:white;}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px;}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb;} \ No newline at end of file +.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;height:1px !important;margin:-1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline{float:right}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb} diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 5b2017c3..fdfed8fe 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -1,9 +1,9 @@ # encoding: utf-8 -# +# # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # # This program is free software; you can redistribute it and/or @@ -26,22 +26,22 @@ cs: label_dmsf_file_revision_plural: Revize dokumentů label_dmsf_file_revision_access_plural: Přístupy k dokumentům warning_no_entries_selected: Není nic vybráno - error_email_to_must_be_entered: Musí být zadán adresát + error_email_to_must_be_entered: Musí být zadán příjemce warning_file_already_locked: Soubor je již zamčen notice_file_locked: Soubor byl zamčen warning_file_not_locked: Soubor není zamčen notice_file_unlocked: Soubor byl odemčen - error_only_user_that_locked_file_can_unlock_it: Soubor může být odemčen pouze uživatelem, který ho zamkl - error_max_files_exceeded: "Limit pro %{number} najednou stažených souborů je překročen" + error_only_user_that_locked_file_can_unlock_it: Soubor může být odemčen pouze uživatelem, který ho zamkl + error_max_files_exceeded: "Limit pro %{number} najednou stažených souborů je překročen" error_entry_project_does_not_match_current_project: Zadaný projekt neodpovídá aktuálnímu projektu - notice_folder_created: Adresář byl vytvořen + notice_folder_created: Složka byla vytvořena error_folder_creation_failed: Vytváření složky selhalo error_folder_title_must_be_entered: Musí být zadán název - notice_folder_deleted: Adresář byl smazán - error_folder_is_not_empty: Adresář není prázdný + notice_folder_deleted: Složka byla smazána + error_folder_is_not_empty: Složka není prázdná error_folder_title_is_already_used: Název již existuje notice_folder_details_were_saved: Detaily složky byly uloženy - error_folder_is_locked: Adresář je zamčen + error_folder_is_locked: Složka je zamčena error_file_is_locked: Soubor je zamčen notice_file_deleted: Soubor byl smazán error_at_least_one_revision_must_be_present: Musí existovat alespoň jedna revize @@ -81,8 +81,8 @@ cs: 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 - title_lock_file: Zamknout a zabránit změnám ostatních uživatelů - title_download_checked: Stáhnout vybrané jako Zip + title_lock_file: Zamknout a zabránit změnám ostatních uživatelů + title_download_checked: Stáhnout vybrané jako Zip title_send_checked_by_email: Zaslat vybrané emailem link_user_preferences: Vaše nastavení heading_send_documents_by_email: Odeslat dokumenty emailem @@ -94,11 +94,7 @@ cs: label_email_body: Obsah label_email_send: Odesláno title_notifications_active: Notifikace jsou aktivní - label_file_size: Velikost souboru - heading_file_upload: Nahrát - note_uploaded_maximum_files_at_once: "Maximálně může být nahráno %{number} souborů najednou." - note_upload_files_greater_than_two_gb: Nahrání souborů větších než 2GB vyžaduje 64b prohlížeč. - submit_upload_files: Nahrání + label_upload_upload: Nahrát heading_new_folder: Nová složka label_title: Název label_description: Popis @@ -109,7 +105,7 @@ cs: select_option_deactivated: Deaktivováno select_option_activated: Aktivováno label_title_format: Formát názvu - text_title_format: "Formát názvu souboru pro stažení (%t - název, %d - datum, %v - verze, %i - ID, %r - revize). Např.: %t_%v" + text_title_format: "Formát názvu souboru pro stažení (%t - název, %f - soubor, %d - datum, %v - verze, %i - ID, %r - revize). Např.: %t_%v" title_save_preferences: Uložit nastavení heading_revisions: Revize title_download: Stáhnout @@ -117,9 +113,9 @@ cs: label_created: Vytvořeno label_changed: Změněno info_changed_by_user: "%{changed} uživatelem" - label_filename: Jméno souboru + label_filename: Jméno souboru label_mime: Typ - label_size: Velikost + label_size: Velikost heading_new_revision: Nová revize option_version_same: Stejná option_version_minor: Podružná @@ -139,7 +135,7 @@ cs: option_stem_none: Stem nic (výchozí) option_stem_some: Stem něco option_stem_all: Stem vše - label_stemming_description: Tímto je určeno, jak analyzátor dotazu aplikuje algoritmus Stemmingu. Výchozí hodnota je STEM_NONE. Dostupné hodnoty jsou + label_stemming_description: Tímto je určeno, jak analyzátor dotazu aplikuje algoritmus Stemmingu. Výchozí hodnota je STEM_NONE. Dostupné hodnoty jsou note_do_not_stem: Žádný stemming. note_stem_some: "Hledej stemmed formy výrazů kromě těch, které začínají velkým písmenem nebo jsou následovány určitými znaky nebo jsou použity s operátory, které vyžadují informaci o pozici. Stemmed výrazy začínají písmenem 'Z'." note_stem_all: "Hledej stemmed formy všech slov (poznámka: 'Z' prefix není přidán)." @@ -154,7 +150,7 @@ cs: permission_user_preferences: Nastavení uživatele permission_view_dmsf_files: Zobrazit dokumenty permission_folder_manipulation: Manipulace se složkami - permission_file_manipulation: Manipulace se soubory + permission_file_manipulation: Manipulace se soubory permission_force_file_unlock: Vynucené odemknutí souboru permission_manage_workflows: Spravovat schvalovací procesy permission_file_delete: Mazat dokumenty @@ -167,12 +163,12 @@ cs: error_user_has_not_right_delete_folder: Uživatel nemá právo mazat složky error_user_has_not_right_delete_file: Uživatel nemá právo mazat soubor notice_entries_deleted: Položky smazány - warning_some_entries_were_not_deleted: "Některé položky nebyly smazány: %{entries}" + warning_some_entries_were_not_deleted: "Některé položky nebyly smazány: %{entries}" title_delete_checked: Smaž vybrané title_items: položek title_filename_for_download: Název Zip archivu ke stažení label_number_of_folders: Složky - label_number_of_documents: Dokumenty + label_number_of_documents: Dokumenty error_file_storage_directory_does_not_exist: Cílová složka neexistuje a nemůže být vytvořena error_file_can_not_be_created: Nelze vytvořit soubor v cílové složce error_wrong_zip_encoding: Chybné kódování Zipu @@ -185,31 +181,31 @@ cs: heading_access_last: Poslední label_dmsf_updated: Změněno label_dmsf_downloaded: Staženo - title_total_size_of_all_files: Celková velikost všech souborů v adresáři + title_total_size_of_all_files: Celková velikost všech souborů ve složce project_module_dmsf: DMSF warning_no_project_to_copy_file_to: Neexistuje projekt, do kterého můžete kopírovat comment_copied_from: "Zkopírováno z %{source}" notice_file_copied: Soubor zkopírován notice_file_moved: Soubor přesunut field_target_project: Cílový projekt - field_target_folder: Cílový adresář + field_target_folder: Cílová složka title_copy_or_move: Kopírovat/Přesunout label_dmsf_folder_plural: Složky comment_moved_from: "Přesunuto z %{source}" - error_target_folder_same: Cílový adresář a projekt jsou stejné jako aktuální + error_target_folder_same: Cílový složka a projekt jsou stejné jako aktuální error_file_cannot_be_moved: Soubor nemůže být přesunut error_file_cannot_be_copied: Soubor nemůže být zkopírován warning_no_project_to_copy_folder_to: Neexistuje projekt, do kterého můžete kopírovat title_copy: Kopírovat error_folder_cannot_be_copied: Složka nemůže být zkopírována notice_folder_copied: Složka zkopírována - - error_max_email_filesize_exceeded: "Přesáhli jste maximální velikost souboru, který lze poslat emailem. (%{number} MB)" + + error_max_email_filesize_exceeded: "Přesáhli jste maximální velikost souboru, který lze poslat emailem. (%{number} MB)" note_maximum_email_filesize: Omezí se maximální velikost souboru, který může být poslán emailem. 0 znamená neomezený. Číslo je v MB. label_maximum_email_filesize: Maximální velikost souboru emailu header_minimum_filesize: Chyba souboru. error_minimum_filesize: "Soubor %{file} má nulovou velikost a nebude přiložen." - parent_directory: Nadřazený adresář + parent_directory: Nadřazená složka note_webdav: "Webdav je po aktivaci k dispozici na %{protocol}://%{domain}/dmsf/webdav/[identifikátor projektu]" label_webdav: Webdav functionalita label_dmsf_plural: "Kopíruj dokumenty a složky (%{files} souborů v %{folders} složkách)" @@ -219,7 +215,6 @@ cs: warning_folder_not_locked: Složku nelze zamknout notice_folder_unlocked: Složka byla odemčena error_only_user_that_locked_folder_can_unlock_it: Nemáte oprávnění k odemknutí této složky - title_folder_parent_locked: "Nadřazená složka %{name} je zamčená" title_unlock_folder: Odemknout title_lock_folder: Zamknout @@ -227,7 +222,7 @@ cs: select_option_webdav_readwrite: Čtení/Zápis label_webdav_strategy: Webdav strategie note_webdav_strategy: Umožní administrátorovi rozhodnout, zdali je webdav pouze pro čtení nebo i pro zápis. - + error_unable_delete_dmsf_workflow: Nelze smazat schvalovací proces error_empty_note: Musí být vyplněn komentář error_workflow_assign: Chyba při přiřazování @@ -268,25 +263,25 @@ cs: info_revision: "r%{rev}" link_workflow: Schvalovací proces notice_workflow_started: Schvalovací proces byl úspěšně zahájen - text_email_subject_approved: "Schvalovací proces %{name} schválen" - text_email_subject_rejected: "Schvalovací proces %{name} zamítnut" - text_email_subject_delegated: "Schvalovací proces %{name} delegován" - text_email_subject_requires_approval: "Schvalovací proces %{name} očekává Vaše schválení" - text_email_subject_updated: "Schvalovací proces %{name} aktualizován" - text_email_subject_started: "Schvalovací proces %{name} spuštěn" + text_email_subject_approved: schválen + text_email_subject_rejected: zamítnut + text_email_subject_delegated: delegován + text_email_subject_requires_approval: očekává Vaše schválení + text_email_subject_updated: aktualizován + text_email_subject_started: spuštěn text_email_finished_approved: "Schvalovací proces '%{name}' přiřazený k dokumentu '%{filename}' byl právě ukončen a dokument je schválen." text_email_finished_rejected: "Schvalovací proces '%{name}' přiřazený k dokumentu '%{filename}' byl dokončen a dokument byl zamítnut, protože '%{notice}'." text_email_finished_delegated: "Schvalovací proces '%{name}' přiřazený k dokumentu '%{filename}' byl delegován, protože '%{notice}' a od Vás se očekává schválení v aktuálním schvalovacím kroku." text_email_finished_step: "Schvalovací proces '%{name}' přiřazený k dokumentu '%{filename}' právě ukončil jeden ze schvalovacích kroků a od Vás se očekává schválení v dalším schvalovacím kroku." text_email_finished_step_short: "Schvalovací proces '%{name}' přiřazený k dokumentu '%{filename}' právě ukončil jeden ze schvalovacích kroků." - text_email_started: "Schvalovací proces '%{name}' přiřazený k dokumentu '%{filename}' byl zahájen a od Vás se očekává schválení v aktuálním schvalovacím kroku." + text_email_started: "Schvalovací proces '%{name}' přiřazený k dokumentu '%{filename}' byl zahájen a od Vás se očekává schválení v aktuálním schvalovacím kroku." text_email_to_proceed: Pro schválení klikněte na zaškrtávací ikonku vedle dokumentu v text_email_to_see_history: Pro zobrazení historie schvalovacího procesu klikněte na status dokumentu v text_email_to_see_status: Pro zobrazení aktuálního stavu schvalovacího procesu klikněte na status dokumentu v label_my_open_approvals: My open approvals label_my_locked_documents: My locked documents - - title_create_link: Vytvořit symbolický odkaz + + title_create_link: Vytvořit symbolický odkaz label_link_from: Odkaz z label_link_to: Odkaz do label_notifications_on: Zapnout notifikace @@ -294,43 +289,46 @@ cs: field_target_file: Zdrojový soubor title_download_entries: Historie stahování label_external: Externí - + label_link_name: Název odkazu label_link_external_url: URL - label_target_folder: Cílový adresář - label_source_folder: Zdrojový adresář + label_target_folder: Cílová složka + label_source_folder: Zdrojová složka label_target_project: Cílový projekt - label_source_project: Zdrojový projekt - - text_email_doc_updated_subject: "Dokumenty projektu %{project} aktualizovány" + label_source_project: Zdrojový projekt + + text_email_doc_updated_subject: Dokumenty aktualizovány text_email_doc_updated: právě aktualizoval dokumenty projektu text_email_doc_follows: takto - text_email_doc_deleted_subject: "Dokumenty projektu %{project} smazány" + text_email_doc_deleted_subject: Dokumenty smazány text_email_doc_deleted: právě smazal dokumety projektu - label_links_only: pouze odkazy - + label_links_only: pouze odkazy + label_display_notified_recipients: Zobrazit příjemce notifikací note_display_notified_recipients: Uživatel bude informován o příjemcích právě odeslané emailové notifikace. warning_email_notifications: "Notifikační email poslán na uživatele %{to}" - + link_trash_bin: Koš title_restore: Obnovit notice_dmsf_file_restored: Document byl úspěšně obnoven - notice_dmsf_folder_restored: Adresář byl úspěšně obnoven + notice_dmsf_folder_restored: Složka byla úspěšně obnovena notice_dmsf_link_restored: Odkaz byl úspěšně obnoven title_restore_checked: Obnov vybrané - error_parent_folder: Nadřazený adresář neexistuje - + error_parent_folder: Nadřazená složka neexistuje + error_resource_or_parent_locked: Nelze zamknout - zdrojový nebo nadřazený objekt je zamčený error_parent_locked: Nelze zamknout - nadřazený objekt je zamčený error_resource_locked: Nelze zamknout - zdrojový objekt je zamčený - error_lock_exclusively: Nelze zamknout již zamčený objekt - error_unlock_parent_locked: Nelze odemknout - nadřazený objekt je zamčený - - my: - blocks: - locked_documents: Zamčené dokumenty - open_approvals: Procesy ke schválení - + error_lock_exclusively: Nelze zamknout již zamčený objekt + error_unlock_parent_locked: Nelze odemknout - nadřazený objekt je zamčený + + field_dmsf_tree_view: Zobrazit složky jako stromovou strukturu + label_dmsf_version: Verze + + locked_documents: Zamčené dokumenty + open_approvals: Procesy ke schválení + label_maximum_ajax_upload_filesize: Maximální velikost souboru nahratelná přes AJAX - note_maximum_ajax_upload_filesize: Omezuje velikost souboru, který může být nahrán přes standardní rozhraní AJAX, jinak se použije standarní rozhraní Redminu. Číslo je MB. \ No newline at end of file + note_maximum_ajax_upload_filesize: Omezuje velikost souboru, který může být nahrán přes standardní rozhraní AJAX, jinak se použije standardní rozhraní Redminu. Číslo je v MB. + label_classic: Klasický + label_drag_drop: "Drag&Drop" diff --git a/config/locales/de.yml b/config/locales/de.yml index 8463641b..7a13db07 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1,10 +1,10 @@ # encoding: utf-8 -# +# # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Terrence Miller # Copyright (C) 2013 Christian Wetting -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,13 +26,13 @@ de: label_dmsf_file_revision_plural: Dokumenteversion label_dmsf_file_revision_access_plural: Dokumentezugriffe warning_no_entries_selected: Keine Einträge ausgewählt - error_email_to_must_be_entered: Es muss ein Email-Empfänger angegeben werden. + error_email_to_must_be_entered: Es muss ein Email-Empfänger angegeben werden. warning_file_already_locked: Datei schon gesperrt notice_file_locked: Datei gesperrt warning_file_not_locked: Datei nicht gesperrt notice_file_unlocked: Datei freigegeben - error_only_user_that_locked_file_can_unlock_it: Nur der Benutzer, der die Datei gesperrt hat, kann sie auch wieder freigeben - error_max_files_exceeded: "Grenze für %{number} gleichzeitig heruntergeladene Dateien überschritten" + error_only_user_that_locked_file_can_unlock_it: Nur der Benutzer, der die Datei gesperrt hat, kann sie auch wieder freigeben + error_max_files_exceeded: "Grenze für %{number} gleichzeitig heruntergeladene Dateien überschritten" error_entry_project_does_not_match_current_project: Eingangsprojekt entspricht nicht aktuellem Projekt notice_folder_created: Ordner erstellt error_folder_creation_failed: Ordnererstellung fehlgeschlagen @@ -40,7 +40,7 @@ de: notice_folder_deleted: Ordner gelöscht error_folder_is_not_empty: Ordner ist nicht leer error_folder_title_is_already_used: Titel wird schon benutzt. Denk dir was Neues aus. - notice_folder_details_were_saved: Ordnerdetails wurden gespeichert + notice_folder_details_were_saved: Ordnerdetails wurden gespeichert error_folder_is_locked: Ordner ist gesperrt error_file_is_locked: Datei ist gesperrt notice_file_deleted: Datei gelöscht @@ -81,8 +81,8 @@ de: title_waiting_for_approval: Warte auf Zustimmung title_approved: Zugestimmt title_unlock_file: Hebe Sperre auf um Änderungen anderer Nutzer zu ermöglichen - title_lock_file: Sperre um Änderungen anderer Nutzer zu verhindern - title_download_checked: Download der ausgewählten Dateien in einem ZIP-Archiv + title_lock_file: Sperre um Änderungen anderer Nutzer zu verhindern + title_download_checked: Download der ausgewählten Dateien in einem ZIP-Archiv title_send_checked_by_email: Sende gewählte Dateien per Email link_user_preferences: Deine DMS Projekt Einstellungen heading_send_documents_by_email: Sende Dateien per Email @@ -94,11 +94,7 @@ de: label_email_body: Text label_email_send: Senden title_notifications_active: Benachrichtigungen sind aktiv - label_file_size: Dateigröße - heading_file_upload: Upload - note_uploaded_maximum_files_at_once: "Es können maximal %{number} Dateien auf einmal hochgeladen werden." - note_upload_files_greater_than_two_gb: Um Dateien größer 2GB hochzuladen, brauchst du einen 64bit-Browser. - submit_upload_files: Upload + label_upload: Upload heading_new_folder: Neuer Ordner label_title: Titel label_description: Beschreibung @@ -109,7 +105,7 @@ de: select_option_deactivated: Aus select_option_activated: Ein label_title_format: Title format - text_title_format: "Format des Dokumente-Titels für Speichern (%t - Titel, %d - Datum, %v - Version, %i - ID, %r - Revision). z.B.: %t_%v" + text_title_format: "Format des Dokumente-Titels für Speichern (%t - Titel, %f - Datei, %d - Datum, %v - Version, %i - ID, %r - Revision). z.B.: %t_%v" title_save_preferences: Einstellungen speichern heading_revisions: Versionen title_download: Download @@ -117,9 +113,9 @@ de: label_created: Erstellt label_changed: Geändert info_changed_by_user: "%{changed} von" - label_filename: Dateiname + label_filename: Dateiname label_mime: Mime - label_size: Größe + label_size: Größe heading_new_revision: Neue Version option_version_same: gleiche Version option_version_minor: Unterversion @@ -154,8 +150,8 @@ de: permission_user_preferences: Benutzereinstellungen permission_view_dmsf_files: Betrachte Dateien permission_folder_manipulation: Ordner bearbeiten - permission_file_manipulation: Dateien bearbeiten - permission_force_file_unlock: Erzwinge Aufhebung der Dateisperre + permission_file_manipulation: Dateien bearbeiten + permission_force_file_unlock: Erzwinge Aufhebung der Dateisperre permission_manage_workflows: Workflows verwalten permission_file_delete: Datei löschen label_file: Datei @@ -167,12 +163,12 @@ de: error_user_has_not_right_delete_folder: Der Nutzer hat kein Recht die Ordner zu löschen. error_user_has_not_right_delete_file: Der Nutzer hat kein Recht die Datei zu löschen. notice_entries_deleted: Einträge löschen - warning_some_entries_were_not_deleted: "Enige Einträge wurden nicht gelöscht: %{entries}" + warning_some_entries_were_not_deleted: "Enige Einträge wurden nicht gelöscht: %{entries}" title_delete_checked: Löschen ausgewählt title_items: items title_filename_for_download: Dateiname beim Herunterladen oder in ZIP-Archiv verwenden label_number_of_folders: Ordner - label_number_of_documents: Dokumente + label_number_of_documents: Dokumente error_file_storage_directory_does_not_exist: Der Dateiablageordner existiert nicht auf dem Server und kann nicht erstellt werden. error_file_can_not_be_created: Datei kann nicht in dem gewählten Ordner erstellt werden. error_wrong_zip_encoding: Falsche ZIP-Kodierung @@ -203,7 +199,7 @@ de: title_copy: Kopieren error_folder_cannot_be_copied: Der Ordner kann nicht kopiert werden. notice_folder_copied: Ordner kopiert - + error_max_email_filesize_exceeded: "Maximale Dateigröße der Anlage wurde überschritten. (%{number} MB)" note_maximum_email_filesize: Maximale Dateigröße der Anlage. 0 bedeutet keinen Limit. Angabe in MB. label_maximum_email_filesize: Maximale Dateigröße der Anlage @@ -213,21 +209,20 @@ de: note_webdav: "Nach der Aktivierung von WebDav kann der Dienst über die URL %{protocol}://%{domain}/dmsf/webdav/[project identifier] erreicht werden." label_webdav: Webdav Funktionalität label_dmsf_plural: "Kopieren von Dateien und Ordnern (%{files} Dateien in %{folders} Ordnern)" - + warning_folder_already_locked: Dieser Ordner ist bereits gesperrt notice_folder_locked: Der Ordner wurde erfolgreich gesperrt warning_folder_not_locked: Der Ordner konnte nicht gesperrt werden notice_folder_unlocked: Der Ordner wurde erfolgreich entsperrt error_only_user_that_locked_folder_can_unlock_it: Sie haben keine Berechtigung zur Entsperrung des Ordners - title_folder_parent_locked: "Übergeordnetes Verzeichnis %{name} ist gesperrt" title_unlock_folder: Ordner zur Bearbeitung durch andere Benutzer entsperren title_lock_folder: Ordner zum Schutz vor Bearbeitung durch andere Benutzer sperren - + select_option_webdav_readonly: nur Lesen select_option_webdav_readwrite: Lesen/Schreiben label_webdav_strategy: Webdav Strategie - note_webdav_strategy: Erlaubt dem Administrator den Wechsel der WebDav Nutzung zwischen nur lesend und auch schreibenden Zugriffen. - + note_webdav_strategy: Erlaubt dem Administrator den Wechsel der WebDav Nutzung zwischen nur lesend und auch schreibenden Zugriffen. + error_unable_delete_dmsf_workflow: Konnte den Workflow nicht löschen error_empty_note: Die Notiz darf nicht leer sein. error_workflow_assign: Es trat ein Fehler beim Zuweisen des Workflows auf @@ -251,12 +246,12 @@ de: label_dmsf_workflow_add_approver: "Füge einen neuen Genehmiger mit einer logischen Funktion hinzu:" label_or: oder label_action: Aktion - label_note: Notiz + label_note: Notiz title_none: Niemand title_rejection: Ablehnung title_delegation: Deligierung title_assignment: Zuweisung - title_start: Start + title_start: Start title_dmsf_workflow_log: Genehmigungs-Workflow Verlauf title_assigned: Zugewiesen title_approval: Genehmigt @@ -266,27 +261,27 @@ de: dmsf_new_step: Neuer Schritt message_dmsf_wokflow_note: Deine Notiz... info_revision: "r%{rev}" - link_workflow: Workflow + link_workflow: Workflow notice_workflow_started: Genehmigungs-Workflow gestartet - text_email_subject_approved: "Genehmigungs-Workflow %{name} genehmigt" - text_email_subject_rejected: "Genehmigungs-Workflow %{name} abgelehnt" - text_email_subject_delegated: "Genehmigungs-Workflow %{name} deligiert" - text_email_subject_requires_approval: "Genehmigungs-Workflow %{name} benötigt deine Genehmigung" - text_email_subject_updated: "Genehmigungs-Workflow %{name} bearbeitet" - text_email_subject_started: "Genehmigungs-Workflow %{name} gestartet" + text_email_subject_approved: genehmigt + text_email_subject_rejected: abgelehnt + text_email_subject_delegated: deligiert + text_email_subject_requires_approval: benötigt deine Genehmigung + text_email_subject_updated: bearbeitet + text_email_subject_started: gestartet text_email_finished_approved: "Der Genehmigungs-Workflow '%{name}' zugewiesen an die Datei '%{filename}' ist abgeschlossen und die Datei wurde genehmigt." text_email_finished_rejected: "Der Genehmigungs-Workflow '%{name}' zugewiesen an die Datei '%{filename}' ist abgeschlossen, aber die Datei wurde abgelehnt, weil: '%{notice}'." text_email_finished_delegated: "Der Genehmigungs-Workflow '%{name}' zugewiesen an die Datei '%{filename}' wurde an dich deligiert, weil: '%{notice}' und weil deine Zustimmung im aktuellen Genehmigungsschritt benötigt wird." text_email_finished_step: "Der Genehmigungs-Workflow '%{name}' zugewiesen an die Datei '%{filename}' hat grade einen Zustimmungsschritt abgeschlossen und im nächsten Genehmigungsschritt wird deine Zustimmung benötigt." text_email_finished_step_short: "Der Genehmigungs-Workflow '%{name}' zugewiesen an die Datei '%{filename}' hat grade einen Genehmigungsschritt abgeschlossen." - text_email_started: "Der Genehmigungs-Workflow '%{name}' zugewiesen an '%{filename}' wurde gestartet und im aktuellen Genehmigungsschritt wird deine Zustimmung benötigt." + text_email_started: "Der Genehmigungs-Workflow '%{name}' zugewiesen an '%{filename}' wurde gestartet und im aktuellen Genehmigungsschritt wird deine Zustimmung benötigt." text_email_to_proceed: Um fortzufahren klicke auf das Häckchen neben der Datei in text_email_to_see_history: Um den Verlauf des Genehmigungs-Workflows zu sehen klicke auf den Workflowstatus zur Datei in text_email_to_see_status: Um den aktuellen Status des Genehmigungs-Workflows zu sehen klicke auf den Workflowstatus zur Datei in label_my_open_approvals: Meine offenen Genehmigungen label_my_locked_documents: Meine geschlossenen Genehmigungen - - title_create_link: Verknüpfung anlegen + + title_create_link: Verknüpfung anlegen label_link_from: Verlinke von label_link_to: Verlinke zu label_notifications_on: Benachrichtigungen ein @@ -294,25 +289,25 @@ de: field_target_file: Quelldatei title_download_entries: Download entries label_external: Außen - + label_link_name: Name der Verknüpfung label_link_external_url: URL label_target_folder: Zielordner label_source_folder: Quellordner label_target_project: Zielprojekt - label_source_project: Quellprojekt - - text_email_doc_updated_subject: "Dokumente im Projekt %{project} wurden aktualisiert" + label_source_project: Quellprojekt + + text_email_doc_updated_subject: Dokumente wurden aktualisiert text_email_doc_updated: hat folgende Dokumente bearbeitet text_email_doc_follows: wie folgt - text_email_doc_deleted_subject: "Dokumente im Projekt %{project} wurden gelöscht" + text_email_doc_deleted_subject: Dokumente wurden gelöscht text_email_doc_deleted: hat folgende Dokumente gelöscht - label_links_only: nur Verknüpfungen - + label_links_only: nur Verknüpfungen + label_display_notified_recipients: Zeige benachrichtigte Empfänger note_display_notified_recipients: Der User wird über alle Empfänger der Emailbenachrichtigung informiert. warning_email_notifications: "Emailbenachrichtigung wurde gesendet an %{to}" - + link_trash_bin: Trash bin title_restore: Restore notice_dmsf_file_restored: The document has been successfully restored @@ -320,17 +315,20 @@ de: notice_dmsf_link_restored: The link has been successfully restored title_restore_checked: Restore checked error_parent_folder: "The parent folder doesn't exist" - + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked error_parent_locked: Unable to complete lock - resource parent is locked error_resource_locked: Unable to complete lock - resource is locked - error_lock_exclusively: unable to lock exclusively an already-locked resource - error_unlock_parent_locked: Unlock failed - resource parent is locked - - my: - blocks: - locked_documents: Gesperrte Dateien - open_approvals: Offene Genehmigungs-Workflows - + error_lock_exclusively: Unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked + + field_dmsf_tree_view: Navigate folders in a tree + label_dmsf_version: Version + + locked_documents: Gesperrte Dateien + open_approvals: Offene Genehmigungs-Workflows + label_maximum_ajax_upload_filesize: Maximale Dateigröße für den Upload via AJAX - note_maximum_ajax_upload_filesize: Maximale Dateigröße für den Upload über die AJAX-Schnittstelle. Für größere Dateien muss der Standard-Uploader von Redmine verwendet werden. Angabe in MB. \ No newline at end of file + note_maximum_ajax_upload_filesize: Maximale Dateigröße für den Upload über die AJAX-Schnittstelle. Für größere Dateien muss der Standard-Uploader von Redmine verwendet werden. Angabe in MB. + label_classic: Klassisch + label_drag_drop: "Drag&Drop" diff --git a/config/locales/en.yml b/config/locales/en.yml index fa7fde8d..9722dcc2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,10 +1,10 @@ # encoding: utf-8 -# +# # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,13 +26,13 @@ en: label_dmsf_file_revision_plural: Document revisions label_dmsf_file_revision_access_plural: Document accesses warning_no_entries_selected: No entries selected - error_email_to_must_be_entered: Email To must be entered + error_email_to_must_be_entered: Email To must be entered warning_file_already_locked: File already locked notice_file_locked: File locked warning_file_not_locked: File not locked notice_file_unlocked: File unlocked - error_only_user_that_locked_file_can_unlock_it: Only user that locked the file can unlock it - error_max_files_exceeded: "Limit for %{number} simultaneously downloaded files exceeded" + error_only_user_that_locked_file_can_unlock_it: Only user that locked the file can unlock it + error_max_files_exceeded: "Limit for %{number} simultaneously downloaded files exceeded" error_entry_project_does_not_match_current_project: "Entry project doesn't match current project" notice_folder_created: Folder created error_folder_creation_failed: Folder creation failed @@ -40,7 +40,7 @@ en: notice_folder_deleted: Folder deleted error_folder_is_not_empty: Folder is not empty error_folder_title_is_already_used: Title is already used - notice_folder_details_were_saved: Folder details were saved + notice_folder_details_were_saved: Folder details were saved error_folder_is_locked: Folder is locked error_file_is_locked: File is locked notice_file_deleted: File deleted @@ -81,8 +81,8 @@ en: title_waiting_for_approval: Waiting for Approval title_approved: Approved title_unlock_file: Unlock to allow changes for other members - title_lock_file: Lock to prevent changes for other members - title_download_checked: Download checked in Zip archive + title_lock_file: Lock to prevent changes for other members + title_download_checked: Download checked in Zip archive title_send_checked_by_email: Send checked by email link_user_preferences: Your DMSF project preferences heading_send_documents_by_email: Send documents by email @@ -94,11 +94,7 @@ en: label_email_body: Body label_email_send: Send title_notifications_active: Notifications active - label_file_size: File size - heading_file_upload: Upload - note_uploaded_maximum_files_at_once: "There can be uploaded maximum of %{number} files at once." - note_upload_files_greater_than_two_gb: To upload files greater than 2GB you must have 64b browser. - submit_upload_files: Upload + label_upload: Upload heading_new_folder: New Folder label_title: Title label_description: Description @@ -109,7 +105,7 @@ en: select_option_deactivated: Deactivated select_option_activated: Activated label_title_format: Title format - text_title_format: "Document title format for download (%t - title, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" + text_title_format: "Document title format for download (%t - title, %f - file, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" title_save_preferences: Save preferences heading_revisions: Revisions title_download: Download @@ -117,9 +113,9 @@ en: label_created: Created label_changed: Changed info_changed_by_user: "%{changed} by" - label_filename: Filename + label_filename: Filename label_mime: Mime - label_size: Size + label_size: Size heading_new_revision: New Revision option_version_same: Same option_version_minor: Minor @@ -154,7 +150,7 @@ en: permission_user_preferences: User preferences permission_view_dmsf_files: View documents permission_folder_manipulation: Folder manipulation - permission_file_manipulation: File manipulation + permission_file_manipulation: File manipulation permission_force_file_unlock: Force file unlock permission_manage_workflows: Manage workflows permission_file_delete: Delete documents @@ -167,12 +163,12 @@ en: error_user_has_not_right_delete_folder: "User hasn't right to delete forders" error_user_has_not_right_delete_file: "User hasn't right to delete file" notice_entries_deleted: Entries deleted - warning_some_entries_were_not_deleted: "Some entries weren't deleted: %{entries}" + warning_some_entries_were_not_deleted: "Some entries weren't deleted: %{entries}" title_delete_checked: Delete checked title_items: items title_filename_for_download: Filename used for download or in Zip archive label_number_of_folders: Folders - label_number_of_documents: Documents + label_number_of_documents: Documents error_file_storage_directory_does_not_exist: "File storage directory doesn't exist and can't be created" error_file_can_not_be_created: "File can't be created in storage directory" error_wrong_zip_encoding: Wrong Zip encoding @@ -203,7 +199,7 @@ en: title_copy: Copy error_folder_cannot_be_copied: "Folder can't be copied" notice_folder_copied: Folder copied - + error_max_email_filesize_exceeded: "You've exceeded the maximum filesize for sending via email. (%{number} MB)" note_maximum_email_filesize: Limits maximum filesize that can be sent via email. 0 means unlimited. Number is in MB. label_maximum_email_filesize: Maximum email attachment size @@ -219,21 +215,20 @@ en: warning_folder_not_locked: Unfortunately, the folder could not be locked notice_folder_unlocked: The folder was successfully unlocked error_only_user_that_locked_folder_can_unlock_it: You are not authorised to unlock this folder - title_folder_parent_locked: "Parent folder %{name} is locked" title_unlock_folder: Unlock to allow changes for other members title_lock_folder: Lock to prevent changes for other members select_option_webdav_readonly: Read-only select_option_webdav_readwrite: Read/Write label_webdav_strategy: Webdav strategy - note_webdav_strategy: Enables the administrator to decide if webdav is a read-only or read-write platform for end users. - + note_webdav_strategy: Enables the administrator to decide if webdav is a read-only or read-write platform for end users. + error_unable_delete_dmsf_workflow: Unable to delete the workflow error_empty_note: "The note can't be empty" error_workflow_assign: An error occured while assigning error_cannot_start_workflow: "Workflow can't be started" error_cannot_renumber_steps: "Steps can't be renumbered" - label_dmsf_workflow_new: New approval workflow + label_dmsf_workflow_new: New approval workflow field_label_dmsf_workflow: Approval Workflow field_label_dmsf_workflow_name: Approval workflow name label_dmsf_workflow_plural: Approval workflows @@ -251,42 +246,42 @@ en: label_dmsf_workflow_add_approver: "Add a new approver with a logical function:" label_or: or label_action: Action - label_note: Note + label_note: Note title_none: None title_rejection: Rejection title_delegation: Delegation title_assignment: Assignment - title_start: Start + title_start: Start title_dmsf_workflow_log: Approval Workflow Log title_assigned: Assigned title_approval: Approval title_rejected: Rejected dmsf_and: AND dmsf_or: OR - dmsf_new_step: New step + dmsf_new_step: New step message_dmsf_wokflow_note: Your note... info_revision: "r%{rev}" link_workflow: Workflow - notice_workflow_started: Approval workflow successfully started - text_email_subject_approved: "Approval workflow %{name} approved" - text_email_subject_rejected: "Approval workflow %{name} rejected" - text_email_subject_delegated: "Approval workflow %{name} delegated" - text_email_subject_requires_approval: "Approval workflow %{name} requires your approval" - text_email_subject_updated: "Approval workflow %{name} updated" - text_email_subject_started: "Approval workflow %{name} started" + notice_workflow_started: Approval workflow successfully started + text_email_subject_approved: approved + text_email_subject_rejected: rejected + text_email_subject_delegated: delegated + text_email_subject_requires_approval: requires your approval + text_email_subject_updated: updated + text_email_subject_started: started text_email_finished_approved: "The approval workflow '%{name}' assigned to '%{filename}' document has just been finished and the document has been approved." text_email_finished_rejected: "The approval workflow '%{name}' assigned to '%{filename}' document has just been finished and the document has been rejected because of '%{notice}'." text_email_finished_delegated: "The approval workflow '%{name}' assigned to '%{filename}' document has just been delegated because of '%{notice}' and you are expected to do an approval in the current approval step." text_email_finished_step: "The approval workflow '%{name}' assigned to '%{filename}' document has just finished one of the approval steps and you are expected to do an approval in the next approval step." text_email_finished_step_short: "The approval workflow '%{name}' assigned to '%{filename}' document has just finished one of the approval steps." - text_email_started: "The approval workflow '%{name}' assigned to '%{filename}' document has just been started and you are expected to do an approval in the current approval step." + text_email_started: "The approval workflow '%{name}' assigned to '%{filename}' document has just been started and you are expected to do an approval in the current approval step." text_email_to_proceed: To proceed click on the check box icon next to the document in text_email_to_see_history: To see the approval history click on the workflow status of the document in text_email_to_see_status: To see the current status of the approval workflow click on the workflow status the document in label_my_open_approvals: My open approvals label_my_locked_documents: My locked documents - - title_create_link: Create a symbolic link + + title_create_link: Create a symbolic link label_link_from: Link from label_link_to: Link to label_notifications_on: Notifications on @@ -301,18 +296,18 @@ en: label_source_folder: Source folder label_target_project: Target project label_source_project: Source project - - text_email_doc_updated_subject: "Documents of %{project} updated" + + text_email_doc_updated_subject: Documents updated text_email_doc_updated: has just actualized documents of text_email_doc_follows: as follows - text_email_doc_deleted_subject: "Documents of %{project} deleted" + text_email_doc_deleted_subject: Documents deleted text_email_doc_deleted: has just deleted documents of - label_links_only: links only - + label_links_only: links only + label_display_notified_recipients: Display notified recipients note_display_notified_recipients: The user will be informed about all recipients of just sent the email notification. warning_email_notifications: "Email notifications sent to %{to}" - + link_trash_bin: Trash bin title_restore: Restore notice_dmsf_file_restored: The document has been successfully restored @@ -320,17 +315,20 @@ en: notice_dmsf_link_restored: The link has been successfully restored title_restore_checked: Restore checked error_parent_folder: "The parent folder doesn't exist" - + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked error_parent_locked: Unable to complete lock - resource parent is locked error_resource_locked: Unable to complete lock - resource is locked - error_lock_exclusively: unable to lock exclusively an already-locked resource - error_unlock_parent_locked: Unlock failed - resource parent is locked - - my: - blocks: - locked_documents: Locked documents - openap_provals: Open approvals - + error_lock_exclusively: Unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked + + field_dmsf_tree_view: Navigate folders in a tree + label_dmsf_version: Version + + locked_documents: Locked documents + open_approvals: Open approvals + label_maximum_ajax_upload_filesize: Maximum file size uploadable via AJAX - note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB. \ No newline at end of file + note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB. + label_classic: Classic + label_drag_drop: "Drag&Drop" diff --git a/config/locales/es.yml b/config/locales/es.yml index cab4c704..faa35823 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1,10 +1,10 @@ # encoding: utf-8 -# +# # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2015 Agustin Ivorra -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,13 +26,13 @@ es: label_dmsf_file_revision_plural: Document revisions label_dmsf_file_revision_access_plural: Document accesses warning_no_entries_selected: No ha seleccionado ningún ítem - error_email_to_must_be_entered: Ingrese un email + error_email_to_must_be_entered: Ingrese un email warning_file_already_locked: El archivo ya está bloqueado notice_file_locked: Archivo bloqueado warning_file_not_locked: Archivo no bloqueado notice_file_unlocked: Archivo desbloqueado - error_only_user_that_locked_file_can_unlock_it: Solo los usuarios que bloquearon previamente al archivo lo pueden desbloquear - error_max_files_exceeded: "Se excedio el numero permitido de archivos bajados de manera simultánea:" + error_only_user_that_locked_file_can_unlock_it: Solo los usuarios que bloquearon previamente al archivo lo pueden desbloquear + error_max_files_exceeded: "Se excedio el numero permitido de archivos bajados de manera simultánea:" error_entry_project_does_not_match_current_project: Las entradas del proyecto no concuerdan con el proyecto seleccionado notice_folder_created: Carpeta creada satisfactoriamente error_folder_creation_failed: La creacion de la carpeta ha fallado @@ -40,7 +40,7 @@ es: notice_folder_deleted: Carpeta borrada error_folder_is_not_empty: La carpeta no está vacía error_folder_title_is_already_used: El título ingresado ya está siendo usado por otro documento - notice_folder_details_were_saved: Los detalles de la carpeta fueron grabados correctamente + notice_folder_details_were_saved: Los detalles de la carpeta fueron grabados correctamente error_folder_is_locked: Carpeta bloqueado error_file_is_locked: Archivo bloqueado notice_file_deleted: Archivo borrado @@ -58,7 +58,7 @@ es: warning_file_notifications_already_activated: Las notificaciones del archivo seleccionado ya estaban activadas previamente notice_file_notifications_activated: Notificación de archivo activado warning_file_notifications_already_deactivated: Las notificaciones del archivo seleccionado ya estaban desactivadas previamente - notice_file_notifications_deactivated: Notificación de archivo desactivada + notice_file_notifications_deactivated: Notificación de archivo desactivada link_details: "%{title} detalles" link_edit: "Editar %{title}" submit_create: Crear @@ -81,8 +81,8 @@ es: title_waiting_for_approval: Esperando Aprobación title_approved: Aprobado title_unlock_file: Desbloquear para que otros miembros puedan editarlo - title_lock_file: Bloquear para que otros miembros no puedan editarlo - title_download_checked: Descargar archivos seleccionados en Zip + title_lock_file: Bloquear para que otros miembros no puedan editarlo + title_download_checked: Descargar archivos seleccionados en Zip title_send_checked_by_email: Enviar los seleccionados por email link_user_preferences: Preferencias de su proyecto DMSF heading_send_documents_by_email: Enviar documentos por email @@ -94,11 +94,7 @@ es: label_email_body: Cuerpo de Mensaje label_email_send: Enviar title_notifications_active: Activar Notificaciones - label_file_size: Tamaño de Archivo - heading_file_upload: Subir - note_uploaded_maximum_files_at_once: "El número máximo de una subida de archivos es %{number}." - note_upload_files_greater_than_two_gb: Para subir archivos mayores a 2GB debe tener un navegador web de 64bits. - submit_upload_files: Subir + label_upload: Subir heading_new_folder: Nuevo directorio label_title: Título label_description: Descripción @@ -109,7 +105,7 @@ es: select_option_deactivated: Desactivado select_option_activated: Activado label_title_format: Title format - text_title_format: "Document title format for download (%t - title, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" + text_title_format: "Document title format for download (%t - title, %f - file, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" title_save_preferences: Guardar Preferencias heading_revisions: Revisiones title_download: Descargar @@ -117,9 +113,9 @@ es: label_created: Creado label_changed: Modificado info_changed_by_user: "%{changed} por" - label_filename: Nombre + label_filename: Nombre label_mime: Mime - label_size: Tamaño + label_size: Tamaño heading_new_revision: Nueva Revisión option_version_same: Misma option_version_minor: Menor @@ -154,7 +150,7 @@ es: permission_user_preferences: Preferencias de usuario permission_view_dmsf_files: Ver documentos permission_folder_manipulation: Manipulación de directorio - permission_file_manipulation: Manipulación de Archivos + permission_file_manipulation: Manipulación de Archivos permission_force_file_unlock: Forzar desbloqueo de archivo permission_manage_workflows: Gesti{on de flujo de trabajo permission_file_delete: Eliminar documentos @@ -167,7 +163,7 @@ es: error_user_has_not_right_delete_folder: "No tiene permisos para eliminar el directorio" error_user_has_not_right_delete_file: "No tiene permisos para eliminar el archivo" notice_entries_deleted: Entradas eliminadas - warning_some_entries_were_not_deleted: "Algunas entradas no fueron eliminadas: %{entries}" + warning_some_entries_were_not_deleted: "Algunas entradas no fueron eliminadas: %{entries}" title_delete_checked: Eliminación chequeada title_items: items title_filename_for_download: Nombre de archivo utilizado para descargar o crear zip @@ -219,15 +215,14 @@ es: warning_folder_not_locked: Desafortunadamente, el directorio no se pudo bloquear notice_folder_unlocked: El directorio se desbloqueó exitosamente error_only_user_that_locked_folder_can_unlock_it: No está autorizado para desbloquearel directorio - title_folder_parent_locked: "Directorio padre %{name} está bloqueado" title_unlock_folder: Desbloquear para que sea editado por otros miembros title_lock_folder: Bloquear para evitar que sea editado por otros miembros select_option_webdav_readonly: Solo Lectura select_option_webdav_readwrite: Lectura/Escritura label_webdav_strategy: Estrategia Webdav - note_webdav_strategy: Habilitar el administrador para configurar si la plataforma webdav es solo lectura o lectura-escritura para los usuarios finales. - + note_webdav_strategy: Habilitar el administrador para configurar si la plataforma webdav es solo lectura o lectura-escritura para los usuarios finales. + error_unable_delete_dmsf_workflow: Incapaz de eliminar el flujo de trabajo error_empty_note: "La nota no puede estar vacía" error_workflow_assign: "Ocurió un errpr mientras se asignaba" @@ -251,7 +246,7 @@ es: label_dmsf_workflow_add_approver: "Añadir un nuevo aprobador con una función lógica:" label_or: o label_action: "Acción" - label_note: Nota + label_note: Nota title_none: Ninguno title_rejection: Rechazo title_delegation: "Delegación" @@ -266,26 +261,26 @@ es: dmsf_new_step: Nuevo Paso message_dmsf_wokflow_note: Tu nota... info_revision: "r%{rev}" - link_workflow: Flujo de Trabajo + link_workflow: Flujo de Trabajo notice_workflow_started: "Flujo de trabajo de aprobación iniciado satisfactoriamente" - text_email_subject_approved: "Flujo de trabajo de aprobación %{name} aprobado" - text_email_subject_rejected: "Flujo de trabajo de aprobación %{name} rechazado" - text_email_subject_delegated: "Flujo de trabajo de aprobación %{name} delegado" - text_email_subject_requires_approval: "Flujo de trabajo de aprobación %{name} requiere su aprobación" - text_email_subject_updated: "Flujo de trabajo de aprobación %{name} actualizado" - text_email_subject_started: "Flujo de trabajo de aprobación %{name} comenzado" + text_email_subject_approved: aprobado + text_email_subject_rejected: rechazado + text_email_subject_delegated: delegado + text_email_subject_requires_approval: requiere su aprobación + text_email_subject_updated: actualizado + text_email_subject_started: comenzado text_email_finished_approved: "El flujo de trabajo de aprobación '%{name}' asignado al documento '%{filename}' acaba de ser terminado y él ha sido aprobado." text_email_finished_rejected: "El flujo de trabajo de aprobación '%{name}' asignado al documento '%{filename}' acaba de ser terminado y él ha sido rechazado por el siguiente motivo '%{notice}'." text_email_finished_delegated: "El flujo de trabajo de aprobación '%{name}' asignado al documento '%{filename}' acaba de ser delegado por '%{notice}' y se espera que haga una aprobación en la etapa de aprobación actual." text_email_finished_step: "El flujo de trabajo de aprobación '%{name}' asignado al documento '%{filename}' acaba de terminar uno de los pasos de aprobación y se espera que haga una aprobación en el siguiente paso." text_email_finished_step_short: "El flujo de trabajo de aprobación '%{name}' asignado al documento '%{filename}' acaba de finalizar uno de los pasos de aprobación." - text_email_started: "El flujo de trabajo de aprobación '%{name}' asignado al documento '%{filename}' acaba de iniciarse y se espera que haga una aprobación en la etapa de aprobación actual." + text_email_started: "El flujo de trabajo de aprobación '%{name}' asignado al documento '%{filename}' acaba de iniciarse y se espera que haga una aprobación en la etapa de aprobación actual." text_email_to_proceed: "Para continuar, haga clic en el icono de la casilla de verificación al lado del documento" text_email_to_see_history: "Para ver el historial de aprobación haga clic en el estado del flujo de trabajo del documento" text_email_to_see_status: "Para ver el estado actual del flujo de trabajo de aprobación, haga clic en el estado del flujo de trabajo del documento" label_my_open_approvals: Mis aprobaciones abiertas label_my_locked_documents: Mis documentos bloqueados - + title_create_link: "Crear un enlace simbólico" label_link_from: Enlace desde label_link_to: Enlace hacia @@ -294,25 +289,25 @@ es: field_target_file: Archivo fuente title_download_entries: Entradas de descarga label_external: External - + label_link_name: Nombre de enlace label_link_external_url: URL label_target_folder: Directorio destino label_source_folder: Directorio fuente label_target_project: Proyecto destino - label_source_project: Proyecto fuente - - text_email_doc_updated_subject: "Documentos de %{project} actualizados" + label_source_project: Proyecto fuente + + text_email_doc_updated_subject: Documentos actualizados text_email_doc_updated: acaba de actualizar los ducumentos de text_email_doc_follows: lo siguiente - text_email_doc_deleted_subject: "Documentos de %{project} eliminados" + text_email_doc_deleted_subject: Documentos eliminados text_email_doc_deleted: acaba de eliminar documentos de label_links_only: Solo enlaces - + label_display_notified_recipients: Mostrar destinatarios notificados note_display_notified_recipients: "El usuario será informado de todos los destinatarios a los que acaba de enviar la notificación por correo electrónico" warning_email_notifications: "Notificaciones de email enviadas a %{to}" - + link_trash_bin: Tacho title_restore: Recuperar notice_dmsf_file_restored: El documento ha sido restaurado satisfactoriamente @@ -320,17 +315,20 @@ es: notice_dmsf_link_restored: El enlace ha sido restaurado satisfactoriamente title_restore_checked: Restaurar Seleccionados error_parent_folder: "El directorio padre no existe" - + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked error_parent_locked: Unable to complete lock - resource parent is locked error_resource_locked: Unable to complete lock - resource is locked - error_lock_exclusively: unable to lock exclusively an already-locked resource - error_unlock_parent_locked: Unlock failed - resource parent is locked - - my: - blocks: - locked_documents: Documentos bloqueados - open_approvals: Aprobaciones abiertas - + error_lock_exclusively: Unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked + + field_dmsf_tree_view: Navigate folders in a tree + label_dmsf_version: Versión + + locked_documents: Documentos bloqueados + open_approvals: Aprobaciones abiertas + label_maximum_ajax_upload_filesize: "El máximo tamaño de archivo para subir por AJAX" - note_maximum_ajax_upload_filesize: "El límite máximo de tamaño de archivo que puede ser subido por la interfaz AJAX estandar, de lo contrario se debe utilizar el formulario estandar de Redmine. El número es en MB." \ No newline at end of file + note_maximum_ajax_upload_filesize: "El límite máximo de tamaño de archivo que puede ser subido por la interfaz AJAX estandar, de lo contrario se debe utilizar el formulario estandar de Redmine. El número es en MB." + label_classic: Classic + label_drag_drop: "Drag&Drop" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 6102f966..0132ed4c 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1,11 +1,11 @@ # encoding: utf-8 -# +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn -# Copyright (C) 2014 Atmis -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2014 Atmis +# Copyright (C) 2011-16 Karel Pičman # 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 @@ -31,8 +31,8 @@ fr: notice_file_locked: Fichier verrouillé warning_file_not_locked: Fichier déverrouillé notice_file_unlocked: Fichier déverrouillé - error_only_user_that_locked_file_can_unlock_it: "Le fichier ne peut être déverrouillé que par celui qui l'a verrouillé" - error_max_files_exceeded: Le nombre de fichiers pouvant être téléchargés simultanément est dépassé + error_only_user_that_locked_file_can_unlock_it: "Le fichier ne peut être déverrouillé que par celui qui l'a verrouillé" + error_max_files_exceeded: Le nombre de fichiers pouvant être téléchargés simultanément est dépassé error_entry_project_does_not_match_current_project: "Le projet saisi ne correspond pas au projet courant" notice_folder_created: Dossier créé error_folder_creation_failed: Erreur de création du dossier @@ -40,7 +40,7 @@ fr: notice_folder_deleted: Dossier supprimé error_folder_is_not_empty: "Le dossier n'est pas vide" error_folder_title_is_already_used: Le titre du fichier est déjà utilisé - notice_folder_details_were_saved: Les détails du dossier ont été enregistrés + notice_folder_details_were_saved: Les détails du dossier ont été enregistrés error_folder_is_locked: Le dossier est verrouillé error_file_is_locked: Le fichier est verrouillé notice_file_deleted: Le fichier a été supprimé @@ -81,8 +81,8 @@ fr: 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 - title_lock_file: "Verrouiller afin d'empêcher les modifications du document" - title_download_checked: Télécharger les fichiers sélectionnés au format zip + title_lock_file: "Verrouiller afin d'empêcher les modifications du document" + title_download_checked: Télécharger les fichiers sélectionnés au format zip title_send_checked_by_email: Transmettre les fichiers sélectionnés par mail link_user_preferences: Préférences personnelles du module DMSF heading_send_documents_by_email: Transmettre les documents par mail @@ -94,11 +94,7 @@ fr: label_email_body: Message label_email_send: Envoyer title_notifications_active: Notifications actives - label_file_size: Taille du fichier - heading_file_upload: Déposer des fichiers - note_uploaded_maximum_files_at_once: "Seulement %{number} fichiers peuvent être déposés à la fois." - note_upload_files_greater_than_two_gb: "Afin de transmettre des fichiers de plus de 2Go, l'utilisation d'un navigateur de 64Bits est nécessaire." - submit_upload_files: Transmission + label_upload: Transmission heading_new_folder: Nouveau Dossier label_title: Titre label_description: Description @@ -109,7 +105,7 @@ fr: select_option_deactivated: Désactivé select_option_activated: Activé label_title_format: Title format - text_title_format: "Document title format for download (%t - title, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" + text_title_format: "Document title format for download (%t - title, %f - file, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" title_save_preferences: Enregistrer les préférences heading_revisions: Révisions title_download: Télécharger @@ -117,9 +113,9 @@ fr: label_created: Créé label_changed: Modifié info_changed_by_user: "%{changed} par" - label_filename: Fichier + label_filename: Fichier label_mime: Type - label_size: Taille + label_size: Taille heading_new_revision: Nouvelle révision option_version_same: (identique) option_version_minor: (modification mineure) @@ -154,7 +150,7 @@ fr: permission_user_preferences: Préférences utilisateur permission_view_dmsf_files: Afficher documents permission_folder_manipulation: Gestion des dossiers - permission_file_manipulation: Gestion des documents + permission_file_manipulation: Gestion des documents permission_force_file_unlock: Forcer le déverrouillage du document permission_manage_workflows: Gérer les flux de validation permission_file_delete: Supprimer les documents @@ -167,12 +163,12 @@ fr: error_user_has_not_right_delete_folder: "L'utilisateur ne dispose pas des droits nécessaires permettant la suppression du dossier" error_user_has_not_right_delete_file: "L'utilisateur ne dispose pas des droits nécessaires permettant la suppression du dossier" notice_entries_deleted: Elément(s) supprimé(s) - warning_some_entries_were_not_deleted: "Certains éléments n'ont pas été supprimés : %{entries}" + warning_some_entries_were_not_deleted: "Certains éléments n'ont pas été supprimés : %{entries}" title_delete_checked: Supprimer les éléments sélectionnés title_items: items title_filename_for_download: "Nom du fichier à utiliser lors du téléchargement ou de l'archive ZIP" label_number_of_folders: Dossiers - label_number_of_documents: Fichiers + label_number_of_documents: Fichiers error_file_storage_directory_does_not_exist: Le répertoire de stockage des fichiers n'existe pas ou n'a pas pu être créé error_file_can_not_be_created: "Le fichier n'a pas pu être enregistré dans le répertoire de stockage" error_wrong_zip_encoding: Mauvais jeu de caractères pour la transformation du nom du ZIP @@ -203,7 +199,7 @@ fr: title_copy: Copie error_folder_cannot_be_copied: Le dossier ne peut pas être copié notice_folder_copied: Dossier copié - + error_max_email_filesize_exceeded: "Vous avez dépassé la taille maximale des fichiers pouvant être transmis par mail (%{number} MB)" note_maximum_email_filesize: Taille maximale, en méga octets, des fichiers pouvant être transmis par mail. 0 indique aucune restriction label_maximum_email_filesize: Taille maximale du fichier attaché @@ -219,15 +215,14 @@ fr: warning_folder_not_locked: Echec du verrouillage du dossier notice_folder_unlocked: Le dossier a été déverrouillé error_only_user_that_locked_folder_can_unlock_it: "Vous n'êtes autorisé à déverrouiller ce dossier" - title_folder_parent_locked: "Le dossier parent %{name} verrouillé" title_unlock_folder: Déverrouiller afin de permettre la modification par les membres du projet title_lock_folder: "Verrouiller afin d'empêcher les modifications du dossier" - + select_option_webdav_readonly: Lecture select_option_webdav_readwrite: Lecture/Ecriture label_webdav_strategy: Accès Webdav note_webdav_strategy: "Permet à l'administrateur d'autoriser les utilisateurs au module Webdav en letcure seule ou en lecture et écriture." - + error_unable_delete_dmsf_workflow: Impossible de supprimer le flux de validation error_empty_note: La note ne peut pas être vide error_workflow_assign: "Une erreur s'est produite lors de l'attribution" @@ -251,29 +246,29 @@ fr: label_dmsf_workflow_add_approver: "Ajouter un nouvel approbateur avec une fonction logique :" label_or: ou label_action: Action - label_note: Note + label_note: Note title_none: Aucun title_rejection: Rejet title_delegation: Délégation title_assignment: Attribution - title_start: Démarrer + title_start: Démarrer title_dmsf_workflow_log: Journal du flux de validation title_assigned: Assigné title_approval: Approbation title_rejected: Rejeté dmsf_and: ET dmsf_or: OU - dmsf_new_step: Nouvelle étape + dmsf_new_step: Nouvelle étape message_dmsf_wokflow_note: Votre note... info_revision: "r%{rev}" - link_workflow: Flux + link_workflow: Flux notice_workflow_started: Flux de validation démarré avec succès - text_email_subject_approved: "Flux de validation %{name} approuvé" - text_email_subject_rejected: "Flux de validation %{name} rejeté" - text_email_subject_delegated: "Flux de validation %{name} délégué" - text_email_subject_requires_approval: "Flux de validation %{name} requiert votre approbation" - text_email_subject_updated: "Flux de validation %{name} mis à jour" - text_email_subject_started: "Flux de validation %{name} démarré" + text_email_subject_approved: approuvé + text_email_subject_rejected: rejeté + text_email_subject_delegated: délégué + text_email_subject_requires_approval: requiert votre approbation + text_email_subject_updated: mis à jour + text_email_subject_started: démarré text_email_finished_approved: "Le flux de validation '%{name}' assigné au document '%{filename}' vient de se terminer et le document a été approuvé." text_email_finished_rejected: "Le flux de validation '%{name}' assigné au document '%{filename}' vient de se terminer et le document a été rejeté pour la raison '%{notice}'." text_email_finished_delegated: "Le flux de validation '%{name}' assigné au document '%{filename}' a été délégué pour la raison '%{notice}' et vous êtes tenu d'approuver l'étape actuelle." @@ -285,8 +280,8 @@ fr: text_email_to_see_status: Pour consulter le statut actuel du flux de validation, cliquez sur le statut du flux du document dans label_my_open_approvals: Mes approbations en attente label_my_locked_documents: Mes documents verrouillés - - title_create_link: Créer un lien symbolique + + title_create_link: Créer un lien symbolique label_link_from: Lien depuis label_link_to: Lien vers label_notifications_on: Activer les notifications @@ -294,25 +289,25 @@ fr: field_target_file: Fichier source title_download_entries: Historique des téléchargements label_external: Externe - + label_link_name: Nom du lien label_link_external_url: Adresse Internet label_target_folder: Dossier cible label_source_folder: Dossier source label_target_project: Projet cible - label_source_project: Projet source - - text_email_doc_updated_subject: "Documents de %{project} mis à jour" + label_source_project: Projet source + + text_email_doc_updated_subject: Documents mis à jour text_email_doc_updated: a mis à jour des documents de text_email_doc_follows: comme suit - text_email_doc_deleted_subject: "Documents de %{project} supprimés" + text_email_doc_deleted_subject: Documents supprimés text_email_doc_deleted: a supprimé des documents de - label_links_only: liens seulement - + label_links_only: liens seulement + label_display_notified_recipients: Afficher les destinataires notifiés note_display_notified_recipients: "L'utilisateur sera informé de tous les destinataires à qui un email de notifcation a été envoyé" warning_email_notifications: "Email de notification envoyé à %{to}" - + link_trash_bin: Corbeille title_restore: Récupérer notice_dmsf_file_restored: Le document a été récupéré avec succès @@ -320,17 +315,20 @@ fr: notice_dmsf_link_restored: Le lien a été récupéré avec succès title_restore_checked: Restauration vérifiée error_parent_folder: "Le dossier parent n'existe pas" - + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked error_parent_locked: Unable to complete lock - resource parent is locked error_resource_locked: Unable to complete lock - resource is locked - error_lock_exclusively: unable to lock exclusively an already-locked resource - error_unlock_parent_locked: Unlock failed - resource parent is locked + error_lock_exclusively: Unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked - my: - blocks: - locked_documents: Documents verrouillés - open_approvals: Approbations en attente + field_dmsf_tree_view: Navigate folders in a tree + label_dmsf_version: Version + + locked_documents: Documents verrouillés + open_approvals: Approbations en attente label_maximum_ajax_upload_filesize: Taille maximale de fichier pour téléversement via AJAX note_maximum_ajax_upload_filesize: "Taille maximale, en méga octets, de fichier pour téléversement via l'interface standard AJAX. Sinon l'interface standard de Redmine doit être utilisée." + label_classic: Classic + label_drag_drop: "Drag&Drop" diff --git a/config/locales/it.yml b/config/locales/it.yml new file mode 100644 index 00000000..34c3a8b4 --- /dev/null +++ b/config/locales/it.yml @@ -0,0 +1,334 @@ +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-15 Karel Pičman +# +# 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. + +it: # Italian strings thx 2 Matteo Arceci! + dmsf: DMSF + label_dmsf_file_plural: Documenti + label_dmsf_file_revision_plural: Revisioni al documento + label_dmsf_file_revision_access_plural: Accessi al documento + warning_no_entries_selected: Nessun documento selezionato + error_email_to_must_be_entered: Deve essere inserito l'indirizzo email + warning_file_already_locked: Documento già bloccato + notice_file_locked: Documento bloccato + warning_file_not_locked: Documento non bloccato + notice_file_unlocked: Documento sbloccato + error_only_user_that_locked_file_can_unlock_it: Solo chi ha bloccato il documento può sbloccarlo + error_max_files_exceeded: "Superato il limite di %{number} documenti per il download simultaneo" + error_entry_project_does_not_match_current_project: "Il progetto non corrisponde al progetto corrente" + notice_folder_created: Cartella creata + error_folder_creation_failed: Creazione cartella fallita + error_folder_title_must_be_entered: Deve essere inserito il titolo + notice_folder_deleted: Cartella cancellata + error_folder_is_not_empty: La cartella non è vuota + error_folder_title_is_already_used: Il titolo è già in uso + notice_folder_details_were_saved: I dettagli della cartella sono stati salvati + error_folder_is_locked: La cartella è bloccata + error_file_is_locked: Il documento è bloccato + notice_file_deleted: Il documento è stato cancellato + error_at_least_one_revision_must_be_present: Deve essere presente almeno una revisione + notice_revision_deleted: Revisione cancellata + warning_one_of_files_locked: Uno dei documenti è bloccato + notice_file_unlocked: Documento sbloccato + notice_file_revision_created: Revisione del documento creata + notice_your_preferences_were_saved: Le tue preferenze sono state salvate + notice_your_preferences_were_not_saved: Le tue preferenze non sono state salvate + warning_folder_notifications_already_activated: La notifica su cartella è già attiva + notice_folder_notifications_activated: La notifica su cartella è stata attivata + warning_folder_notifications_already_deactivated: La notifica su cartella è già disattiva + notice_folder_notifications_deactivated: La notifica su cartella è stat disattivata + warning_file_notifications_already_activated: La notifica su documento è già attiva + notice_file_notifications_activated: La notifica su documento è stata attivata + warning_file_notifications_already_deactivated: La notifica su documento è già disattiva + notice_file_notifications_deactivated: La notifica su documento è disattiva + link_details: "%{title} dettagli" + link_edit: "Modifica %{title}" + submit_create: Creato + link_create_folder: Creata cartella + title_check_uncheck_all_for_zip_download_or_email: Seleziona/Deseleziona tutto per lo zip, download o email + title_check_uncheck_all_for_restore_or_delete: Seleziona/Deseleziona tutto per ripristinare o cancellare + link_title: Nome documento + link_size: Dimensioni + link_modified: Modificato + link_ver: Ver. + link_author: Autore + title_check_for_zip_download_or_email: Seleziona per zip, download o email + title_check_for_restore_or_delete: Seleziona per ripristinare o cancellare + title_delete: Cancella + title_notifications_active_deactivate: "Notifiche attive: Disattiva" + 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 + title_lock_file: Blocca per evitare modifiche degli altri membri + title_download_checked: Download selezionati in archivio Zip + title_send_checked_by_email: Selezionati spediti per email + link_user_preferences: Le tue preferenze DMSF di progetto + heading_send_documents_by_email: Documenti spediti per email + label_email_from: Da + label_email_to: A + label_email_cc: CC + label_email_subject: Oggetto + label_email_documents: Documenti + label_email_body: Corpo + label_email_send: Spedisci + title_notifications_active: Notifiche attive + label_upload: Carica + heading_new_folder: Nuova cartella + label_title: Titolo + label_description: Descrizione + submit_save: Salva + info_file_locked: Documento bloccato! + label_notifications: Notifiche + select_option_default: Default + select_option_deactivated: Disattivato + select_option_activated: Attivato + label_title_format: Titolo formattato + text_title_format: "Titolo del documento formattato per il download (%t - titolo, %d - data, %v - versione, %i - ID, %r - revisione). Esempio: %t_%v" + title_save_preferences: Salva preferenze + heading_revisions: Revisioni + title_download: Download + title_delete_revision: Cancella revisione + label_created: Creato + label_changed: Modificato + info_changed_by_user: "%{changed} da" + label_filename: Nome file + label_mime: Mime + label_size: Dimensioni + heading_new_revision: Nuova revisione + option_version_same: Stessa + option_version_minor: Miniore + option_version_major: Maggiore + option_version_custom: Personalizzata + label_new_content: Nuovo contenuto + label_maximum_files_upload: Limite di caricamento + note_maximum_number_of_files_uploaded: Limita il numero massimo di documenti caricati alla volta. 0 significa senza limiti. + label_maximum_files_download: Numero massimo di documenti scaricabili + note_maximum_number_of_files_downloaded: Limita il numero massimo di documenti scaricabili in archivio zip o spediti via email. 0 significa senza limiti. + label_file_storage_directory: Cartella dei documenti + label_index_database: Indice database + label_stemming_language: Linguaggio di Stemming + note_possible_values: Valori possibili + note_pass_none_to_disable_stemming: "passa 'nulla' per disabilitare lo stemming" + label_stem_strategy: Strategia dello Stem + option_stem_none: Stem nulla (default) + option_stem_some: Stem qualcosa + option_stem_all: Stem tutto + label_stemming_description: Questo controlla come il parser delle query applica l'algoritmo di Stemming. Il valore di default è STEM_NONE. I valori possibili sono + note_do_not_stem: "Non fare alcun stemming." + note_stem_some: Cerca per termini 'Stemmed' ad eccezione di quelli che iniziano con la lettera maiuscola, o sono seguiti da alcuni caratteri, o vengono utilizzati con gli operatori che hanno bisogno di informazioni di posizione. I termini 'Stemmed' hanno il prefisso 'Z'. + note_stem_all: "Cerca per termini 'Stemmed' di tutte le parole (nota: non viene aggiunto alcun prefisso 'Z')." + note_stemming_applied: Si noti che l'algoritmo di 'Stemming' viene applicato solo alle parole nei campi probabilistici - termini con filtro booleano non sono compresi. + label_default_notifications: Notifica di default dei documenti + heading_uploaded_files: Documenti caricati + submit_commit: Invia/Salva + link_documents: Documenti + permission_view_dmsf_file_revision_accesses: Visualizza i download nel flusso di attività + permission_view_dmsf_file_revisions: Visualizza le revisioni nel flusso di attività + permission_view_dmsf_folders: Sfoglia i documenti + permission_user_preferences: Preferenze + permission_view_dmsf_files: Visualizza i documenti + permission_folder_manipulation: Modifica la cartella + permission_file_manipulation: Modifica il documento + permission_force_file_unlock: Forza lo sblocco del documento + permission_manage_workflows: Gestisci i flussi di lavoro + permission_file_delete: Elimina i documenti + label_file: Documento + field_folder: Cartella + error_create_cycle_in_folder_dependency: crea un ciclo nella dipendenza della cartella + error_contains_invalid_character: contiene carattere(i) non validi + error_file_commit_require_uploaded_file: L'aggiornamento del documento richiede un documento già caricato in precedenza + warning_some_files_were_not_commited: "Alcuni documenti non sono stati aggiornati a causa di errori di validazione: %{files}" + error_user_has_not_right_delete_folder: "L'utente non ha i diritti per eliminare le cartelle" + error_user_has_not_right_delete_file: "L'utente non ha i diritti per eliminare il documento" + notice_entries_deleted: Voci eliminate + warning_some_entries_were_not_deleted: "Alcune voci non sono state eliminate: %{entries}" + title_delete_checked: Elimina i selezionati + title_items: elementi + title_filename_for_download: Nome file utilizzato per il download o nell'archivio Zip + label_number_of_folders: Cartelle + label_number_of_documents: Documenti + error_file_storage_directory_does_not_exist: "La cartella di archiviazione non esiste e non può essere creata" + error_file_can_not_be_created: "Il documento non può essere creato nella cartella di archiviazione" + error_wrong_zip_encoding: Errato encoding dello Zip + warning_xapian_not_available: Xapian non disponibile + menu_dmsf: Documenti + label_physical_file_delete: File fisico eliminato + user_is_not_project_member: Non sei un membro del progetto + heading_access_downloads_emails: Downloads/Emails + heading_access_first: Primo + heading_access_last: Ultimo + label_dmsf_updated: Aggiornato + label_dmsf_downloaded: Scaricato + title_total_size_of_all_files: Dimensione totale di tutti i files in questa cartella + project_module_dmsf: DMSF + warning_no_project_to_copy_file_to: Nessun progetto nel quale copiare il documento + comment_copied_from: "Copiato da %{source}" + notice_file_copied: Documento copiato + notice_file_moved: Documento spostato + field_target_project: Progetto di destinazione + field_target_folder: Cartella di destinazione + title_copy_or_move: Copia/Sposta + label_dmsf_folder_plural: Cartelle + comment_moved_from: "Spostato da %{source}" + error_target_folder_same: La cartella di destinazione ed il progetto sono gli stessi di adesso + error_file_cannot_be_moved: "Il documento non può essere spostato" + error_file_cannot_be_copied: "Il documento non può essere copiato" + warning_no_project_to_copy_folder_to: Nessun progetto dove copiare la cartella + title_copy: Copia + error_folder_cannot_be_copied: "La cartella non può essere copiata" + notice_folder_copied: Cartella copiata + + error_max_email_filesize_exceeded: "Hai superato la dimensione massima del file per l'invio tramite e-mail. (%{number} MB)" + note_maximum_email_filesize: Limiti di dimensione massima dei file che possono essere inviati via e-mail. 0 significa illimitato. Il numero è in MB. + label_maximum_email_filesize: Dimensione massima degli allegati delle e-mail + header_minimum_filesize: Errore file. + error_minimum_filesize: "Il file %{file} è 0 bytes e non sarà allegato." + parent_directory: Cartella superiore + note_webdav: "Una volta abilitato il Webdav può essere contattato sul percorso %{protocol}://%{domain}/dmsf/webdav/[project identifier]" + label_webdav: Funzionalità Webdav + label_dmsf_plural: "Copia documenti e cartelle (%{files} documenti in %{folders} cartelle)" + + warning_folder_already_locked: Questa cartella è già bloccata + notice_folder_locked: La cartella è stata bloccata + warning_folder_not_locked: Purtroppo la cartella non può essere bloccata + notice_folder_unlocked: La cartella è stata sbloccata + error_only_user_that_locked_folder_can_unlock_it: Non sei autorizzato a sbloccare questa cartella + title_unlock_folder: Sblocca per consentire modifiche agli altri membri + title_lock_folder: Blocca per evitare modifiche da parte di altri membri + + select_option_webdav_readonly: Sola lettura + select_option_webdav_readwrite: Lettura/Scrittura + label_webdav_strategy: Strategia Webdav + note_webdav_strategy: Abilita l'amministratore a decidere se Webdav è di sola lettura oppure lettura-scrittura per gli utenti finali. + + error_unable_delete_dmsf_workflow: Impossibile eliminare il flusso di lavoro + error_empty_note: "La nota non può essere vuota" + error_workflow_assign: C'è stato un errore durante l'assegnazione + error_cannot_start_workflow: "Il flusso di lavoro non può partire" + error_cannot_renumber_steps: "I passi non possono essere numerati" + label_dmsf_workflow_new: Nuova 'approvazione di flusso di lavoro' + field_label_dmsf_workflow: Approvazione di flusso di lavoro + field_label_dmsf_workflow_name: Nome per l'approvazione di flusso di lavoro + label_dmsf_workflow_plural: Approvazioni flusso di lavoro + label_dmsf_workflow_plural_num: Approvazioni flusso di lavoro (%{count}) + label_dmsf_workflow_step: Passo + label_dmsf_workflow_step_plural: Passi + label_dmsf_workflow_approval: Approvazione + label_dmsf_workflow_approval_plural: Approvazioni + label_dmsf: DMSF + label_dmsf_wokflow_action_approve: Approva + label_dmsf_wokflow_action_reject: Rifiuta + label_dmsf_wokflow_action_delegate: Delega a + label_dmsf_wokflow_action_assign: Assegna un 'approvazione di flusso di lavoro' + label_dmsf_wokflow_action_start: Inizia un flusso di lavoro + label_dmsf_workflow_add_approver: "Aggiungi un nuovo approvatore con funzioni logiche:" + label_or: oppure + label_action: Azione + label_note: Note + title_none: Nessuno + title_rejection: Rifiuto + title_delegation: Delega + title_assignment: Assegnazione + title_start: Inizio + title_dmsf_workflow_log: Log di approvazione di flusso di lavoro + title_assigned: Assegnato + title_approval: Approvazione + title_rejected: Rifiutato + dmsf_and: AND + dmsf_or: OR + dmsf_new_step: Nuovo passo + message_dmsf_wokflow_note: La tua nota... + info_revision: "r%{rev}" + link_workflow: Flusso di lavoro + notice_workflow_started: Approvazione di flusso di lavoro inizializzato + text_email_subject_approved: "Approvazione di flusso di lavoro %{name} approvato" + text_email_subject_rejected: "Approvazione di flusso di lavoro %{name} rifiutato" + text_email_subject_delegated: "Approvazione di flusso di lavoro %{name} delegato" + text_email_subject_requires_approval: "Approvazione di flusso di lavoro %{name} richiede la tua approvazione" + text_email_subject_updated: "Approvazione di flusso di lavoro %{name} aggiornato" + text_email_subject_started: "Approvazione di flusso di lavoro %{name} inizializzato" + text_email_finished_approved: "L'approvazione di flusso di lavoro '%{name}' assegnato a '%{filename}' è stata completata ed il documento è stato approvato." + text_email_finished_rejected: "L'approvazione di flusso di lavoro '%{name}' assegnato a '%{filename}' è stata completata ma il documento è stato rifiutato a causa di '%{notice}'." + text_email_finished_delegated: "L'approvazione di flusso di lavoro '%{name}' assegnato a '%{filename}' è stata delegata a causa di '%{notice}' e sei tenuto ad approvare nel passo corrente." + text_email_finished_step: "L'approvazione di flusso di lavoro '%{name}' assegnato a '%{filename}' ha completato uno dei passi di approvazione e sei tenuto ad approvare il prossimo passo." + text_email_finished_step_short: "L'approvazione di flusso di lavoro '%{name}' assegnato a '%{filename}' ha completato uno dei passi di approvazione." + text_email_started: "L'approvazione di flusso di lavoro '%{name}' assegnato a '%{filename}' è stato inizializzato e sei tenuto ad approvare il passo corrente." + text_email_to_proceed: Per proseguire clicca sull'icona checkbox a fianco del documento + text_email_to_see_history: Per vedere la storia di approvazione clicca sullo stato del flusso di lavoro del documento + text_email_to_see_status: Per vedere lo stato attuale del flusso di lavoro di approvazione clicca sullo stato del flusso di lavoro del documento + label_my_open_approvals: Le mie approvazioni aperte + label_my_locked_documents: I miei documenti bloccati + + title_create_link: Crea un collegamento + label_link_from: Collegamento da + label_link_to: Collegamento a + label_notifications_on: Attiva notifiche + label_notifications_off: Disattiva notifiche + field_target_file: Scarica sorgente + title_download_entries: Scarica documenti + label_external: Esterno + + label_link_name: Nome del collegamento + label_link_external_url: URL + label_target_folder: Cartella di destinazione + label_source_folder: Cartella sorgente + label_target_project: Progetto di destinazione + label_source_project: Progetto sorgente + + text_email_doc_updated_subject: "Documenti del progetto %{project} caricati" + text_email_doc_updated: ha documenti appena attualizzati + text_email_doc_follows: come segue + text_email_doc_deleted_subject: "Documenti del progetto %{project} cancellati" + text_email_doc_deleted: ha documenti appena eliminati + label_links_only: solo colegamenti + + label_display_notified_recipients: Visualizza i destinatari notificati + note_display_notified_recipients: L'utente sarà informato di tutti i destinatari appena inviato la notifica e-mail. + warning_email_notifications: "Notifica email inviata a %{to}" + + link_trash_bin: Cestino + title_restore: Ripristina + notice_dmsf_file_restored: Il documento è stato ripristinato correttamente + notice_dmsf_folder_restored: La cartella è stata ripristinata correttamente + notice_dmsf_link_restored: Il collegamento è stato ripristinato correttamente + title_restore_checked: Ripristina selezionati + error_parent_folder: "La cartella padre non esiste" + + error_resource_or_parent_locked: Impossibile completare il blocco - la risorsa (o superiore) è bloccata + error_parent_locked: Impossibile completare il blocco - la risorsa superiore è bloccata + error_resource_locked: Impossibile completare il blocco - la risorsa è bloccata + error_lock_exclusively: impossibile bloccare in modo esclusivo una risorsa già bloccata + error_unlock_parent_locked: Sblocco fallito - la risorsa superiore è bloccata + + field_dmsf_tree_view: Navigate folders in a tree + label_dmsf_version: Version + + locked_documents: Documenti bloccati + open_approvals: Approvazioni aperte + + label_maximum_ajax_upload_filesize: Dimensione massima dei documenti caricabili tramite AJAX + note_maximum_ajax_upload_filesize: Limita la dimensione massima dei documenti che possono essere caricati tramite interfaccia standard AJAX altrimenti sarà necessario utilizzare il modulo standard di Redmine. Il numero è espresso in MB. + label_classic: Classic + label_drag_drop: "Drag&Drop" diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 8d15c24d..5ae5ccf4 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -1,9 +1,9 @@ # encoding: utf-8 -# +# # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # # This program is free software; you can redistribute it and/or @@ -26,13 +26,13 @@ ja: label_dmsf_file_revision_plural: 文書管理ファイルリビジョン label_dmsf_file_revision_access_plural: 文書管理ファイルアクセス warning_no_entries_selected: エントリーが選ばれていません - error_email_to_must_be_entered: 電子メールの To は省略できません + error_email_to_must_be_entered: 電子メールの To は省略できません warning_file_already_locked: ファイルは既にロックされています notice_file_locked: ファイルをロックしました warning_file_not_locked: ファイルはロックされていません notice_file_unlocked: ファイルをロック解除しました - error_only_user_that_locked_file_can_unlock_it: ファイルをロックしたユーザだけがロック解除できます - error_max_files_exceeded: "同時にダウンロードできるファイル数の上限 %{number} を超えています" + error_only_user_that_locked_file_can_unlock_it: ファイルをロックしたユーザだけがロック解除できます + error_max_files_exceeded: "同時にダウンロードできるファイル数の上限 %{number} を超えています" error_entry_project_does_not_match_current_project: 指定したプロジェクトは現在のプロジェクトと一致しません notice_folder_created: フォルダを作成しました error_folder_creation_failed: フォルダを作成できません @@ -40,7 +40,7 @@ ja: notice_folder_deleted: フォルダを削除しました error_folder_is_not_empty: フォルダが空ではありません error_folder_title_is_already_used: タイトルは既に使われています - notice_folder_details_were_saved: フォルダの詳細を保存しました + notice_folder_details_were_saved: フォルダの詳細を保存しました error_folder_is_locked: Folder is locked error_file_is_locked: ファイルはロックされています notice_file_deleted: ファイルを削除しました @@ -81,8 +81,8 @@ ja: title_waiting_for_approval: 承認待ち title_approved: 承認済み title_unlock_file: ロック解除して他のメンバーの変更を許可します - title_lock_file: ロックして他のメンバーの変更を禁止します - title_download_checked: チェックしたものを Zip アーカイブでダウンロードします + title_lock_file: ロックして他のメンバーの変更を禁止します + title_download_checked: チェックしたものを Zip アーカイブでダウンロードします title_send_checked_by_email: チェックしたものを電子メールで送信します link_user_preferences: プロジェクトの文書管理設定 heading_send_documents_by_email: 電子メールによる文書の送信 @@ -94,11 +94,7 @@ ja: label_email_body: 本文 label_email_send: 送信 title_notifications_active: 通知は有効です - label_file_size: ファイルサイズ - heading_file_upload: アップロード - note_uploaded_maximum_files_at_once: "最大 %{number} ファイルを一度にアップロードすることができます。" - note_upload_files_greater_than_two_gb: 2GB より大きいファイルをアップロードするには 64bit 対応ブラウザが必要です。 - submit_upload_files: アップロード + label_upload: アップロード heading_new_folder: 新規フォルダ label_title: タイトル label_description: 説明 @@ -109,7 +105,7 @@ ja: select_option_deactivated: 無効 select_option_activated: 有効 label_title_format: Title format - text_title_format: "Document title format for download (%t - title, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" + text_title_format: "Document title format for download (%t - title, %f - file, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" title_save_preferences: 設定を保存します heading_revisions: リビジョン title_download: ダウンロードします @@ -117,9 +113,9 @@ ja: label_created: 作成者/日時 label_changed: 更新者/日時 info_changed_by_user: "/ %{changed}" - label_filename: ファイル名 + label_filename: ファイル名 label_mime: Mime - label_size: サイズ + label_size: サイズ heading_new_revision: 新しいリビジョン option_version_same: 変更なし option_version_minor: マイナー @@ -144,7 +140,7 @@ ja: note_stem_some: "大文字から始まる、特定の文字の後に続く、あるいは位置情報を必要とするオペレーターと共に使われる語を除くそれ以外の語の語幹を検索します。語幹抽出された語は、先頭に 'Z' が付きます。" note_stem_all: "すべての語の語幹を検索します。(注: 先頭に 'Z' は付きません。)" note_stemming_applied: 語幹抽出アルゴリズムは、確率を測る対象の位置にある語にしか適用できない (論理演算子の用語自体は語幹抽出されない) ことにご注意ください。 - label_default_notifications: ファイル通知の既定値 + label_default_notifications: ファイル通知の既定値 heading_uploaded_files: アップロードされたファイル submit_commit: コミット link_documents: 文書 @@ -154,11 +150,10 @@ ja: permission_user_preferences: ユーザ設定 permission_view_dmsf_files: ファイルの表示 permission_folder_manipulation: フォルダの操作 - permission_file_manipulation: ファイルの操作 + permission_file_manipulation: ファイルの操作 permission_force_file_unlock: ファイルの強制ロック解除 permission_manage_workflows: ワークフロー管理 permission_file_delete: ファイルの削除 - permission_file_approval: ファイルの承認 label_file: ファイル field_folder: フォルダ error_create_cycle_in_folder_dependency: フォルダの依存関係が循環しています @@ -168,12 +163,12 @@ ja: error_user_has_not_right_delete_folder: ユーザにフォルダを削除する権限がありません error_user_has_not_right_delete_file: ユーザにファイルを削除する権限がありません notice_entries_deleted: エントリーを削除しました - warning_some_entries_were_not_deleted: "いくつかのエントリーは削除されませんでした: %{entries}" + warning_some_entries_were_not_deleted: "いくつかのエントリーは削除されませんでした: %{entries}" title_delete_checked: チェックしたものを削除します title_items: items title_filename_for_download: ファイル名はダウンロードまたは Zip アーカイブに使われます label_number_of_folders: フォルダ - label_number_of_documents: 文書 + label_number_of_documents: 文書 error_file_storage_directory_does_not_exist: ファイル保存フォルダが存在せず作ることもできません error_file_can_not_be_created: ファイルを保存フォルダに作ることができません error_wrong_zip_encoding: Zip エンコーディングが正しくありません @@ -197,14 +192,14 @@ ja: title_copy_or_move: コピー/移動 label_dmsf_folder_plural: Dmsf フォルダ comment_moved_from: "%{source} から移動しました" - error_target_folder_same: コピー/移動先のフォルダとプロジェクトが現在と同じです + error_target_folder_same: コピー/移動先のフォルダとプロジェクトが現在と同じです error_file_cannot_be_moved: ファイルを移動できません error_file_cannot_be_copied: ファイルをコピーできません warning_no_project_to_copy_folder_to: フォルダをコピーするプロジェクトがありません title_copy: コピー error_folder_cannot_be_copied: フォルダをコピーできません - notice_folder_copied: フォルダをコピーしました - + notice_folder_copied: フォルダをコピーしました + error_max_email_filesize_exceeded: "電子メールで送信可能なファイルサイズの上限を越えています (%{number} MB)" note_maximum_email_filesize: "電子メールで送信可能なファイルサイズの上限。0は無制限。単位はMB。" label_maximum_email_filesize: "電子メール添付ファイルサイズ上限" @@ -220,7 +215,6 @@ ja: warning_folder_not_locked: "フォルダをロックすることができませんでした" notice_folder_unlocked: "フォルダのロックを解除しました" error_only_user_that_locked_folder_can_unlock_it: "このフォルダのロックを解除する権限がありません" - title_folder_parent_locked: "親フォルダ %{name} はロックされています" title_unlock_folder: "ロックを解除し他メンバが更新できるようにします" title_lock_folder: "ロックし他メンバとの競合を回避します" @@ -252,67 +246,68 @@ ja: label_dmsf_workflow_add_approver: "新規承認者の追加:" label_or: または label_action: アクション - label_note: コメント + label_note: コメント title_none: 無し title_rejection: 否認 title_delegation: 代理承認 title_assignment: アサイン - title_start: 開始 + title_start: 開始 title_dmsf_workflow_log: 承認ワークフローの履歴 title_assigned: アサイン title_approval: 承認 title_rejected: 否認 dmsf_and: AND dmsf_or: OR - dmsf_new_step: 新規ステップ + dmsf_new_step: 新規ステップ message_dmsf_wokflow_note: コメント info_revision: "r%{rev}" - link_workflow: ワークフロー + link_workflow: ワークフロー notice_workflow_started: 承認ワークフローが開始されました - text_email_subject_approved: "承認ワークフロー '%{name}' は承認されました" - text_email_subject_rejected: "承認ワークフロー '%{name}' は否認されました" - text_email_subject_delegated: "承認ワークフロー '%{name}' は代理承認が設定されました" - text_email_subject_requires_approval: "承認ワークフロー '%{name}' はあなたの承認待ちです" - text_email_subject_updated: "承認ワークフロー '%{name}' が更新されました" - text_email_subject_started: "承認ワークフロー '%{name}' が開始されました" + text_email_subject_approved: は承認されました + text_email_subject_rejected: は否認されました + text_email_subject_delegated: は代理承認が設定されました + text_email_subject_requires_approval: はあなたの承認待ちです + text_email_subject_updated: が更新されました + text_email_subject_started: が開始されました" text_email_finished_approved: "承認ワークフロー '%{name}' において '%{filename}' が承認されました。" text_email_finished_rejected: "承認ワークフロー '%{name}' において '%{filename}' が否認されました。理由:'%{notice}'。" text_email_finished_delegated: "承認ワークフロー '%{name}' において代理承認が依頼されました。承認対象 '%{filename}' の内容をご確認の上、承認・否認のご判断をお願い致します(依頼主コメント:'%{notice}')。" text_email_finished_step: "承認ワークフロー '%{name}' からの承認依頼です。承認対象 '%{filename}' の内容をご確認の上、承認・否認のご判断をお願い致します。" text_email_finished_step_short: "承認ワークフロー '%{name}' において '%{filename}' の承認ステップが一つ終了しました。" - text_email_started: "承認ワークフロー '%{name}' からの承認依頼です。承認対象 '%{filename}' の内容をご確認の上、承認・否認のご判断をお願い致します。" + text_email_started: "承認ワークフロー '%{name}' からの承認依頼です。承認対象 '%{filename}' の内容をご確認の上、承認・否認のご判断をお願い致します。" text_email_to_proceed: 次のURLを開き内容を確認の上、右端のチェックマークをクリックし承認・否認の選択をしてください。 text_email_to_see_history: 次のURLで承認ワークフローの履歴を確認することができます。 text_email_to_see_status: 次のURLで承認ワークフローのステータスを確認することができます。 label_my_open_approvals: 自分の承認待ち label_my_locked_documents: 自分のロック中ファイル - - title_create_link: シンボリックリンクを作成します + + title_create_link: シンボリックリンクを作成します label_link_from: リンク生成(from) label_link_to: リンク生成(to) label_notifications_on: 通知オン label_notifications_off: 通知オフ field_target_file: リンク元ファイル title_download_entries: ダウンロード記録 + label_external: 外部 + label_link_name: リンク名 label_link_external_url: URL label_target_folder: リンク先フォルダ label_source_folder: リンク元フォルダ label_target_project: リンク先プロジェクト label_source_project: リンク元プロジェクト - label_external: 外部 - - text_email_doc_updated_subject: "プロジェクト'%{project}'のファイルが更新されました" + + text_email_doc_updated_subject: プロジェクトのファイルが更新されました text_email_doc_updated: が次のファイルを更新しました。 text_email_doc_follows: 対象ファイル: - text_email_doc_deleted_subject: "プロジェクト'%{project}'のファイルが削除されました" + text_email_doc_deleted_subject: プロジェクトのファイルが削除されました text_email_doc_deleted: が次のプロジェクトのファイルを削除しました。 label_links_only: リンクのみ - + label_display_notified_recipients: 通知受信者の表示 note_display_notified_recipients: 電子メールで通知したすべてのユーザの情報を表示します。 warning_email_notifications: "%{to} へ電子メールで通知しました" - + link_trash_bin: ゴミ箱 title_restore: 復元 notice_dmsf_file_restored: ファイルを復元しました @@ -320,18 +315,20 @@ ja: notice_dmsf_link_restored: リンクを復元しました title_restore_checked: チェックしたものを復元します error_parent_folder: "親フォルダが存在しません" - + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked error_parent_locked: Unable to complete lock - resource parent is locked error_resource_locked: Unable to complete lock - resource is locked - error_lock_exclusively: unable to lock exclusively an already-locked resource - error_unlock_parent_locked: Unlock failed - resource parent is locked - - my: - blocks: - locked_documents: ロック中 - open_approvals: 未承認 - + error_lock_exclusively: Unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked + + field_dmsf_tree_view: Navigate folders in a tree + label_dmsf_version: バージョン + + locked_documents: ロック中 + open_approvals: 未承認 + label_maximum_ajax_upload_filesize: アップロードファイルサイズ上限 note_maximum_ajax_upload_filesize: アップロード可能なファイルサイズの上限。AjaxおよびRedmineの仕様に制限される(2ギガバイト程度までは確認済み)単位はMB。 - + label_classic: Classic + label_drag_drop: "Drag&Drop" diff --git a/config/locales/pl.yml b/config/locales/pl.yml index e2d6c5e4..3e8b56b6 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -1,12 +1,11 @@ # encoding: utf-8 -# +# # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # Polish translation created by Sebastian Białas www.bs-it.pl -# # 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 @@ -27,20 +26,20 @@ pl: label_dmsf_file_revision_plural: Document revisions label_dmsf_file_revision_access_plural: Document accesses warning_no_entries_selected: Nie zaznaczono żadnych wierszy - error_email_to_must_be_entered: Musisz podać adres email + error_email_to_must_be_entered: Musisz podać adres email warning_file_already_locked: Plik jest już zablokowany notice_file_locked: Plik zablokowany warning_file_not_locked: Plik nie zablokowany notice_file_unlocked: Plik odblokowany - error_only_user_that_locked_file_can_unlock_it: Plik może zostać odblokowany jedynie przez użytkownika, który go zablokował - error_max_files_exceeded: "Limit %{number} jedocześnie pobieranych plików został przekroczony" + error_only_user_that_locked_file_can_unlock_it: Plik może zostać odblokowany jedynie przez użytkownika, który go zablokował + error_max_files_exceeded: "Limit %{number} jedocześnie pobieranych plików został przekroczony" error_entry_project_does_not_match_current_project: "Podany projekt nie odpowiada obecnemu projektowy" notice_folder_created: Folder został utworzony error_folder_creation_failed: Błąd podczas tworzenia folderu error_folder_title_must_be_entered: Musisz podać tytuł notice_folder_deleted: Folder został usunięty error_folder_is_not_empty: Folder zawiera pliki - error_folder_title_is_already_used: Podany tytuł jest już w użyciu + error_folder_title_is_already_used: Podany tytuł jest już w użyciu notice_folder_details_were_saved: Szczegóły folderu zostały zapisane error_folder_is_locked: Folder jest zablokowany error_file_is_locked: Plik jest zablokowany @@ -82,7 +81,7 @@ pl: title_waiting_for_approval: Oczekiwanie na akceptację title_approved: Zaakceptowany title_unlock_file: Odblokuj aby umożliwić wprowadzanie zmian innym użytkownikom - title_lock_file: Zablokuj aby zabezpieczyć przed wprowadzaniem zmian przez innych użytkowników + title_lock_file: Zablokuj aby zabezpieczyć przed wprowadzaniem zmian przez innych użytkowników title_download_checked: Pobierz zaznaczone jako archiwum Zip title_send_checked_by_email: Wyślij zaznaczone przez email link_user_preferences: Preferencje DMSF dla projektu @@ -95,11 +94,7 @@ pl: label_email_body: Treść label_email_send: Wyślij title_notifications_active: Powiadomienia aktywne - label_file_size: Rozmiar pliku - heading_file_upload: Prześlij - note_uploaded_maximum_files_at_once: "Można przesłać maksymalnie %{number} plików jednocześnie." - note_upload_files_greater_than_two_gb: Aby przesyłać pliki większe niż 2GB musisz posiadać 64bitową przeglądarkę. - submit_upload_files: Prześlij + label_upload: Prześlij heading_new_folder: Nowy Folder label_title: Tytuł label_description: Opis @@ -109,7 +104,7 @@ pl: select_option_default: Domyślny select_option_deactivated: Wyłączono label_title_format: Title format - text_title_format: "Document title format for download (%t - title, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" + text_title_format: "Document title format for download (%t - title, %f - file, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" select_option_activated: Aktywowano title_save_preferences: Zapisz ustawienia heading_revisions: Wersje @@ -118,7 +113,7 @@ pl: label_created: Utworzono label_changed: Zmieniono info_changed_by_user: "%{changed} przez" - label_filename: Nazwa pliku + label_filename: Nazwa pliku label_mime: Mime label_size: Rozmiar heading_new_revision: Nowa wersja @@ -168,15 +163,15 @@ pl: error_user_has_not_right_delete_folder: "Użytkownik nie posiada uprawnień do usuwania folderów" error_user_has_not_right_delete_file: "Użytkownik nie posiada uprawnień do usuwania plików" notice_entries_deleted: Wpisy usunięte - warning_some_entries_were_not_deleted: "Niektóre wpisy nie zostały usunięte: %{entries}" + warning_some_entries_were_not_deleted: "Niektóre wpisy nie zostały usunięte: %{entries}" title_delete_checked: Usuń zaznaczone title_items: items title_filename_for_download: Nazwa pliku używana do pobierania lub tworzenai archiwum Zip label_number_of_folders: Foldery - label_number_of_documents: Dokumenty + label_number_of_documents: Dokumenty error_file_storage_directory_does_not_exist: "Folder przechowywania plików nie istnieje i nie może zostać utworzony" error_file_can_not_be_created: "Plik nie może zostać utworzony w folderze przechowywania" - error_wrong_zip_encoding: Złe kodowanie Zip + error_wrong_zip_encoding: Złe kodowanie Zip warning_xapian_not_available: Xapian niedostępny menu_dmsf: DMSF label_physical_file_delete: Fizyczne usuwanie plików @@ -204,7 +199,7 @@ pl: title_copy: Kopiuj error_folder_cannot_be_copied: "Folder nie może zostać skopiowany" notice_folder_copied: Folder został skopiowany - + error_max_email_filesize_exceeded: "Maksymalny rozmiar pliku załącznika email został przekroczony. (%{number} MB)" note_maximum_email_filesize: Maksymalny rozmiar pliku, który może zostać wysłany przez email. 0 oznacza brak ograniczeń. Rozmiar w MB. label_maximum_email_filesize: Maksymalny rozmiar załącznika email @@ -220,24 +215,24 @@ pl: warning_folder_not_locked: Niestety folder nie może zostać zablokowany notice_folder_unlocked: Folder został odblokowany error_only_user_that_locked_folder_can_unlock_it: Nie posiadasz uprawnień do odblokowania tego folderu - title_folder_parent_locked: "Folder nadrzędny %{name} jest zablokowany" title_unlock_folder: Odblokuj w celu umożliwienia wprowadzania zmian innym użytkownikom title_lock_folder: Zablokuj aby zabezpieczyć przed wprowadzaniem zmian przez innych użytkowników select_option_webdav_readonly: Tylko do odczytu select_option_webdav_readwrite: Odczyt/Zapis label_webdav_strategy: Webdav strategy - note_webdav_strategy: Enables the administrator to decide if webdav is a read-only or read-write platform for end users. - + note_webdav_strategy: Enables the administrator to decide if webdav is a read-only or read-write platform for end users. + error_unable_delete_dmsf_workflow: Nie można usunąć procesu workflow error_empty_note: "Notatka nie może być pusta" error_workflow_assign: Wystąpił błąd podczas przydzielania error_cannot_start_workflow: "Workflow nie może zostać uruchomiony" error_cannot_renumber_steps: "Kroki nie mogą zostać przenumerowane" - label_dmsf_workflow_new: Nowy proces akceptacji + label_dmsf_workflow_new: Nowy proces akceptacji field_label_dmsf_workflow: Proces akceptacji field_label_dmsf_workflow_name: Nazwa procesu akceptacji label_dmsf_workflow_plural: Procesy akceptacji + label_dmsf_workflow_plural_num: Procesy akceptacji (%{count}) label_dmsf_workflow_step: Krok label_dmsf_workflow_step_plural: Kroki label_dmsf_workflow_approval: Akceptacja @@ -251,42 +246,42 @@ pl: label_dmsf_workflow_add_approver: "Dodaj nowego akceptującego z warunkiem logicznym:" label_or: lub label_action: Akcja - label_note: Notatka + label_note: Notatka title_none: Brak title_rejection: Odrzucenie title_delegation: Delegacja title_assignment: Przydział - title_start: Start + title_start: Start title_dmsf_workflow_log: Historia procesu akceptacji title_assigned: Przydzielony title_approval: Akceptacja title_rejected: Odrzucony dmsf_and: AND dmsf_or: OR - dmsf_new_step: Nowy krok + dmsf_new_step: Nowy krok message_dmsf_wokflow_note: Twoja notatka... info_revision: "r%{rev}" link_workflow: Proces akceptacji - notice_workflow_started: Proces akceptacji został uruchomiony - text_email_subject_approved: "Proces akceptacji %{name} został zakończony akceptacją" - text_email_subject_rejected: "Proces akceptacji %{name} został odrzucony" - text_email_subject_delegated: "Proces akceptacji %{name} został delegowany" - text_email_subject_requires_approval: "Proces akceptacji %{name} wymaga Twojej akceptacji" - text_email_subject_updated: "Proces akceptacji %{name} został zaktualizowany" - text_email_subject_started: "Proces akceptacji %{name} został uruchomiony" + notice_workflow_started: Proces akceptacji został uruchomiony + text_email_subject_approved: został zakończony akceptacją + text_email_subject_rejected: został odrzucony + text_email_subject_delegated: został delegowany + text_email_subject_requires_approval: wymaga Twojej akceptacji + text_email_subject_updated: został zaktualizowany + text_email_subject_started: został uruchomiony text_email_finished_approved: "Proces akceptacji '%{name}' dokumentu '%{filename}' został właśnie zakończony. Dokument został zaakceptowany." text_email_finished_rejected: "Proces akceptacji '%{name}' dokumentu '%{filename}' został właśnie zakończony. Dokument został odrzucony z powodu '%{notice}'." text_email_finished_delegated: "Proces akceptacji '%{name}' dokumentu '%{filename}' został właśnie delegowany z powodu '%{notice}'. Zostałeś wskazany jako akceptujący w bieżącym kroku zatwierdzania." text_email_finished_step: "Zakończono krok w procesie akceptacji '%{name}' dokumentu '%{filename}'. Jesteś kolejną osobą decyzyjną w procesie akceptacji." text_email_finished_step_short: "Zakończono krok w procesie akceptacji '%{name}' dokumentu '%{filename}'." - text_email_started: "Proces akceptacji '%{name}' dokuentu '%{filename}' został uruchomiony. Jesteś osobą akceptującą w bieżącym kroku zatwierdzania." + text_email_started: "Proces akceptacji '%{name}' dokuentu '%{filename}' został uruchomiony. Jesteś osobą akceptującą w bieżącym kroku zatwierdzania." text_email_to_proceed: Aby procedować zaznacz check box przy dokumencie text_email_to_see_history: Aby zobaczyć historię akceptacji kliknij w proces akceptacji dokumentu text_email_to_see_status: Aby zobaczyć aktualny stan procesu akceptacji kliknij w proces akceptacji dokumentu label_my_open_approvals: Otwarte akceptacje label_my_locked_documents: Zablokowane dokumenty - - title_create_link: Utwórz symbolic link + + title_create_link: Utwórz symbolic link label_link_from: Odnośnik z label_link_to: Odnośnik do label_notifications_on: Powiadomienia włączone @@ -294,25 +289,25 @@ pl: field_target_file: Plik źródłowy title_download_entries: Pobrane label_external: External - + label_link_name: Nazwa odnośnika label_link_external_url: URL label_target_folder: Folder docelowy label_source_folder: Folder źródłowy label_target_project: Projekt docelowy - label_source_project: Projekt źródłowy - - text_email_doc_updated_subject: "Dokumenty projektu %{project} zostały zaktualizowane" + label_source_project: Projekt źródłowy + + text_email_doc_updated_subject: Dokumenty zostały zaktualizowane text_email_doc_updated: dokumenty zostały zaktualizowane text_email_doc_follows: następujące - text_email_doc_deleted_subject: "Dokumenty projektu %{project} zostały usunięte" + text_email_doc_deleted_subject: Dokumenty zostały usunięte text_email_doc_deleted: dokumenty zostały usunięte label_links_only: odnośniki - + label_display_notified_recipients: Wyświetl odbiorców powiadomienia note_display_notified_recipients: Użytkownik zostanie poinformowany o wszystkich odbiorcach wysłanego powiadomienia email. warning_email_notifications: "Powiadomienie email zostało wysłane do %{to}" - + link_trash_bin: Kosz title_restore: Przywróć notice_dmsf_file_restored: Dokument został przywrócony @@ -320,17 +315,20 @@ pl: notice_dmsf_link_restored: Link został przywrócony title_restore_checked: Restore checked error_parent_folder: "Folder nadrzędny nie istnieje" - + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked error_parent_locked: Unable to complete lock - resource parent is locked error_resource_locked: Unable to complete lock - resource is locked - error_lock_exclusively: unable to lock exclusively an already-locked resource + error_lock_exclusively: Unable to lock exclusively an already-locked resource error_unlock_parent_locked: Unlock failed - resource parent is locked - - my: - blocks: - locked_documents: Dokumenty zablokowane - openap_provals: Otwarte procesy akceptacji - + + field_dmsf_tree_view: Navigate folders in a tree + label_dmsf_version: Wersja + + locked_documents: Dokumenty zablokowane + open_approvals: Otwarte procesy akceptacji + label_maximum_ajax_upload_filesize: Maximum file size uploadable via AJAX - note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB. \ No newline at end of file + note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB. + label_classic: Classic + label_drag_drop: "Drag&Drop" diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index c282ebee..a436ba1a 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -4,7 +4,7 @@ # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -40,7 +40,7 @@ pt-BR: notice_folder_deleted: Pasta deletada error_folder_is_not_empty: Pasta não está vazia error_folder_title_is_already_used: Título já utilizado - notice_folder_details_were_saved: Pasta atualizada + notice_folder_details_were_saved: Pasta atualizada error_folder_is_locked: Pasta está bloqueada error_file_is_locked: Arquivo está bloqueado notice_file_deleted: Arquivo excluído @@ -94,11 +94,7 @@ pt-BR: label_email_body: Descrição label_email_send: Enviar title_notifications_active: Notificações Ativas - label_file_size: Tamanho do Arquivo - heading_file_upload: Envio - note_uploaded_maximum_files_at_once: "Não pode ser carregado o máximo de %{number} arquivo de apenas uma vez." - note_upload_files_greater_than_two_gb: Para fazer o upload maiores que 2GB você deve possuir sistema 64b. - submit_upload_files: Envio + label_upload: Envio heading_new_folder: Nova Pasta label_title: Taxonomia label_description: Descrição @@ -109,7 +105,7 @@ pt-BR: select_option_deactivated: Desativado select_option_activated: Ativado label_title_format: Title format - text_title_format: "Document title format for download (%t - title, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" + text_title_format: "Document title format for download (%t - title, %f - file, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" title_save_preferences: Salvar preferências heading_revisions: Revisões title_download: Download @@ -219,7 +215,6 @@ pt-BR: warning_folder_not_locked: A pasta não pode ser bloqueada notice_folder_unlocked: A pasta foi desbloqueada com sucesso error_only_user_that_locked_folder_can_unlock_it: Você não está autorizado a desbloquear esta pasta - title_folder_parent_locked: "A pasta %{name} está bloqueada" title_unlock_folder: Clique aqui para desbloquear e permitir alterações por outros usuários title_lock_folder: Clique aqui para impedir alterações por outros usuários @@ -268,12 +263,12 @@ pt-BR: info_revision: "r%{rev}" link_workflow: Workflow notice_workflow_started: Workflow de aprovação foi iniciado com êxito - text_email_subject_approved: "Workflow de aprovação %{name}: aprovado" - text_email_subject_rejected: "Workflow de aprovação %{name} reprovado" - text_email_subject_delegated: "Workflow de aprovação %{name} atribuído para" - text_email_subject_requires_approval: "Workflow de aprovação %{name} requer sua aprovação" - text_email_subject_updated: "Workflow de aprovação %{name}: atualizado" - text_email_subject_started: "Workflow de aprovação %{name}: iniciado" + text_email_subject_approved: aprovado + text_email_subject_rejected: reprovado + text_email_subject_delegated: atribuído para + text_email_subject_requires_approval: requer sua aprovação + text_email_subject_updated: atualizado + text_email_subject_started: iniciado text_email_finished_approved: "O workflow de aprovação'%{name}' definido para o documento '%{filename}' foi finalizado e o documento foi aprovado." text_email_finished_rejected: "O workflow de aprovação '%{name}' definido para o documento '%{filename}' foi finalizado e o documento foi reprovado devido a '%{notice}'." text_email_finished_delegated: "O workflow de aprovação '%{name}' definido para o documento '%{filename}' foi atribuido pois '%{notice}' e está aguardando a sua aprovação na etapa atual." @@ -302,10 +297,10 @@ pt-BR: label_target_project: Target project label_source_project: Source project - text_email_doc_updated_subject: "%{project} : Documentos atualizados" + text_email_doc_updated_subject: Documentos atualizados text_email_doc_updated: atualizou os documentos da área text_email_doc_follows: as follows - text_email_doc_deleted_subject: "%{project} : Exclusão de documentos" + text_email_doc_deleted_subject: Exclusão de documentos text_email_doc_deleted: deletou os documentos da área label_links_only: links only @@ -320,17 +315,20 @@ pt-BR: notice_dmsf_link_restored: O link foi restaurado com sucesso title_restore_checked: Restaurar pasta ou arquivo error_parent_folder: "A pasta de pai não existe" - + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked error_parent_locked: Unable to complete lock - resource parent is locked error_resource_locked: Unable to complete lock - resource is locked - error_lock_exclusively: unable to lock exclusively an already-locked resource - error_unlock_parent_locked: Unlock failed - resource parent is locked + error_lock_exclusively: Unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked - my: - blocks: - locked_documents: Documentos bloqueados - open_approvals: Aprovações abertas + field_dmsf_tree_view: Navigate folders in a tree + label_dmsf_version: Versão + + locked_documents: Documentos bloqueados + open_approvals: Aprovações abertas label_maximum_ajax_upload_filesize: Maximum file size uploadable via AJAX - note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB. \ No newline at end of file + note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB. + label_classic: Classic + label_drag_drop: "Drag&Drop" diff --git a/config/locales/ru.yml b/config/locales/ru.yml index d884ecd4..e282cf65 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -1,9 +1,9 @@ # encoding: utf-8 -# +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011-16 Karel Pičman # # # This program is free software; you can redistribute it and/or @@ -26,13 +26,13 @@ ru: label_dmsf_file_revision_plural: Изменения документов label_dmsf_file_revision_access_plural: Доступы к документам warning_no_entries_selected: Файлы не выбраны - error_email_to_must_be_entered: Нужно указать, на какую почту отправить письмо + error_email_to_must_be_entered: Нужно указать, на какую почту отправить письмо warning_file_already_locked: Файл уже заблокирован notice_file_locked: Файл заблокирован warning_file_not_locked: Файл не заблокирован notice_file_unlocked: Файл разблокирован - error_only_user_that_locked_file_can_unlock_it: Только пользователь, который заблокировал файл, может его разблокировать - error_max_files_exceeded: "Ограничение для %{number} одновременно загружаемых файлов превышено" + error_only_user_that_locked_file_can_unlock_it: Только пользователь, который заблокировал файл, может его разблокировать + error_max_files_exceeded: "Ограничение для %{number} одновременно загружаемых файлов превышено" error_entry_project_does_not_match_current_project: Проект, которому принадлежит файл, не соответсвует текущему проекту notice_folder_created: Папка создана error_folder_creation_failed: Папку не удалось создать @@ -40,7 +40,7 @@ ru: notice_folder_deleted: Папка удалена error_folder_is_not_empty: Папка не пустая error_folder_title_is_already_used: Название папки уже используется - notice_folder_details_were_saved: Описание папки было сохранено + notice_folder_details_were_saved: Описание папки было сохранено error_folder_is_locked: Папка заблокированa error_file_is_locked: Файл заблокирован notice_file_deleted: Файл удален @@ -81,8 +81,8 @@ ru: title_waiting_for_approval: Ожидается на утверждение title_approved: Утверждено title_unlock_file: Разблокируйте файл, чтобы разрешить изменение его другими участниками - title_lock_file: Заблокируйте файл, чтобы запретить его изменение другими участниками - title_download_checked: Скачать выбранные файлы + title_lock_file: Заблокируйте файл, чтобы запретить его изменение другими участниками + title_download_checked: Скачать выбранные файлы title_send_checked_by_email: Отправить выбранные файлы по электронной почте link_user_preferences: Ваши настройки DMSF проекта heading_send_documents_by_email: Отправить документы по электронной почте @@ -94,11 +94,7 @@ ru: label_email_body: Содержание label_email_send: Отправить title_notifications_active: Уведомления активны - label_file_size: Размер файла - heading_file_upload: Закачать - note_uploaded_maximum_files_at_once: "За один раз можно загрузить только %{number} файлов." - note_upload_files_greater_than_two_gb: Чтобы загружать файлы размером больше чем 2 Гб у вас должен быть 64-битный браузер. - submit_upload_files: Загрузить + label_upload: Закачать heading_new_folder: Новая папка label_title: Заголовок label_description: Описание @@ -109,7 +105,7 @@ ru: select_option_deactivated: Отключено select_option_activated: Включено label_title_format: Title format - text_title_format: "Document title format for download (%t - title, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" + text_title_format: "Document title format for download (%t - title, %f - file, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" title_save_preferences: Сохранить настройки heading_revisions: Редакции title_download: Скачать @@ -117,9 +113,9 @@ ru: label_created: Создан label_changed: Изменен info_changed_by_user: "%{changed} пользователем" - label_filename: Имя файла + label_filename: Имя файла label_mime: MIME-тип - label_size: Размер + label_size: Размер heading_new_revision: Новая редакция option_version_same: Та же версия option_version_minor: Незначительные изменения @@ -154,7 +150,7 @@ ru: permission_user_preferences: Настройки пользователя permission_view_dmsf_files: Просматривать документы permission_folder_manipulation: Управление папками - permission_file_manipulation: Управление файлами + permission_file_manipulation: Управление файлами permission_force_file_unlock: Разблокировка любых файлов permission_manage_workflows: Управление согласованиями permission_file_delete: Удаление документов @@ -167,7 +163,7 @@ ru: error_user_has_not_right_delete_folder: Пользователь не имеет нужных прав для удаления папки error_user_has_not_right_delete_file: Пользователь не имеет нужных прав для удаления файла notice_entries_deleted: Файлы удалены - warning_some_entries_were_not_deleted: "Некоторые файлы не были удалены: %{entries}" + warning_some_entries_were_not_deleted: "Некоторые файлы не были удалены: %{entries}" title_delete_checked: Удалить выбранные документы title_items: элементы title_filename_for_download: Имя файла для скачиваемого архива @@ -219,7 +215,6 @@ ru: warning_folder_not_locked: "К сожалению, папка не может быть заблокирована" notice_folder_unlocked: "Папка была успешно разблокирована" error_only_user_that_locked_folder_can_unlock_it: "Только пользователь, который заблокировал папку, может её разблокировать" - title_folder_parent_locked: "Родительская папка %{name} заблокирована" title_unlock_folder: "Разблокируйте папку, чтобы разрешить изменение её другими участниками" title_lock_folder: "Заблокируйте папку, чтобы запретить ёё изменение другими участниками" @@ -227,7 +222,7 @@ ru: select_option_webdav_readwrite: "Чтение/Запись" label_webdav_strategy: "Стратегия WebDAV" note_webdav_strategy: "Позволяет администратору решить в каком режиме предоставить доступ к WebDAV для конечных пользователей (Только для чтения или Чтение+Запись)." - + error_unable_delete_dmsf_workflow: Невозможно удалить процесс согласование error_empty_note: Примечание не может быть пустым error_workflow_assign: Возникла ошибка при назначении @@ -263,30 +258,30 @@ ru: title_rejected: Отклонен dmsf_and: AND dmsf_or: OR - dmsf_new_step: Новый шаг + dmsf_new_step: Новый шаг message_dmsf_wokflow_note: Ваше примечание... info_revision: "r%{rev}" - link_workflow: Согласование + link_workflow: Согласование notice_workflow_started: Процесс согласования успешно запущен - text_email_subject_approved: "Процесс согласования %{name} успешно завершен" - text_email_subject_rejected: "Процесс согласования %{name} отклонен" - text_email_subject_delegated: "Процесс согласования %{name} делегирован" - text_email_subject_requires_approval: "Процесс согласования %{name} требует вашего участия" - text_email_subject_updated: "Процесс согласования %{name} обновлен" - text_email_subject_started: "Процесс согласования %{name} запущен" + text_email_subject_approved: успешно завершен + text_email_subject_rejected: отклонен + text_email_subject_delegated: делегирован + text_email_subject_requires_approval: ребует вашего участия + text_email_subject_updated: обновлен + text_email_subject_started: запущен text_email_finished_approved: "Процесс согласования '%{name}' документа '%{filename}' только что завершился и документ был согласован." text_email_finished_rejected: "Процесс согласования '%{name}' документа '%{filename}' только что завершился и документ был отклонен по причине '%{notice}'." text_email_finished_delegated: "Процесс согласования '%{name}' документа '%{filename}' только что был делегирован по причине '%{notice}' и от Вас ожидается согласование." text_email_finished_step: "Процесс согласования '%{name}' документа '%{filename}' только что завершил один из шагов согласования и ожидает Вашего соглсования." text_email_finished_step_short: "Процесс согласования '%{name}' документа '%{filename}' только что завершил один из шагов согласования." - text_email_started: "Процесс согласования '%{name}' документа '%{filename}' только что начался и ожидает Вашего соглсования." - text_email_to_proceed: Для продолжения поставьте отметку рядом с документом в - text_email_to_see_history: Для просмотра истории согласования нажмите статус согласования документа в - text_email_to_see_status: Для просмотра текущего статуса согласования нажмите статус согласования документа в + text_email_started: "Процесс согласования '%{name}' документа '%{filename}' только что начался и ожидает Вашего соглсования." + text_email_to_proceed: Для продолжения поставьте отметку рядом с документом в + text_email_to_see_history: Для просмотра истории согласования нажмите статус согласования документа в + text_email_to_see_status: Для просмотра текущего статуса согласования нажмите статус согласования документа в label_my_open_approvals: Мои согласования label_my_locked_documents: Мои заблокированные документы - - title_create_link: Создать символическую ссылку + + title_create_link: Создать символическую ссылку label_link_from: Ссылка из label_link_to: Ссылка на label_notifications_on: Включить уведомления @@ -294,25 +289,25 @@ ru: field_target_file: Исходный файл title_download_entries: Скачать записи label_external: Внешний - + label_link_name: Наименование ссылки label_link_external_url: URL label_target_folder: Целевая папка label_source_folder: Исходная папка label_target_project: Целевой проект - label_source_project: Исходный проект - - text_email_doc_updated_subject: "Документы проекта %{project} обновлены" + label_source_project: Исходный проект + + text_email_doc_updated_subject: Документы обновлены text_email_doc_updated: только что обновил документы text_email_doc_follows: следующим образом - text_email_doc_deleted_subject: "Документы проекта %{project} удалены" + text_email_doc_deleted_subject: Документы удалены text_email_doc_deleted: только что удалил документы label_links_only: только ссылки - + label_display_notified_recipients: Показывать получателей, получивших уведомление note_display_notified_recipients: Пользователь будет проинфмораирован о всех получателях, кому было направлено уведомление. warning_email_notifications: "Уведомление отправлено %{to}" - + link_trash_bin: Корзина title_restore: Восстановить notice_dmsf_file_restored: Документ был успешно восстановлен @@ -320,17 +315,20 @@ ru: notice_dmsf_link_restored: Папка была успешно восстановлена title_restore_checked: Восстановить отмеченные error_parent_folder: "Родительская папка не существует" - + error_resource_or_parent_locked: Невозможно выполнить блокировку - ресурс (или родительская запись) заблокированы error_parent_locked: Невозможно выполнить блокировку - родительская запись заблокирована error_resource_locked: Невозможно выполнить блокировку - ресурс заблокирован - error_lock_exclusively: невозможно эксклюзивно заблокировать уже забловированный ресурс + error_lock_exclusively: Невозможно эксклюзивно заблокировать уже забловированный ресурс error_unlock_parent_locked: Разблокировка не удалась - родительская запись заблокирована - - my: - blocks: - locked_documents: Заблокированные документы - open_approvals: Открытые согласования - + + field_dmsf_tree_view: Navigate folders in a tree + label_dmsf_version: Версия + + locked_documents: Заблокированные документы + open_approvals: Открытые согласования + label_maximum_ajax_upload_filesize: Максимальный размер файла, загружаемого посредством AJAX note_maximum_ajax_upload_filesize: Превышает максимальный размер файла, загружаемого посредством интерфейса AJAX, для загрузки можно использовать стандартную форму Redmine. Размер указан в MB. + label_classic: Classic + label_drag_drop: "Drag&Drop" diff --git a/config/locales/sl.yml b/config/locales/sl.yml index b29b5583..cf71701b 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -1,9 +1,9 @@ # encoding: utf-8 -# +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011 Zdravko Balorda +# Copyright (C) 2011-16 Karel Pičman # # # This program is free software; you can redistribute it and/or @@ -26,13 +26,13 @@ sl: label_dmsf_file_revision_plural: Document revisions label_dmsf_file_revision_access_plural: Document accesses warning_no_entries_selected: Ničesar niste izbrali - error_email_to_must_be_entered: Email Naslovnik mora bit izbran + error_email_to_must_be_entered: Email Naslovnik mora bit izbran warning_file_already_locked: Datoteka že zaklenjena notice_file_locked: Datoteka zaklenjena warning_file_not_locked: Datoteka ni zaklenjena notice_file_unlocked: Datoteka odklenjena - error_only_user_that_locked_file_can_unlock_it: Samo oseba, ki je zaklenila datoteko, jo lahko odklene. - error_max_files_exceeded: "Max %{number} datotek za istočasno nalaganje je preseženo." + error_only_user_that_locked_file_can_unlock_it: Samo oseba, ki je zaklenila datoteko, jo lahko odklene. + error_max_files_exceeded: "Max %{number} datotek za istočasno nalaganje je preseženo." error_entry_project_does_not_match_current_project: Projekt se ujema s trenutno nastavljenim projektom notice_folder_created: Mapa kreirana error_folder_creation_failed: Mape ne morem kreirati @@ -40,7 +40,7 @@ sl: notice_folder_deleted: Mapa izbrisana error_folder_is_not_empty: Mapa ni prazna error_folder_title_is_already_used: Naslov je že uporabljen - notice_folder_details_were_saved: Podatki o mapi so shranjeni + notice_folder_details_were_saved: Podatki o mapi so shranjeni error_folder_is_locked: Папка заблокированa error_file_is_locked: Mapa je zaklenjena notice_file_deleted: Datoteka izbrisana @@ -81,8 +81,8 @@ sl: title_waiting_for_approval: V postopku odobritve title_approved: Odobreno title_unlock_file: Odkleni drugim članom za posodabljanje - title_lock_file: Zakleni za posodabljanje - title_download_checked: Prenesi izbrano v Zip formatu + title_lock_file: Zakleni za posodabljanje + title_download_checked: Prenesi izbrano v Zip formatu title_send_checked_by_email: Pošlji izbrano po elektronski pošti link_user_preferences: Vaše Arhivske nastavitve za projekt heading_send_documents_by_email: Pošlji dokumente po elektronski pošti @@ -94,11 +94,7 @@ sl: label_email_body: Vsebina label_email_send: Pošlji title_notifications_active: Obveščanje aktivno - label_file_size: Velikost datoteke - heading_file_upload: Naloži - note_uploaded_maximum_files_at_once: "Naenkrat lahko prenesete največ %{number} datotek." - note_upload_files_greater_than_two_gb: Za datoteke večje od 2GB rabite 64bitni brskalnik. - submit_upload_files: Naloži + label_upload: Naloži heading_new_folder: Nova mapa label_title: Naziv mape label_description: Opis @@ -109,7 +105,7 @@ sl: select_option_deactivated: Deaktivirano select_option_activated: Aktivirano label_title_format: Title format - text_title_format: "Document title format for download (%t - title, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" + text_title_format: "Document title format for download (%t - title, %f - file, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" title_save_preferences: Save preferences heading_revisions: Verzije title_download: Prenesi dol @@ -117,9 +113,9 @@ sl: label_created: Narejeno label_changed: Spremenjeno info_changed_by_user: "%{changed} po" - label_filename: Datoteka + label_filename: Datoteka label_mime: Mime - label_size: Velikost + label_size: Velikost heading_new_revision: Nova verzija option_version_same: Enako option_version_minor: Minor @@ -154,7 +150,7 @@ sl: permission_user_preferences: Uporabniške nastavitve permission_view_dmsf_files: Preglej dokumente permission_folder_manipulation: Upravljanje z mapami - permission_file_manipulation: Upravljanje z datotekami + permission_file_manipulation: Upravljanje z datotekami permission_force_file_unlock: Prisilno odkleni datoteko permission_manage_workflows: Manage workflows permission_file_delete: Delete documents @@ -167,7 +163,7 @@ sl: error_user_has_not_right_delete_folder: Uporabnik nima privilegija brisati mapo error_user_has_not_right_delete_file: Uporabnik nima privilegija brisati datoteke notice_entries_deleted: Izbrane enote izbrisane - warning_some_entries_were_not_deleted: "Nekatere enote niso izbrisane: %{entries}" + warning_some_entries_were_not_deleted: "Nekatere enote niso izbrisane: %{entries}" title_delete_checked: Izbriši izbrano title_items: items title_filename_for_download: Naziv datoteke za prenos dol ali Zip arhiva @@ -186,7 +182,7 @@ sl: label_dmsf_updated: Arhiv posodobljen label_dmsf_downloaded: Arhiv downloaded title_total_size_of_all_files: Skupna velikost vseh datotek v tej mapi - project_module_dmsf: Arhiv + project_module_dmsf: Arhiv warning_no_project_to_copy_file_to: Ni projekta kamor bi kopiral datoteko comment_copied_from: "Skopirano iz %{source}" notice_file_copied: Datoteka skopirana @@ -203,7 +199,7 @@ sl: title_copy: Kopiraj error_folder_cannot_be_copied: Mapo se ne da skopirati notice_folder_copied: Mapa skopirana - + error_max_email_filesize_exceeded: "Presegli ste največjo velikost datoteke za pošiljanje po email-u. (%{number} MB)" note_maximum_email_filesize: Omejitev največje velikosti datoteke, ki se lahko pošlje po email-u. 0 pomeni neomejeno. Količina je v MB. label_maximum_email_filesize: Največja velikost email priponke @@ -219,7 +215,6 @@ sl: warning_folder_not_locked: Ne, ta mapa ne more biti zaklenjena notice_folder_unlocked: Mapa je uspešno odklenjena error_only_user_that_locked_folder_can_unlock_it: Nimate privilegijev, da bi odklenili to mapo - title_folder_parent_locked: "Nadrejena mapa %{name} je zaklenjena" title_unlock_folder: Odkleni, da bi drugim članom omogočil spreminjanje title_lock_folder: Zakleni, da bi drugim članom preprečil spreminjanje @@ -227,7 +222,7 @@ sl: select_option_webdav_readwrite: "Beri/Piši" label_webdav_strategy: Webdav strategija note_webdav_strategy: "Omogoči administratorju da odloči ali je webdav platforma na voljo izključno za branje ali beri/piši za končne uporabnike." - + error_unable_delete_dmsf_workflow: Unable to delete the workflow error_empty_note: "The note can't be empty" error_workflow_assign: An error occured while assigning @@ -251,42 +246,42 @@ sl: label_dmsf_workflow_add_approver: "Add a new approver with a logical function:" label_or: or label_action: Action - label_note: Note + label_note: Note title_none: None title_rejection: Rejection title_delegation: Delegation title_assignment: Assignment - title_start: Start + title_start: Start title_dmsf_workflow_log: Approval Workflow Log title_assigned: Assigned title_approval: Approval title_rejected: Rejected dmsf_and: AND dmsf_or: OR - dmsf_new_step: New step + dmsf_new_step: New step message_dmsf_wokflow_note: Your note... info_revision: "r%{rev}" - link_workflow: Workflow + link_workflow: Workflow notice_workflow_started: Approval workflow successfully started - text_email_subject_approved: "Approval workflow %{name} approved" - text_email_subject_rejected: "Approval workflow %{name} rejected" - text_email_subject_delegated: "Approval workflow %{name} delegated" - text_email_subject_requires_approval: "Approval workflow %{name} requires your approval" - text_email_subject_updated: "Approval workflow %{name} updated" - text_email_subject_started: "Approval workflow %{name} started" + text_email_subject_approved: approved + text_email_subject_rejected: rejected + text_email_subject_delegated: delegated + text_email_subject_requires_approval: requires your approval + text_email_subject_updated: updated + text_email_subject_started: started text_email_finished_approved: "The approval workflow '%{name}' assigned to '%{filename}' document has just been finished and the document has been approved." text_email_finished_rejected: "The approval workflow '%{name}' assigned to '%{filename}' document has just been finished and the document has been rejected because of '%{notice}'." text_email_finished_delegated: "The approval workflow '%{name}' assigned to '%{filename}' document has just been delegated because of '%{notice}' and you are expected to do an approval in the current approval step." text_email_finished_step: "The approval workflow '%{name}' assigned to '%{filename}' document has just finished one of the approval steps and you are expected to do an approval in the next approval step." text_email_finished_step_short: "The approval workflow '%{name}' assigned to '%{filename}' document has just finished one of the approval steps." - text_email_started: "The approval workflow '%{name}' assigned to '%{filename}' document has just been started and you are expected to do an approval in the current approval step." + text_email_started: "The approval workflow '%{name}' assigned to '%{filename}' document has just been started and you are expected to do an approval in the current approval step." text_email_to_proceed: To proceed click on the check box icon next to the document in text_email_to_see_history: To see the approval history click on the workflow status of the document in text_email_to_see_status: To see the current status of the approval workflow click on the workflow status the document in label_my_open_approvals: My open approvals label_my_locked_documents: My locked documents - - title_create_link: Create a symbolic link + + title_create_link: Create a symbolic link label_link_from: Link from label_link_to: Link to label_notifications_on: Notifications on @@ -294,25 +289,25 @@ sl: field_target_file: Source file title_download_entries: Download entries label_external: External - + label_link_name: Link name label_link_external_url: URL label_target_folder: Target folder label_source_folder: Source folder label_target_project: Target project - label_source_project: Source project - - text_email_doc_updated_subject: "Documents of %{project} updated" + label_source_project: Source project + + text_email_doc_updated_subject: Documents updated text_email_doc_updated: has just actualized documents of text_email_doc_follows: as follows - text_email_doc_deleted_subject: "Documents of %{project} deleted" + text_email_doc_deleted_subject: Documents deleted text_email_doc_deleted: has just deleted documents of label_links_only: links only - + label_display_notified_recipients: Display notified recipients note_display_notified_recipients: The user will be informed about all recipients of just sent the email notification. warning_email_notifications: "Email notifications sent to %{to}" - + link_trash_bin: Trash bin title_restore: Restore notice_dmsf_file_restored: The document has been successfully restored @@ -320,17 +315,20 @@ sl: notice_dmsf_link_restored: The link has been successfully restored title_restore_checked: Restore checked error_parent_folder: "The parent folder doesn't exist" - + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked error_parent_locked: Unable to complete lock - resource parent is locked error_resource_locked: Unable to complete lock - resource is locked - error_lock_exclusively: unable to lock exclusively an already-locked resource - error_unlock_parent_locked: Unlock failed - resource parent is locked - - my: - blocks: - locked_documents: Locked documents - open_approvals: Open approvals - + error_lock_exclusively: Unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked + + field_dmsf_tree_view: Navigate folders in a tree + label_dmsf_version: Verzija + + locked_documents: Locked documents + open_approvals: Open approvals + label_maximum_ajax_upload_filesize: Maximum file size uploadable via AJAX - note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB. \ No newline at end of file + note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB. + label_classic: Classic + label_drag_drop: "Drag&Drop" diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index d15c5227..002c846b 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1,10 +1,10 @@ # encoding: utf-8 -# +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2011-15 Karel Pičman -# Copyright (C) 2013 Aecho Liu +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011-16 Karel Pičman +# Copyright (C) 2013 Aecho Liu # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -32,7 +32,7 @@ zh-TW: warning_file_not_locked: 檔案尚未鎖定 notice_file_unlocked: 檔案己解除鎖定 error_only_user_that_locked_file_can_unlock_it: 只有檔案的鎖定者,才能解除鎖定。 - error_max_files_exceeded: "目前容許同時的檔案下載數量為%{number}個,己經超出此限制了。" + error_max_files_exceeded: "目前容許同時的檔案下載數量為%{number}個,己經超出此限制了。" error_entry_project_does_not_match_current_project: 所指定的專案,和目前的專案不一致。 notice_folder_created: 資料夾己建立 error_folder_creation_failed: 資料夾建立失敗 @@ -40,7 +40,7 @@ zh-TW: notice_folder_deleted: 資料夾己刪除 error_folder_is_not_empty: 資料夾尚未清空 error_folder_title_is_already_used: 標題己經被使用了 - notice_folder_details_were_saved: 資料夾描述己儲存 + notice_folder_details_were_saved: 資料夾描述己儲存 error_folder_is_locked: 資料夾己經鎖定 error_file_is_locked: 檔案己經被鎖定了 notice_file_deleted: 檔案己經被刪除了 @@ -81,8 +81,8 @@ zh-TW: title_waiting_for_approval: 等待批准 title_approved: 已經被批准 title_unlock_file: 解除鎖定。允許其它使用者修改。 - title_lock_file: 鎖定檔案。禁止其它使用者修改。 - title_download_checked: 以ZIP下載所選取的檔案 + title_lock_file: 鎖定檔案。禁止其它使用者修改。 + title_download_checked: 以ZIP下載所選取的檔案 title_send_checked_by_email: 以電子郵件發送所選取的檔案 link_user_preferences: 你的DMSF的系統偏好設定 heading_send_documents_by_email: 電子郵件寄送檔案 @@ -94,11 +94,7 @@ zh-TW: label_email_body: 郵件內容 label_email_send: 寄信囉 title_notifications_active: 通知處於啟用中 - label_file_size: 檔案大小 - heading_file_upload: 上傳 - note_uploaded_maximum_files_at_once: "一次最多,只能上傳%{number}個檔案。" - note_upload_files_greater_than_two_gb: 為了上傳大於 2GB 的檔案,您需要使用64位元版本的瀏覽器。 - submit_upload_files: 上傳 + label_upload: 上傳 heading_new_folder: 新增資料夾 label_title: 標題 label_description: 描述 @@ -109,7 +105,7 @@ zh-TW: select_option_deactivated: 關閉 select_option_activated: 啟用 label_title_format: Title format - text_title_format: "Document title format for download (%t - title, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" + text_title_format: "Document title format for download (%t - title, %f - file, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" title_save_preferences: 儲存偏好設定 heading_revisions: 修訂版本 title_download: 下載 @@ -117,9 +113,9 @@ zh-TW: label_created: 建立者/時間 label_changed: 修改者/時間 info_changed_by_user: "%{changed} by" - label_filename: 檔案名稱 + label_filename: 檔案名稱 label_mime: Mime - label_size: 大小 + label_size: 大小 heading_new_revision: 新增修訂版本 option_version_same: Same option_version_minor: Minor @@ -154,7 +150,7 @@ zh-TW: permission_user_preferences: 使用者偏好設定 permission_view_dmsf_files: 查看文件檔案 permission_folder_manipulation: 資料夾操作 - permission_file_manipulation: 文件操作 + permission_file_manipulation: 文件操作 permission_force_file_unlock: 強制解除檔案鎖定 permission_manage_workflows: Manage workflows permission_file_delete: Delete documents @@ -167,7 +163,7 @@ zh-TW: error_user_has_not_right_delete_folder: 使用者沒有權限,刪除資料夾。 error_user_has_not_right_delete_file: 使用者沒有權限,刪除檔案。 notice_entries_deleted: 項目己刪除 - warning_some_entries_were_not_deleted: "部份項目無法刪除: %{entries}" + warning_some_entries_were_not_deleted: "部份項目無法刪除: %{entries}" title_delete_checked: 刪除選取項目 title_items: items title_filename_for_download: 下載時的檔名,或是ZIP的檔案名稱。 @@ -219,7 +215,6 @@ zh-TW: warning_folder_not_locked: 不好意思,這個資料夾無法被鎖定。 notice_folder_unlocked: 這個資料夾己經成功地解除鎖定了。 error_only_user_that_locked_folder_can_unlock_it: 您未被授權,解除這個資料夾的鎖定狀態。 - title_folder_parent_locked: "上層資料夾 %{name} 被鎖定了。" title_unlock_folder: 解除鎖定。允許其它使用者修改。 title_lock_folder: 鎖定資料夾。禁止其它使用者修改。 @@ -251,42 +246,42 @@ zh-TW: label_dmsf_workflow_add_approver: "Add a new approver with a logical function:" label_or: or label_action: Action - label_note: Note + label_note: Note title_none: None title_rejection: Rejection title_delegation: Delegation title_assignment: Assignment - title_start: Start + title_start: Start title_dmsf_workflow_log: 批准流程Log title_assigned: Assigned title_approval: Approval title_rejected: Rejected dmsf_and: AND dmsf_or: OR - dmsf_new_step: New step + dmsf_new_step: New step message_dmsf_wokflow_note: Your note... info_revision: "r%{rev}" - link_workflow: Workflow + link_workflow: Workflow notice_workflow_started: Approval workflow successfully started - text_email_subject_approved: "Approval workflow %{name} approved" - text_email_subject_rejected: "Approval workflow %{name} rejected" - text_email_subject_delegated: "Approval workflow %{name} delegated" - text_email_subject_requires_approval: "Approval workflow %{name} requires your approval" - text_email_subject_updated: "Approval workflow %{name} updated" - text_email_subject_started: "Approval workflow %{name} started" + text_email_subject_approved: approved + text_email_subject_rejected: rejected + text_email_subject_delegated: delegated + text_email_subject_requires_approval: requires your approval + text_email_subject_updated: updated + text_email_subject_started: started text_email_finished_approved: "The approval workflow '%{name}' assigned to '%{filename}' document has just been finished and the document has been approved." text_email_finished_rejected: "The approval workflow '%{name}' assigned to '%{filename}' document has just been finished and the document has been rejected because of '%{notice}'." text_email_finished_delegated: "The approval workflow '%{name}' assigned to '%{filename}' document has just been delegated because of '%{notice}' and you are expected to do an approval in the current approval step." text_email_finished_step: "The approval workflow '%{name}' assigned to '%{filename}' document has just finished one of the approval steps and you are expected to do an approval in the next approval step." text_email_finished_step_short: "The approval workflow '%{name}' assigned to '%{filename}' document has just finished one of the approval steps." - text_email_started: "The approval workflow '%{name}' assigned to '%{filename}' document has just been started and you are expected to do an approval in the current approval step." + text_email_started: "The approval workflow '%{name}' assigned to '%{filename}' document has just been started and you are expected to do an approval in the current approval step." text_email_to_proceed: To proceed click on the check box icon next to the document in text_email_to_see_history: To see the approval history click on the workflow status of the document in text_email_to_see_status: To see the current status of the approval workflow click on the workflow status the document in label_my_open_approvals: My open approvals label_my_locked_documents: My locked documents - title_create_link: Create a symbolic link + title_create_link: Create a symbolic link label_link_from: Link from label_link_to: Link to label_notifications_on: Notifications on @@ -294,25 +289,25 @@ zh-TW: field_target_file: Source file title_download_entries: Download entries label_external: External - + label_link_name: Link name label_link_external_url: URL label_target_folder: Target folder label_source_folder: Source folder label_target_project: Target project - label_source_project: Source project + label_source_project: Source project - text_email_doc_updated_subject: "Documents of %{project} updated" + text_email_doc_updated_subject: Documents updated text_email_doc_updated: has just actualized documents of text_email_doc_follows: as follows - text_email_doc_deleted_subject: "Documents of %{project} deleted" + text_email_doc_deleted_subject: Documents deleted text_email_doc_deleted: has just deleted documents of label_links_only: links only label_display_notified_recipients: Display notified recipients note_display_notified_recipients: The user will be informed about all recipients of just sent the email notification. warning_email_notifications: "Email notifications sent to %{to}" - + link_trash_bin: Trash bin title_restore: Restore notice_dmsf_file_restored: The document has been successfully restored @@ -320,17 +315,20 @@ zh-TW: notice_dmsf_link_restored: The link has been successfully restored title_restore_checked: Restore checked error_parent_folder: "The parent folder doesn't exist" - + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked error_parent_locked: Unable to complete lock - resource parent is locked error_resource_locked: Unable to complete lock - resource is locked - error_lock_exclusively: unable to lock exclusively an already-locked resource - error_unlock_parent_locked: Unlock failed - resource parent is locked + error_lock_exclusively: Unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked - my: - blocks: - locked_documents: Locked documents - open_approvals: Open approvals + field_dmsf_tree_view: Navigate folders in a tree + label_dmsf_version: 版本 + + locked_documents: Locked documents + open_approvals: Open approvals label_maximum_ajax_upload_filesize: Maximum file size uploadable via AJAX - note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB. \ No newline at end of file + note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB. + label_classic: Classic + label_drag_drop: "Drag&Drop" diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 65a18212..d5aac430 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -1,9 +1,9 @@ # encoding: utf-8 -# +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011-16 Karel Pičman # # # This program is free software; you can redistribute it and/or @@ -26,13 +26,13 @@ zh: label_dmsf_file_revision_plural: Document revisions label_dmsf_file_revision_access_plural: Document accesses warning_no_entries_selected: 未选择任何条目 - error_email_to_must_be_entered: 请输入电子邮件 + error_email_to_must_be_entered: 请输入电子邮件 warning_file_already_locked: 文件已经锁定 notice_file_locked: 文件锁定 warning_file_not_locked: 文件未锁定 notice_file_unlocked: 文件解锁 - error_only_user_that_locked_file_can_unlock_it: 只有锁定文件的用户才能解锁该文件 - error_max_files_exceeded: "超出同时下载%{number}个文件数量限制" + error_only_user_that_locked_file_can_unlock_it: 只有锁定文件的用户才能解锁该文件 + error_max_files_exceeded: "超出同时下载%{number}个文件数量限制" error_entry_project_does_not_match_current_project: 入口项目与当前项目不匹配 notice_folder_created: 文件夹创建完毕 error_folder_creation_failed: 文件夹创建失败 @@ -40,7 +40,7 @@ zh: notice_folder_deleted: 文件夹已删除 error_folder_is_not_empty: 非空文件夹 error_folder_title_is_already_used: 标题已经被使用 - notice_folder_details_were_saved: 文件夹详细信息已保存 + notice_folder_details_were_saved: 文件夹详细信息已保存 error_file_is_locked: Folder is locked error_file_is_locked: 文件被锁定 notice_file_deleted: 文件已删除 @@ -81,8 +81,8 @@ zh: title_waiting_for_approval: 待批准 title_approved: 已批准 title_unlock_file: 解除锁定允许其他成员修改 - title_lock_file: 锁定以防其他成员修改 - title_download_checked: zip归档下载所选 + title_lock_file: 锁定以防其他成员修改 + title_download_checked: zip归档下载所选 title_send_checked_by_email: 电子邮件发送所选 link_user_preferences: 您的文档管理系统项目偏好设定 heading_send_documents_by_email: 电子邮件发送文档 @@ -94,11 +94,7 @@ zh: label_email_body: 正文 label_email_send: 发送 title_notifications_active: 通知处于有效状态 - label_file_size: 文件大小 - heading_file_upload: 上传 - note_uploaded_maximum_files_at_once: "一次最多上传%{number}个文件." - note_upload_files_greater_than_two_gb: 为了上传大于2GB文件,您需要64位版本的浏览器. - submit_upload_files: 上传 + label_upload: 上传 heading_new_folder: 新建文件夹 label_title: 标题 label_description: 描述 @@ -110,16 +106,16 @@ zh: select_option_activated: 激活 title_save_preferences: 保存偏好设定 label_title_format: Title format - text_title_format: "Document title format for download (%t - title, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" + text_title_format: "Document title format for download (%t - title, %f - file, %d - date, %v - version, %i - ID, %r - revision). Example: %t_%v" heading_revisions: 修订版本 title_download: 下载 title_delete_revision: 删除此修订 label_created: 创建 label_changed: 修改 info_changed_by_user: "%{changed} by" - label_filename: 文件名 + label_filename: 文件名 label_mime: Mime - label_size: 大小 + label_size: 大小 heading_new_revision: 新修订 option_version_same: Same option_version_minor: Minor @@ -154,7 +150,7 @@ zh: permission_user_preferences: 用户偏好设定 permission_view_dmsf_files: 查看文档 permission_folder_manipulation: 文件夹操作 - permission_file_manipulation: 文件操作 + permission_file_manipulation: 文件操作 permission_force_file_unlock: 强制文件解锁 permission_manage_workflows: Manage workflows permission_file_delete: Delete documents @@ -167,7 +163,7 @@ zh: error_user_has_not_right_delete_folder: 用户没有权限删除文件夹 error_user_has_not_right_delete_file: 用户没有权限删除文件 notice_entries_deleted: 条目已删除 - warning_some_entries_were_not_deleted: "某些条目未被删除: %{entries}" + warning_some_entries_were_not_deleted: "某些条目未被删除: %{entries}" title_delete_checked: 删除选中 title_items: items title_filename_for_download: 用于下载或zip归档的文件名 @@ -186,7 +182,7 @@ zh: label_dmsf_updated: DMSF updated label_dmsf_downloaded: DMSF document downloaded title_total_size_of_all_files: 文件夹所有文件总大小 - project_module_dmsf: 文档管家 + project_module_dmsf: 文档管家 warning_no_project_to_copy_file_to: No project to copy file to comment_copied_from: "Copied from %{source}" notice_file_copied: File copied @@ -203,7 +199,7 @@ zh: title_copy: Copy error_folder_cannot_be_copied: "Folder can't be copied" notice_folder_copied: Folder copied - + error_max_email_filesize_exceeded: "You've exceeded the maximum filesize for sending via email. (%{number} MB)" note_maximum_email_filesize: Limits maximum filesize that can be sent via email. 0 means unlimited. Number is in MB. label_maximum_email_filesize: Maximum email attachment size @@ -219,15 +215,14 @@ zh: warning_folder_not_locked: Unfortunately, the folder could not be locked notice_folder_unlocked: The folder was successfully unlocked error_only_user_that_locked_folder_can_unlock_it: You are not authorised to unlock this folder - title_folder_parent_locked: "Parent folder %{name} is locked" title_unlock_folder: Unlock to allow changes for other members title_lock_folder: Lock to prevent changes for other members select_option_webdav_readonly: Read-only select_option_webdav_readwrite: Read/Write label_webdav_strategy: Webdav strategy - note_webdav_strategy: Enables the administrator to decide if webdav is a read-only or read-write platform for end users. - + note_webdav_strategy: Enables the administrator to decide if webdav is a read-only or read-write platform for end users. + error_unable_delete_dmsf_workflow: Unable to delete the workflow error_empty_note: "The note can't be empty" error_workflow_assign: An error occured while assigning @@ -251,42 +246,42 @@ zh: label_dmsf_workflow_add_approver: "Add a new approver with a logical function:" label_or: or label_action: Action - label_note: Note + label_note: Note title_none: None title_rejection: Rejection title_delegation: Delegation title_assignment: Assignment - title_start: Start + title_start: Start title_dmsf_workflow_log: Approval Workflow Log title_assigned: Assigned title_approval: Approval title_rejected: Rejected dmsf_and: AND dmsf_or: OR - dmsf_new_step: New step + dmsf_new_step: New step message_dmsf_wokflow_note: Your note... info_revision: "r%{rev}" - link_workflow: Workflow + link_workflow: Workflow notice_workflow_started: Approval workflow successfully started - text_email_subject_approved: "Approval workflow %{name} approved" - text_email_subject_rejected: "Approval workflow %{name} rejected" - text_email_subject_delegated: "Approval workflow %{name} delegated" - text_email_subject_requires_approval: "Approval workflow %{name} requires your approval" - text_email_subject_updated: "Approval workflow %{name} updated" - text_email_subject_started: "Approval workflow %{name} started" + text_email_subject_approved: approved + text_email_subject_rejected: rejected + text_email_subject_delegated: delegated + text_email_subject_requires_approval: requires your approval + text_email_subject_updated: updated + text_email_subject_started: started text_email_finished_approved: "The approval workflow '%{name}' assigned to '%{filename}' document has just been finished and the document has been approved." text_email_finished_rejected: "The approval workflow '%{name}' assigned to '%{filename}' document has just been finished and the document has been rejected because of '%{notice}'." text_email_finished_delegated: "The approval workflow '%{name}' assigned to '%{filename}' document has just been delegated because of '%{notice}' and you are expected to do an approval in the current approval step." text_email_finished_step: "The approval workflow '%{name}' assigned to '%{filename}' document has just finished one of the approval steps and you are expected to do an approval in the next approval step." text_email_finished_step_short: "The approval workflow '%{name}' assigned to '%{filename}' document has just finished one of the approval steps." - text_email_started: "The approval workflow '%{name}' assigned to '%{filename}' document has just been started and you are expected to do an approval in the current approval step." + text_email_started: "The approval workflow '%{name}' assigned to '%{filename}' document has just been started and you are expected to do an approval in the current approval step." text_email_to_proceed: To proceed click on the check box icon next to the document in text_email_to_see_history: To see the approval history click on the workflow status of the document in text_email_to_see_status: To see the current status of the approval workflow click on the workflow status the document in label_my_open_approvals: My open approvals label_my_locked_documents: My locked documents - - title_create_link: Create a symbolic link + + title_create_link: Create a symbolic link label_link_from: Link from label_link_to: Link to label_notifications_on: Notifications on @@ -294,25 +289,25 @@ zh: field_target_file: Source file title_download_entries: Download entries label_external: External - + label_link_name: Link name label_link_external_url: URL label_target_folder: Target folder label_source_folder: Source folder label_target_project: Target project - label_source_project: Source project - - text_email_doc_updated_subject: "Documents of %{project} updated" + label_source_project: Source project + + text_email_doc_updated_subject: Documents updated text_email_doc_updated: has just actualized documents of text_email_doc_follows: as follows - text_email_doc_deleted_subject: "Documents of %{project} deleted" + text_email_doc_deleted_subject: Documents deleted text_email_doc_deleted: has just deleted documents of label_links_only: links only - + label_display_notified_recipients: Display notified recipients note_display_notified_recipients: The user will be informed about all recipients of just sent the email notification. warning_email_notifications: "Email notifications sent to %{to}" - + link_trash_bin: Trash bin title_restore: Restore notice_dmsf_file_restored: The document has been successfully restored @@ -320,17 +315,20 @@ zh: notice_dmsf_link_restored: The link has been successfully restored title_restore_checked: Restore checked error_parent_folder: "The parent folder doesn't exist" - + error_resource_or_parent_locked: Unable to complete lock - resource (or parent) is locked error_parent_locked: Unable to complete lock - resource parent is locked error_resource_locked: Unable to complete lock - resource is locked - error_lock_exclusively: unable to lock exclusively an already-locked resource - error_unlock_parent_locked: Unlock failed - resource parent is locked - - my: - blocks: - locked_documents: Locked documents - open_approvals: Open approvals - + error_lock_exclusively: Unable to lock exclusively an already-locked resource + error_unlock_parent_locked: Unlock failed - resource parent is locked + + field_dmsf_tree_view: Navigate folders in a tree + label_dmsf_version: 版本 + + locked_documents: Locked documents + open_approvals: Open approvals + label_maximum_ajax_upload_filesize: Maximum file size uploadable via AJAX - note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB. \ No newline at end of file + note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB. + label_classic: Classic + label_drag_drop: "Drag&Drop" diff --git a/config/routes.rb b/config/routes.rb index 04dd9e33..8f86500c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,8 +1,10 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-14 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -40,8 +42,8 @@ RedmineApp::Application.routes.draw do get '/projects/:id/dmsf/unlock', :controller => 'dmsf', :action => 'unlock', :as => 'unlock_dmsf' get '/projects/:id/dmsf/', :controller => 'dmsf', :action => 'show', :as => 'dmsf_folder' get '/projects/:id/dmsf/new', :controller => 'dmsf', :action => 'new', :as => 'new_dmsf' - get '/projects/:id/dmsf/edit', :controller=> 'dmsf', :action => 'edit', :as => 'edit_dmsf' - get '/projects/:id/dmsf/edit/root', :controller=> 'dmsf', :action => 'edit_root', :as => 'edit_root_dmsf' + get '/projects/:id/dmsf/edit', :controller => 'dmsf', :action => 'edit', :as => 'edit_dmsf' + get '/projects/:id/dmsf/edit/root', :controller => 'dmsf', :action => 'edit_root', :as => 'edit_root_dmsf' get '/projects/:id/dmsf/trash', :controller => 'dmsf', :action => 'trash', :as => 'trash_dmsf' get '/projects/:id/dmsf/restore', :controller => 'dmsf', :action => 'restore', :as => 'restore_dmsf' @@ -61,7 +63,7 @@ RedmineApp::Application.routes.draw do post '/projects/:id/dmsf/upload', :controller => 'dmsf_upload', :action => 'upload' post '/projects/:id/dmsf/upload/commit', :controller => 'dmsf_upload', :action => 'commit_files' post '/projects/:id/dmsf/commit', :controller => 'dmsf_upload', :action => 'commit' - + # # dmsf_files controller # /dmsf/files/ @@ -76,7 +78,7 @@ RedmineApp::Application.routes.draw do get '/dmsf/files/:id/download', :controller => 'dmsf_files', :action => 'show', :download => '' # Otherwise will not route nil download param get '/dmsf/files/:id/download/:download', :controller => 'dmsf_files', :action => 'show', :as => 'download_revision' get '/dmsf/files/:id/view', :controller => 'dmsf_files', :action => 'view' - get '/dmsf/files/:id', :controller => 'dmsf_files', :action => 'show', :as => 'dmsf_file' + get '/dmsf/files/:id', :controller => 'dmsf_files', :action => 'show', :as => 'dmsf_file' delete '/dmsf/files/:id', :controller => 'dmsf_files', :action => 'delete' get '/dmsf/files/:id/restore', :controller => 'dmsf_files', :action => 'restore', :as => 'restore_dmsf_file' @@ -108,15 +110,15 @@ RedmineApp::Application.routes.draw do get '/dmsf/folders/:id/copy', :controller => 'dmsf_folders_copy', :action => 'new', :as => 'copy_folder' # - # DAV4Rack implementation of Webdav [note: if changing path you'll need to update lib/redmine_dmsf/webdav/no_parse.rb also] - # /dmsf/webdav + # DAV4Rack implementation of Webdav mount DAV4Rack::Handler.new( :root_uri_path => "#{Redmine::Utils::relative_url_root}/dmsf/webdav", :resource_class => RedmineDmsf::Webdav::ResourceProxy, - :controller_class => RedmineDmsf::Webdav::Controller - ), :at => "/dmsf/webdav" - - # Approval workflow + :controller_class => RedmineDmsf::Webdav::Controller, + :log_to => Rails.logger + ), :at => '/dmsf/webdav' + + # Approval workflow resources :dmsf_workflows do member do get 'autocomplete_for_user' @@ -129,16 +131,16 @@ RedmineApp::Application.routes.draw do get 'new_step' end end - + match 'dmsf_workflows/:id/edit', :controller => 'dmsf_workflows', :action => 'add_step', :id => /\d+/, :via => :post match 'dmsf_workflows/:id/edit', :controller => 'dmsf_workflows', :action => 'remove_step', :id => /\d+/, :via => :delete - match 'dmsf_workflows/:id/edit', :controller => 'dmsf_workflows', :action => 'reorder_steps', :id => /\d+/, :via => :put - + match 'dmsf_workflows/:id/edit', :controller => 'dmsf_workflows', :action => 'reorder_steps', :id => /\d+/, :via => :put + # Links resources :dmsf_links do member do get 'restore' - end - end - -end + end + end + +end \ No newline at end of file diff --git a/db/migrate/06_dmsf_1_2_0.rb b/db/migrate/06_dmsf_1_2_0.rb index 522706ca..f2099274 100644 --- a/db/migrate/06_dmsf_1_2_0.rb +++ b/db/migrate/06_dmsf_1_2_0.rb @@ -17,7 +17,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class Dmsf120 < ActiveRecord::Migration - + class DmsfFileRevision < ActiveRecord::Base belongs_to :file, :class_name => 'DmsfFile', :foreign_key => 'dmsf_file_id' belongs_to :source_revision, :class_name => 'DmsfFileRevision', :foreign_key => 'source_dmsf_file_revision_id' @@ -26,22 +26,22 @@ class Dmsf120 < ActiveRecord::Migration belongs_to :deleted_by_user, :class_name => 'User', :foreign_key => 'deleted_by_user_id' belongs_to :project end - + def self.up add_column :dmsf_file_revisions, :project_id, :integer, :null => true - + DmsfFileRevision.find_each do |revision| - if revision.file - revision.project_id = revision.file.project.id + if revision.dmsf_file + revision.project_id = revision.dmsf_file.project.id revision.save end end - + change_column :dmsf_file_revisions, :project_id, :integer, :null => false end def self.down - remove_column :dmsf_file_revisions, :project_id + remove_column :dmsf_file_revisions, :project_id end end diff --git a/db/migrate/20141013102501_remove_project_from_revision.rb b/db/migrate/20141013102501_remove_project_from_revision.rb index 109513ef..2277a3e0 100644 --- a/db/migrate/20141013102501_remove_project_from_revision.rb +++ b/db/migrate/20141013102501_remove_project_from_revision.rb @@ -19,19 +19,19 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class RemoveProjectFromRevision < ActiveRecord::Migration - def up - remove_column :dmsf_file_revisions, :project_id + def up + remove_column :dmsf_file_revisions, :project_id end - + def down add_column :dmsf_file_revisions, :project_id, :integer, :null => true - + DmsfFileRevision.find_each do |revision| - if revision.file - revision.project_id = revision.file.project_id + if revision.dmsf_file + revision.project_id = revision.dmsf_file.project_id revision.save end - end + end end - + end \ No newline at end of file diff --git a/db/migrate/20141015132701_remove_folder_from_revision.rb b/db/migrate/20141015132701_remove_folder_from_revision.rb index 007fdd43..403116e4 100644 --- a/db/migrate/20141015132701_remove_folder_from_revision.rb +++ b/db/migrate/20141015132701_remove_folder_from_revision.rb @@ -27,8 +27,8 @@ class RemoveFolderFromRevision < ActiveRecord::Migration add_column :dmsf_file_revisions, :dmsf_folder_id, :integer, :null => true DmsfFileRevision.find_each do |revision| - if revision.file - revision.dmsf_folder_id = revision.file.dmsf_folder_id + if revision.dmsf_file + revision.dmsf_folder_id = revision.dmsf_file.dmsf_folder_id revision.save end end diff --git a/db/migrate/20151209100001_title_format.rb b/db/migrate/20151209100001_title_format.rb index 1ace097d..8efd7d77 100644 --- a/db/migrate/20151209100001_title_format.rb +++ b/db/migrate/20151209100001_title_format.rb @@ -2,7 +2,7 @@ # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2015 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine_dmsf/webdav.rb b/db/migrate/20160215125801_approval_workflow_status.rb similarity index 61% rename from lib/redmine_dmsf/webdav.rb rename to db/migrate/20160215125801_approval_workflow_status.rb index 6e2a836d..1e447a0c 100644 --- a/lib/redmine_dmsf/webdav.rb +++ b/db/migrate/20160215125801_approval_workflow_status.rb @@ -1,6 +1,8 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,13 +18,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# Load up classes that make up our webdav solution ontop -# of DAV4Rack -require 'redmine_dmsf/webdav/no_parse' -require 'redmine_dmsf/webdav/base_resource' -require 'redmine_dmsf/webdav/controller' -require 'redmine_dmsf/webdav/dmsf_resource' -require 'redmine_dmsf/webdav/download' -require 'redmine_dmsf/webdav/index_resource' -require 'redmine_dmsf/webdav/project_resource' -require 'redmine_dmsf/webdav/resource_proxy' +class ApprovalWorkflowStatus < ActiveRecord::Migration + def self.up + add_column :dmsf_workflows, :status, :integer, :null => false, :default => DmsfWorkflow::STATUS_ACTIVE + DmsfWorkflow.all.each {|wf| wf.update_attribute(:status, DmsfWorkflow::STATUS_ACTIVE)} + end + + def self.down + remove_column :dmsf_workflows, :status + end +end \ No newline at end of file diff --git a/db/migrate/20160217133001_status_deleted.rb b/db/migrate/20160217133001_status_deleted.rb new file mode 100644 index 00000000..5adccf4d --- /dev/null +++ b/db/migrate/20160217133001_status_deleted.rb @@ -0,0 +1,46 @@ +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman +# +# 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 StatusDeleted < ActiveRecord::Migration + def self.cast_integer(colname) + adapter_name = ActiveRecord::Base.connection.adapter_name.downcase + case adapter_name + when /postgresql/ + "integer USING CAST(#{colname} AS integer)" + else + 'integer' + end + end + + def self.up + change_column :dmsf_folders, :deleted, :boolean, :default => nil + change_column :dmsf_files, :deleted, :boolean, :default => nil + change_column :dmsf_file_revisions, :deleted, :boolean, :default => nil + change_column :dmsf_links, :deleted, :boolean, :default => nil + + change_column :dmsf_folders, :deleted, cast_integer(:deleted), :null => false, :default => DmsfFolder::STATUS_ACTIVE + change_column :dmsf_files, :deleted, cast_integer(:deleted), :null => false, :default => DmsfFile::STATUS_ACTIVE + change_column :dmsf_file_revisions, :deleted, cast_integer(:deleted), :null => false, :default => DmsfFileRevision::STATUS_ACTIVE + change_column :dmsf_links, :deleted, cast_integer(:deleted), :null => false, :default => DmsfLink::STATUS_ACTIVE + end + + def self.down + end +end \ No newline at end of file diff --git a/db/migrate/20160222140401_approval_workflow_std_fields.rb b/db/migrate/20160222140401_approval_workflow_std_fields.rb new file mode 100644 index 00000000..d96578ac --- /dev/null +++ b/db/migrate/20160222140401_approval_workflow_std_fields.rb @@ -0,0 +1,37 @@ +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman +# +# 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 ApprovalWorkflowStdFields < ActiveRecord::Migration + def self.up + add_column :dmsf_workflows, :updated_on, :timestamp + add_column :dmsf_workflows, :created_on, :datetime + add_column :dmsf_workflows, :author_id, :integer + # Set updated_on + DmsfWorkflow.all.each(&:touch) + # Set created_on and author_id + DmsfWorkflow.update_all 'created_on = updated_on, author_id = (select id from users where admin limit 1)' + end + + def self.down + remove_column :dmsf_workflows, :updated_on + remove_column :dmsf_workflows, :created_on + remove_column :dmsf_workflows, :author_id + end +end \ No newline at end of file diff --git a/db/migrate/20160421150501_add_digest_to_revision.rb b/db/migrate/20160421150501_add_digest_to_revision.rb new file mode 100644 index 00000000..a6babd0d --- /dev/null +++ b/db/migrate/20160421150501_add_digest_to_revision.rb @@ -0,0 +1,29 @@ +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman +# +# 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 AddDigestToRevision < ActiveRecord::Migration + def up + add_column :dmsf_file_revisions, :digest, :string, :limit => 40, :default => '', :null => false + end + + def down + remove_column :dmsf_file_revisions, :digest + end +end diff --git a/dmsf_user_guide.odt b/dmsf_user_guide.odt index 8ca6d1bf..2560a874 100644 Binary files a/dmsf_user_guide.odt and b/dmsf_user_guide.odt differ diff --git a/init.rb b/init.rb index e3e25e8e..94811d86 100644 --- a/init.rb +++ b/init.rb @@ -28,7 +28,7 @@ Redmine::Plugin.register :redmine_dmsf do name 'DMSF' author 'Vít Jonáš / Daniel Munn / Karel Pičman' description 'Document Management System Features' - version '1.5.6' + version '1.5.7 devel' url 'http://www.redmine.org/plugins/dmsf' author_url 'https://github.com/danmunn/redmine_dmsf/graphs/contributors' @@ -93,104 +93,10 @@ Redmine::Plugin.register :redmine_dmsf do # Administration menu extension Redmine::MenuManager.map :admin_menu do |menu| - menu.push :approvalworkflows, {:controller => 'dmsf_workflows', :action => 'index'}, :caption => :label_dmsf_workflow_plural - end - - Redmine::WikiFormatting::Macros.register do - desc "Wiki link to DMSF file:\n\n" + - "{{dmsf(file_id [, title [, revision_id]])}}\n\n" + - "_file_id_ / _revision_id_ can be found in the link for file/revision download." - macro :dmsf do |obj, args| - raise ArgumentError if args.length < 1 # Requires file id - file = DmsfFile.visible.find args[0].strip - if args[2].blank? - revision = file.last_revision - else - revision = DmsfFileRevision.find(args[2]) - if revision.file != file - raise ActiveRecord::RecordNotFound - end - end - if User.current && User.current.allowed_to?(:view_dmsf_files, file.project) - file_view_url = url_for(:controller => :dmsf_files, :action => 'view', :id => file, :download => args[2]) - return link_to(h(args[1] ? args[1] : file.title), - file_view_url, - :target => '_blank', - :title => l(:title_title_version_version_download, :title => h(file.title), :version => file.version), - 'data-downloadurl' => "#{file.last_revision.detect_content_type}:#{h(file.name)}:#{file_view_url}") - else - raise l(:notice_not_authorized) - end - end - - desc "Wiki link to DMSF folder:\n\n" + - "{{dmsff(folder_id [, title])}}\n\n" + - "_folder_id_ may be missing. _folder_id_ can be found in the link for folder opening." - macro :dmsff do |obj, args| - if args.length < 1 - return link_to l(:link_documents), dmsf_folder_url(@project) - else - folder = DmsfFolder.visible.find args[0].strip - if User.current && User.current.allowed_to?(:view_dmsf_folders, folder.project) - return link_to h(args[1] ? args[1] : folder.title), - dmsf_folder_url(folder.project, :folder_id => folder) - else - raise l(:notice_not_authorized) - end - end - end - - desc "Wiki link to DMSF document description:\n\n" + - "{{dmsfd(file_id)}}\n\n" + - "_file_id_ can be found in the link for file/revision download." - macro :dmsfd do |obj, args| - raise ArgumentError if args.length < 1 # Requires file id - file = DmsfFile.visible.find args[0].strip - if User.current && User.current.allowed_to?(:view_dmsf_files, file.project) - return textilizable(file.description) - else - raise l(:notice_not_authorized) - end - end - - desc "Wiki link to DMSF document's content preview:\n\n" + - "{{dmsft(file_id)}}\n\n" + - "_file_id_ can be found in the link for file/revision download." - macro :dmsft do |obj, args| - raise ArgumentError if args.length < 2 # Requires file id and lines number - file = DmsfFile.visible.find args[0].strip - if User.current && User.current.allowed_to?(:view_dmsf_files, file.project) - return file.preview(args[1].strip).gsub("\n", '
          ').html_safe - else - raise l(:notice_not_authorized) - end - end - - desc "Wiki DMSF image:\n\n" + - "{{dmsf_image(file_id)}}\n" + - "{{dmsf_image(file_id, size=300)}} -- with custom title and size\n" + - "{{dmsf_image(file_id, size=640x480)}}" - macro :dmsf_image do |obj, args| - args, options = extract_macro_options(args, :size, :title) - file_id = args.first - raise 'DMSF document ID required' unless file_id.present? - size = options[:size] - if file = DmsfFile.find_by_id(file_id) - unless User.current && User.current.allowed_to?(:view_dmsf_files, file.project) - raise l(:notice_not_authorized) - end - raise 'Not supported image format' unless file.image? - url = url_for(:controller => :dmsf_files, :action => 'view', :id => file) - if size && size.include?('%') - image_tag(url, :alt => file.title, :width => size, :height => size) - else - image_tag(url, :alt => file.title, :size => size) - end - else - raise "Document ID #{file_id} not found" - end - end - end + menu.push :dmsf_approvalworkflows, + {:controller => 'dmsf_workflows', :action => 'index'}, + :caption => :label_dmsf_workflow_plural + end # Rubyzip configuration Zip.unicode_names = true @@ -199,4 +105,4 @@ end Redmine::Search.map do |search| search.register :dmsf_files search.register :dmsf_folders -end +end \ No newline at end of file diff --git a/lib/dmsf_zip.rb b/lib/dmsf_zip.rb index 477345fd..c19a6b16 100644 --- a/lib/dmsf_zip.rb +++ b/lib/dmsf_zip.rb @@ -1,6 +1,9 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -27,6 +30,7 @@ class DmsfZip @zip.chmod(0644) @zip_file = Zip::OutputStream.new(@zip.path) @files = [] + @folders = [] end def finish @@ -40,24 +44,29 @@ class DmsfZip end def add_file(file, member, root_path = nil) - string_path = file.folder.nil? ? '' : "#{file.folder.dmsf_path_str}/" - string_path = string_path[(root_path.length + 1) .. string_path.length] if root_path - string_path += file.formatted_name(member ? member.title_format : nil) - @zip_file.put_next_entry(string_path) - File.open(file.last_revision.disk_file, 'rb') do |f| - while (buffer = f.read(8192)) - @zip_file.write(buffer) + unless @files.include?(file) + string_path = file.dmsf_folder.nil? ? '' : "#{file.dmsf_folder.dmsf_path_str}/" + string_path = string_path[(root_path.length + 1) .. string_path.length] if root_path + string_path += file.formatted_name(member ? member.title_format : nil) + @zip_file.put_next_entry(string_path) + File.open(file.last_revision.disk_file, 'rb') do |f| + while (buffer = f.read(8192)) + @zip_file.write(buffer) + end end + @files << file end - @files << file end def add_folder(folder, member, root_path = nil) - string_path = "#{folder.dmsf_path_str}/" - string_path = string_path[(root_path.length + 1) .. string_path.length] if root_path - @zip_file.put_next_entry(string_path) - folder.subfolders.visible.each { |subfolder| self.add_folder(subfolder, root_path) } - folder.files.visible.each { |file| self.add_file(file, member, root_path) } + unless @folders.include?(folder) + string_path = "#{folder.dmsf_path_str}/" + string_path = string_path[(root_path.length + 1) .. string_path.length] if root_path + @zip_file.put_next_entry(string_path) + @folders << folder + folder.dmsf_folders.visible.each { |subfolder| self.add_folder(subfolder, member, root_path) } + folder.dmsf_files.visible.each { |file| self.add_file(file, member, root_path) } + end end -end +end \ No newline at end of file diff --git a/lib/redmine_dmsf.rb b/lib/redmine_dmsf.rb index 4c6d70eb..5b9718b2 100644 --- a/lib/redmine_dmsf.rb +++ b/lib/redmine_dmsf.rb @@ -1,8 +1,10 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-14 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -21,16 +23,43 @@ DMSF_MAX_NOTIFICATION_RECEIVERS_INFO = 10 # DMSF libraries -require 'redmine_dmsf/patches' #plugin patches -require 'redmine_dmsf/webdav' #DAV4Rack implementation +# Plugin's patches +require 'redmine_dmsf/patches/custom_fields_helper_patch' +require 'redmine_dmsf/patches/acts_as_customizable' +require 'redmine_dmsf/patches/project_patch' +require 'redmine_dmsf/patches/project_tabs_extended' +require 'redmine_dmsf/patches/user_preference_patch' + +# Load up classes that make up our WebDAV solution ontop of DAV4Rack +require 'redmine_dmsf/webdav/base_resource' +require 'redmine_dmsf/webdav/controller' +require 'redmine_dmsf/webdav/dmsf_resource' +require 'redmine_dmsf/webdav/download' +require 'redmine_dmsf/webdav/index_resource' +require 'redmine_dmsf/webdav/project_resource' +require 'redmine_dmsf/webdav/resource_proxy' + +# Exceptions +require 'redmine_dmsf/errors/dmsf_access_error.rb' +require 'redmine_dmsf/errors/dmsf_content_error.rb' +require 'redmine_dmsf/errors/dmsf_email_max_file_error.rb' +require 'redmine_dmsf/errors/dmsf_file_not_found_error.rb' +require 'redmine_dmsf/errors/dmsf_lock_error.rb' +require 'redmine_dmsf/errors/dmsf_zip_max_file_error.rb' # Hooks -require 'redmine_dmsf/hooks/view_projects_form_hook' -require 'redmine_dmsf/hooks/base_view_hooks' +require 'redmine_dmsf/hooks/views/view_projects_form_hook' +require 'redmine_dmsf/hooks/views/base_view_hooks' +require 'redmine_dmsf/hooks/controllers/search_controller_hooks' +require 'redmine_dmsf/hooks/views/my_account_view_hooks' + +# Macros +require 'redmine_dmsf/macros' module RedmineDmsf end # Add the plugin view folder into ActionMailer's paths to search -ActionMailer::Base.append_view_path(File.expand_path(File.dirname(__FILE__) + '/../app/views')) \ No newline at end of file +ActionMailer::Base.append_view_path(File.expand_path( + File.dirname(__FILE__) + '/../app/views')) \ No newline at end of file diff --git a/lib/dmsf_access_error.rb b/lib/redmine_dmsf/errors/dmsf_access_error.rb similarity index 85% rename from lib/dmsf_access_error.rb rename to lib/redmine_dmsf/errors/dmsf_access_error.rb index 98d54324..3131e5ba 100644 --- a/lib/dmsf_access_error.rb +++ b/lib/redmine_dmsf/errors/dmsf_access_error.rb @@ -1,6 +1,9 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/dmsf_content_error.rb b/lib/redmine_dmsf/errors/dmsf_content_error.rb similarity index 85% rename from lib/dmsf_content_error.rb rename to lib/redmine_dmsf/errors/dmsf_content_error.rb index 2cebdffc..1e73e9a2 100644 --- a/lib/dmsf_content_error.rb +++ b/lib/redmine_dmsf/errors/dmsf_content_error.rb @@ -1,6 +1,9 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine_dmsf/hooks/base_view_hooks.rb b/lib/redmine_dmsf/errors/dmsf_email_max_file_error.rb similarity index 67% rename from lib/redmine_dmsf/hooks/base_view_hooks.rb rename to lib/redmine_dmsf/errors/dmsf_email_max_file_error.rb index 130972c2..5ca70174 100644 --- a/lib/redmine_dmsf/hooks/base_view_hooks.rb +++ b/lib/redmine_dmsf/errors/dmsf_email_max_file_error.rb @@ -1,6 +1,8 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2013 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,16 +18,15 @@ # 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 Hooks - include Redmine::Hook - - class DmsfViewListener < Redmine::Hook::ViewListener - - def view_layouts_base_html_head(context={}) - "\n".html_safe + stylesheet_link_tag('dmsf', :plugin => :redmine_dmsf) - end - +class EmailMaxFileSize < StandardError + include Redmine::I18n + + def initialize(message = nil) + if message.present? + super message + else + super l(:error_max_email_filesize_exceeded, + :number => Setting.plugin_redmine_dmsf['dmsf_max_email_filesize']) end end end diff --git a/lib/redmine_dmsf/errors/dmsf_file_not_found_error.rb b/lib/redmine_dmsf/errors/dmsf_file_not_found_error.rb new file mode 100644 index 00000000..b55d226e --- /dev/null +++ b/lib/redmine_dmsf/errors/dmsf_file_not_found_error.rb @@ -0,0 +1,23 @@ +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman +# +# 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 FileNotFound < StandardError + +end diff --git a/lib/dmsf_lock_error.rb b/lib/redmine_dmsf/errors/dmsf_lock_error.rb similarity index 84% rename from lib/dmsf_lock_error.rb rename to lib/redmine_dmsf/errors/dmsf_lock_error.rb index b74493c5..0ccc326a 100644 --- a/lib/dmsf_lock_error.rb +++ b/lib/redmine_dmsf/errors/dmsf_lock_error.rb @@ -1,6 +1,9 @@ +# encoding: utf-8 +# # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/redmine_dmsf/errors/dmsf_zip_max_file_error.rb b/lib/redmine_dmsf/errors/dmsf_zip_max_file_error.rb new file mode 100644 index 00000000..c888c486 --- /dev/null +++ b/lib/redmine_dmsf/errors/dmsf_zip_max_file_error.rb @@ -0,0 +1,32 @@ +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman +# +# 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 ZipMaxFilesError < StandardError + include Redmine::I18n + + def initialize(message = nil) + if message.present? + super message + else + super l(:error_max_files_exceeded, + :number => Setting.plugin_redmine_dmsf['dmsf_max_file_download']) + end + end +end diff --git a/lib/redmine_dmsf/hooks/controllers/search_controller_hooks.rb b/lib/redmine_dmsf/hooks/controllers/search_controller_hooks.rb new file mode 100644 index 00000000..e96e8501 --- /dev/null +++ b/lib/redmine_dmsf/hooks/controllers/search_controller_hooks.rb @@ -0,0 +1,41 @@ +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman +# +# 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 Hooks + include Redmine::Hook + + class ControllerSearchHook < RedmineDmsf::Hooks::Listener + + def controller_search_quick_jump(context={}) + if context.is_a?(Hash) + question = context[:question] + if question.present? + if question.match(/^D(\d+)$/) && DmsfFile.visible.find_by_id($1.to_i) + return { :controller => 'dmsf_files', :action => 'show', :id => $1.to_i } + end + end + end + end + + end + + end +end \ No newline at end of file diff --git a/lib/redmine_dmsf/hooks/views/base_view_hooks.rb b/lib/redmine_dmsf/hooks/views/base_view_hooks.rb new file mode 100644 index 00000000..0eff651c --- /dev/null +++ b/lib/redmine_dmsf/hooks/views/base_view_hooks.rb @@ -0,0 +1,36 @@ +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman +# +# 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 Hooks + include Redmine::Hook + + class DmsfViewListener < Redmine::Hook::ViewListener + + def view_layouts_base_html_head(context={}) + "\n".html_safe + stylesheet_link_tag('redmine_dmsf.css', :plugin => :redmine_dmsf) + + "\n".html_safe + stylesheet_link_tag('select2.min.css', :plugin => :redmine_dmsf) + + "\n".html_safe + javascript_include_tag('select2.min.js', :plugin => :redmine_dmsf) + + "\n".html_safe + javascript_include_tag('redmine_dmsf.js', :plugin => :redmine_dmsf) + end + + end + end +end \ No newline at end of file diff --git a/lib/redmine_dmsf/hooks/views/my_account_view_hooks.rb b/lib/redmine_dmsf/hooks/views/my_account_view_hooks.rb new file mode 100644 index 00000000..2de1da4d --- /dev/null +++ b/lib/redmine_dmsf/hooks/views/my_account_view_hooks.rb @@ -0,0 +1,39 @@ +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman +# +# 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 Hooks + include Redmine::Hook + + class MyAccountViewHook < Redmine::Hook::ViewListener + + def view_my_account_preferences(context={}) + if context.is_a?(Hash) && context[:user] + context[:controller].send( + :render_to_string, { + :partial => 'hooks/redmine_dmsf/view_my_account', + :locals => context + }).html_safe + end + end + + end + end +end \ No newline at end of file diff --git a/lib/redmine_dmsf/hooks/view_projects_form_hook.rb b/lib/redmine_dmsf/hooks/views/view_projects_form_hook.rb similarity index 100% rename from lib/redmine_dmsf/hooks/view_projects_form_hook.rb rename to lib/redmine_dmsf/hooks/views/view_projects_form_hook.rb diff --git a/lib/redmine_dmsf/lockable.rb b/lib/redmine_dmsf/lockable.rb index a2433b3f..66640a7c 100644 --- a/lib/redmine_dmsf/lockable.rb +++ b/lib/redmine_dmsf/lockable.rb @@ -4,7 +4,7 @@ # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -38,17 +38,17 @@ module RedmineDmsf end end if tree - ret = ret | (folder.locks.empty? ? folder.lock : folder.locks) unless folder.nil? + ret = ret | (self.dmsf_folder.locks.empty? ? self.dmsf_folder.lock : self.dmsf_folder.locks) if dmsf_folder end return ret end - def lock! scope = :scope_exclusive, type = :type_write, expire = nil + def lock!(scope = :scope_exclusive, type = :type_write, expire = nil) # Raise a lock error if entity is locked, but its not at resource level - existing = locks(false) + existing = lock(false) raise DmsfLockError.new(l(:error_resource_or_parent_locked)) if self.locked? && existing.empty? unless existing.empty? - if existing[0].lock_scope == :scope_shared && scope == :scope_shared + if (existing[0].lock_scope == :scope_shared) && (scope == :scope_shared) # RFC states if an item is exclusively locked and another lock is attempted we reject # if the item is shared locked however, we can always add another lock to it if self.folder.locked? @@ -79,20 +79,20 @@ module RedmineDmsf return false unless self.locked? existing = self.lock(true) # If its empty its a folder that's locked (not root) - (existing.empty? || (!self.folder.nil? && self.folder.locked?)) ? false : true + (existing.empty? || (!self.dmsf_folder.nil? && self.dmsf_folder.locked?)) ? false : true end # # By using the path upwards, surely this would be quicker? def locked_for_user? return false unless locked? - b_shared = nil - self.dmsf_path.each do |entity| + b_shared = nil + self.dmsf_path.each do |entity| locks = entity.locks || entity.lock(false) next if locks.empty? locks.each do |lock| next if lock.expired? # In case we're in between updates - if (lock.lock_scope == :scope_exclusive && b_shared.nil?) + if (lock.lock_scope == :scope_exclusive && b_shared.nil?) return true if (!lock.user) || (lock.user.id != User.current.id) else b_shared = true if b_shared.nil? @@ -103,11 +103,11 @@ module RedmineDmsf end false end - + def unlock!(force_file_unlock_allowed = false) raise DmsfLockError.new(l(:warning_file_not_locked)) unless self.locked? existing = self.lock(true) - if existing.empty? || (!self.folder.nil? && self.folder.locked?) #If its empty its a folder thats locked (not root) + if existing.empty? || (!self.dmsf_folder.nil? && self.dmsf_folder.locked?) #If its empty its a folder thats locked (not root) raise DmsfLockError.new(l(:error_unlock_parent_locked)) else # If entity is locked to you, you aren't the lock originator (or named in a shared lock) so deny action diff --git a/lib/redmine_dmsf/macros.rb b/lib/redmine_dmsf/macros.rb new file mode 100644 index 00000000..51aaa514 --- /dev/null +++ b/lib/redmine_dmsf/macros.rb @@ -0,0 +1,186 @@ +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman +# +# 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. + +Redmine::WikiFormatting::Macros.register do + + # dmsf - link to a document + desc "Wiki link to DMSF file:\n\n" + + "{{dmsf(file_id [, title [, revision_id]])}}\n\n" + + "_file_id_ / _revision_id_ can be found in the link for file/revision download." + macro :dmsf do |obj, args| + raise ArgumentError if args.length < 1 # Requires file id + file = DmsfFile.visible.find args[0].strip + if args[2].blank? + revision = file.last_revision + else + revision = DmsfFileRevision.find(args[2]) + if revision.dmsf_file != file + raise ActiveRecord::RecordNotFound + end + end + if User.current && User.current.allowed_to?(:view_dmsf_files, file.project) + file_view_url = url_for(:controller => :dmsf_files, :action => 'view', :id => file, :download => args[2]) + return link_to(h(args[1] ? args[1] : file.title), + file_view_url, + :target => '_blank', + :title => h(revision.tooltip), + 'data-downloadurl' => "#{file.last_revision.detect_content_type}:#{h(file.name)}:#{file_view_url}") + else + raise l(:notice_not_authorized) + end + end + + # dmsff - link to a folder + desc "Wiki link to DMSF folder:\n\n" + + "{{dmsff(folder_id [, title])}}\n\n" + + "_folder_id_ may be missing. _folder_id_ can be found in the link for folder opening." + macro :dmsff do |obj, args| + if args.length < 1 + return link_to l(:link_documents), dmsf_folder_url(@project) + else + folder = DmsfFolder.visible.find args[0].strip + if User.current && User.current.allowed_to?(:view_dmsf_folders, folder.project) + return link_to h(args[1] ? args[1] : folder.title), + dmsf_folder_url(folder.project, :folder_id => folder) + else + raise l(:notice_not_authorized) + end + end + end + + # dmsfd - link to a document's description + desc "Wiki link to DMSF document description:\n\n" + + "{{dmsfd(file_id)}}\n\n" + + "_file_id_ can be found in the document's details." + macro :dmsfd do |obj, args| + raise ArgumentError if args.length < 1 # Requires file id + file = DmsfFile.visible.find args[0].strip + if User.current && User.current.allowed_to?(:view_dmsf_files, file.project) + return textilizable(file.description) + else + raise l(:notice_not_authorized) + end + end + + # dmsft - link to a document's content preview + desc "Wiki link to DMSF document's content preview:\n\n" + + "{{dmsft(file_id)}}\n\n" + + "_file_id_ can be found in the document's details." + macro :dmsft do |obj, args| + raise ArgumentError if args.length < 2 # Requires file id and lines number + file = DmsfFile.visible.find args[0].strip + if User.current && User.current.allowed_to?(:view_dmsf_files, file.project) + return file.preview(args[1].strip).gsub("\n", '
          ').html_safe + else + raise l(:notice_not_authorized) + end + end + + # dmsf_image - link to an image + desc "Wiki DMSF image:\n\n" + + "{{dmsf_image(file_id)}}\n" + + "{{dmsf_image(file_id, size=300)}} -- with custom title and size\n" + + "{{dmsf_image(file_id, height=300)}} -- with custom title and height (auto width)\n" + + "{{dmsf_image(file_id, width=300)}} -- with custom title and width (auto height)\n" + + "{{dmsf_image(file_id, size=640x480)}}" + macro :dmsf_image do |obj, args| + args, options = extract_macro_options(args, :size, :width, :height, :title) + file_id = args.first + raise 'DMSF document ID required' unless file_id.present? + size = options[:size] + width = options[:width] + height = options[:height] + if file = DmsfFile.find_by_id(file_id) + unless User.current && User.current.allowed_to?(:view_dmsf_files, file.project) + raise l(:notice_not_authorized) + end + raise 'Not supported image format' unless file.image? + url = url_for(:controller => :dmsf_files, :action => 'view', :id => file) + if size && size.include?('%') + image_tag(url, :alt => file.title, :width => size, :height => size) + elsif height + image_tag(url, :alt => file.title, :width => 'auto', :height => height) + elsif width + image_tag(url, :alt => file.title, :width => width, :height => 'auto') + else + image_tag(url, :alt => file.title, :size => size) + end + else + raise "Document ID #{file_id} not found" + end + end + + # dmsftn - link to an image thumbnail + desc "Wiki DMSF thumbnail:\n\n" + + "{{dmsftn(file_id)}}\n" + + "{{dmsftn(file_id, size=300)}} -- with custom title and size\n" + + "{{dmsftn(file_id, height=300)}} -- with custom title and height (auto width)\n" + + "{{dmsftn(file_id, width=300)}} -- with custom title and width (auto height)\n" + + "{{dmsftn(file_id, size=640x480)}}" + macro :dmsftn do |obj, args| + args, options = extract_macro_options(args, :size, :width, :height, :title) + file_id = args.first + raise 'DMSF document ID required' unless file_id.present? + size = options[:size] + width = options[:width] + height = options[:height] + if file = DmsfFile.find_by_id(file_id) + unless User.current && User.current.allowed_to?(:view_dmsf_files, file.project) + raise l(:notice_not_authorized) + end + raise 'Not supported image format' unless file.image? + url = url_for(:controller => :dmsf_files, :action => 'view', :id => file) + file_view_url = url_for(:controller => :dmsf_files, :action => 'view', :id => file, :download => args[2]) + if size && size.include?("%") + img = image_tag(url, :alt => file.title, :width => size, :height => size) + elsif size && size.include?("x") + img = image_tag(url, :alt => file.title, :size => size) + elsif height + img = image_tag(url, :alt => file.title, :width => 'auto', :height => height) + elsif width + img = image_tag(url, :alt => file.title, :width => width, :height => 'auto') + else + img = image_tag(url, :alt => file.title, :width => 'auto', :height => 200) + end + link_to(img, + file_view_url, :target => '_blank', + :title => h(file.last_revision.try(:tooltip)), + 'data-downloadurl' => "#{file.last_revision.detect_content_type}:#{h(file.name)}:#{file_view_url}") + else + raise "Document ID #{file_id} not found" + end + end + + # dmsfw - link to a document's approval workflow status + desc "Wiki link to DMSF document's approval workflow status:\n\n" + + "{{dmsfw(file_id)}}\n\n" + + "_file_id_ can be found in the document's details." + macro :dmsfw do |obj, args| + raise ArgumentError if args.length < 1 # Requires file id + file = DmsfFile.visible.find args[0].strip + if User.current && User.current.allowed_to?(:view_dmsf_files, file.project) + raise ActiveRecord::RecordNotFound unless file.last_revision + return file.last_revision.workflow_str(false) + else + raise l(:notice_not_authorized) + end + end + +end \ No newline at end of file diff --git a/lib/redmine_dmsf/patches/project_patch.rb b/lib/redmine_dmsf/patches/project_patch.rb index 578fd1a5..aadd9f29 100644 --- a/lib/redmine_dmsf/patches/project_patch.rb +++ b/lib/redmine_dmsf/patches/project_patch.rb @@ -31,10 +31,10 @@ module RedmineDmsf base.class_eval do unloadable alias_method_chain :copy, :dmsf - - has_many :dmsf_files, -> { where dmsf_folder_id: nil}, + + has_many :dmsf_files, -> { where(dmsf_folder_id: nil).order(:name) }, :class_name => 'DmsfFile', :foreign_key => 'project_id', :dependent => :destroy - has_many :dmsf_folders, -> {where dmsf_folder_id: nil}, + has_many :dmsf_folders, -> { where(dmsf_folder_id: nil).order(:title) }, :class_name => 'DmsfFolder', :foreign_key => 'project_id', :dependent => :destroy has_many :dmsf_workflows, :dependent => :destroy @@ -43,12 +43,22 @@ module RedmineDmsf has_many :file_links, -> { where dmsf_folder_id: nil, target_type: 'DmsfFile' }, :class_name => 'DmsfLink', :foreign_key => 'project_id', :dependent => :destroy has_many :url_links, -> { where dmsf_folder_id: nil, target_type: 'DmsfUrl' }, - :class_name => 'DmsfLink', :foreign_key => 'project_id', :dependent => :destroy + :class_name => 'DmsfLink', :foreign_key => 'project_id', :dependent => :destroy + has_many :dmsf_links, -> { where dmsf_folder_id: nil }, + :class_name => 'DmsfLink', :foreign_key => 'project_id', :dependent => :destroy + + before_save :set_default_dmsf_notification end end module InstanceMethods + def set_default_dmsf_notification + if self.new_record? + self.dmsf_notification = Setting.plugin_redmine_dmsf['dmsf_default_notifications'] == '1' + end + end + def dmsf_count file_count = self.dmsf_files.visible.count + self.file_links.visible.count folder_count = self.dmsf_folders.visible.count + self.folder_links.visible.count @@ -65,7 +75,7 @@ module RedmineDmsf project = project.is_a?(Project) ? project : Project.find(project) to_be_copied = %w(dmsf approval_workflows) - to_be_copied = to_be_copied & options[:only].to_a if options[:only].present? + to_be_copied = to_be_copied & Array.wrap(options[:only]) unless options[:only].nil? if save to_be_copied.each do |name| diff --git a/lib/redmine_dmsf/patches/user_preference_patch.rb b/lib/redmine_dmsf/patches/user_preference_patch.rb new file mode 100644 index 00000000..2c424db3 --- /dev/null +++ b/lib/redmine_dmsf/patches/user_preference_patch.rb @@ -0,0 +1,39 @@ +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman +# +# 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 DmsfUserPreference + def self.included(base) + base.send(:include, InstanceMethods) + end + + module InstanceMethods + + def dmsf_tree_view + self[:dmsf_tree_view] || '0' + end + + def dmsf_tree_view=(value) + self[:dmsf_tree_view] = value + end + end + +end + +UserPreference.send(:include, DmsfUserPreference) diff --git a/lib/redmine_dmsf/webdav/controller.rb b/lib/redmine_dmsf/webdav/controller.rb index 963961e6..f8ab6027 100644 --- a/lib/redmine_dmsf/webdav/controller.rb +++ b/lib/redmine_dmsf/webdav/controller.rb @@ -118,7 +118,7 @@ module RedmineDmsf # Escape URL string def url_format(resource) # Additionally escape square brackets, otherwise files with - # file name like file[1].pdf are not visible in some WebDAV clients + # file names like file[1].pdf are not visible in some WebDAV clients URI.encode(super, '[]') end diff --git a/lib/redmine_dmsf/webdav/dmsf_resource.rb b/lib/redmine_dmsf/webdav/dmsf_resource.rb index 66ff3f6f..74e21a10 100644 --- a/lib/redmine_dmsf/webdav/dmsf_resource.rb +++ b/lib/redmine_dmsf/webdav/dmsf_resource.rb @@ -29,7 +29,7 @@ module RedmineDmsf def setup @skip_alias |= [ :folder, :file ] end - + # Here we hook into the fact that resources can have a pre-execution routine run for them # Our sole job here is to ensure that any write functionality is restricted to relevent configuration before do |resource, method_name| @@ -49,15 +49,15 @@ module RedmineDmsf unless @childern @children = [] if collection? - folder.subfolders.visible.map do |p| + folder.dmsf_folders.select(:title).visible.map do |p| @children.push child(p.title) end - folder.files.visible.map do |p| + folder.dmsf_files.select(:name).visible.map do |p| @children.push child(p.name) end end end - @children + @children end # Does the object exist? @@ -65,7 +65,7 @@ module RedmineDmsf # - 2012-06-15: Only if you're allowed to browse the project # - 2012-06-18: Issue #5, ensure item is only listed if project is enabled for dmsf def exist? - return project && project.module_enabled?('dmsf') && (folder || file) && + return project && project.module_enabled?('dmsf') && (folder || file) && (User.current.admin? || User.current.allowed_to?(:view_dmsf_folders, project)) end @@ -74,38 +74,38 @@ module RedmineDmsf folder.present? # No need to check if entity exists, as false is returned if entity does not exist anyways end - # Check if current entity is a folder and return DmsfFolder object if found (nil if not) + # Check if current entity is a folder and return DmsfFolder object if found (nil if not) def folder unless @folder - return nil unless project # If the project doesn't exist, this entity can't exist + return nil unless project # If the project doesn't exist, this entity can't exist # Note: Folder is searched for as a generic search to prevent SQL queries being generated: - # if we were to look within parent, we'd have to go all the way up the chain as part of the + # if we were to look within parent, we'd have to go all the way up the chain as part of the # existence check, and although I'm sure we'd love to access the hierarchy, I can't yet - # see a practical need for it - folders = DmsfFolder.visible.where(:project_id => project.id, :title => basename).order('title ASC').to_a + # see a practical need for it + folders = DmsfFolder.visible.where(:project_id => project.id, :title => basename).order('title ASC').to_a return nil unless folders.length > 0 if (folders.length > 1) folders.delete_if { |x| '/' + x.dmsf_path_str != projectless_path } return nil unless folders.length > 0 @folder = folders[0] - else + else if (('/' + folders[0].dmsf_path_str) == projectless_path) - @folder = folders[0] + @folder = folders[0] end end end @folder - end + end # Check if current entity exists as a file (DmsfFile), and returns corresponding object if found (nil otherwise) - # Currently has a dual search approach (depending on if parent can be determined) - def file + # Currently has a dual search approach (depending on if parent can be determined) + def file unless @file return nil unless project # Again if entity project is nil, it cannot exist in context of this object # Hunt for files parent path f = false - if (parent.projectless_path != '/') - f = parent.folder if parent.folder + if (parent.projectless_path != '/') + f = parent.folder if parent.folder else f = nil end @@ -114,16 +114,16 @@ module RedmineDmsf # DMSF file search by name. @file = DmsfFile.visible.find_file_by_name(project, f, basename) else - # If folder is false, means it couldn't pick up parent, - # as such its probably fine to bail out, however we'll + # If folder is false, means it couldn't pick up parent, + # as such its probably fine to bail out, however we'll # perform a search in this scenario files = DmsfFile.visible.where(:project_id => project.id, :name => basename).order('name ASC').to_a - files.delete_if { |x| File.dirname('/' + x.dmsf_path_str) != File.dirname(projectless_path) } - @file = files[0] if files.length > 0 + files.delete_if { |x| File.dirname('/' + x.dmsf_path_str) != File.dirname(projectless_path) } + @file = files[0] if files.length > 0 end end @file - end + end # Return the content type of file # will return inode/directory for any collections, and appropriate for File entities @@ -187,7 +187,7 @@ module RedmineDmsf end OK end - + # Process incoming MKCOL request # # Create a DmsfFolder at location requested, only if parent is a folder (or root) @@ -201,10 +201,10 @@ module RedmineDmsf if (parent.projectless_path != '/') return Conflict unless parent.folder parent_folder = parent.folder.id - end + end f = DmsfFolder.new f.title = basename - f.dmsf_folder_id = parent_folder + f.dmsf_folder_id = parent_folder f.project = project f.user = User.current f.save ? OK : Conflict @@ -217,7 +217,7 @@ module RedmineDmsf # # should be of entity to be deleted, we simply follow the Dmsf entity method # for deletion and return of appropriate status based on outcome. - def delete + def delete if file raise Forbidden unless User.current.admin? || User.current.allowed_to?(:file_delete, project) file.delete(false) ? NoContent : Conflict @@ -235,13 +235,13 @@ module RedmineDmsf # TODO: Support overwrite between both types of entity, and implement better checking def move(dest, overwrite) # All of this should carry accrross the ResourceProxy frontend, we ensure this to - # prevent unexpected errors - resource = dest.is_a?(ResourceProxy) ? dest.resource : dest - + # prevent unexpected errors + resource = dest.is_a?(ResourceProxy) ? dest.resource : dest + return PreconditionFailed if !resource.is_a?(DmsfResource) || resource.project.nil? || resource.project.id == 0 - + parent = resource.parent - + if collection? # At the moment we don't support cross project destinations return MethodNotImplemented unless project.id == resource.project.id @@ -255,20 +255,20 @@ module RedmineDmsf folder.dmsf_folder_id = nil else return PreconditionFailed unless parent.exist? && parent.folder - folder.dmsf_folder_id = parent.folder.id + folder.dmsf_folder_id = parent.folder.id end folder.title = resource.basename folder.save ? Created : PreconditionFailed end else - raise Forbidden unless User.current.admin? || - User.current.allowed_to?(:folder_manipulation, project) || - User.current.allowed_to?(:folder_manipulation, resource.project) + raise Forbidden unless User.current.admin? || + User.current.allowed_to?(:folder_manipulation, project) || + User.current.allowed_to?(:folder_manipulation, resource.project) if dest.exist? - methodNotAllowed + methodNotAllowed # Files cannot be merged at this point, until a decision is made on how to merge them - # ideally, we would merge revision history for both, ensuring the origin file wins with latest revision. + # ideally, we would merge revision history for both, ensuring the origin file wins with latest revision. else if(parent.projectless_path == '/') #Project root f = nil @@ -279,12 +279,12 @@ module RedmineDmsf return PreconditionFailed unless exist? && file return InternalServerError unless file.move_to(resource.project, f) - # Update Revision and names of file [We can link to old physical resource, as it's not changed] + # Update Revision and names of file [We can link to old physical resource, as it's not changed] if file.last_revision - file.last_revision.name = resource.basename + file.last_revision.name = resource.basename file.last_revision.title = DmsfFileRevision.filename_to_title(resource.basename) end - file.name = resource.basename + file.name = resource.basename # Save Changes (file.last_revision.save! && file.save!) ? Created : PreconditionFailed @@ -307,9 +307,9 @@ module RedmineDmsf end return PreconditionFailed if !resource.is_a?(DmsfResource) || resource.project.nil? || resource.project.id == 0 - + parent = resource.parent - + if collection? # Current object is a folder, so now we need to figure out information about Destination return MethodNotAllowed if(dest.exist?) @@ -320,7 +320,7 @@ module RedmineDmsf # View folders on destination project :view_dmsf_folders # View files on the source project :view_dmsf_files # View fodlers on the source project :view_dmsf_folders - raise Forbidden unless User.current.admin? || + raise Forbidden unless User.current.admin? || (User.current.allowed_to?(:folder_manipulation, resource.project) && User.current.allowed_to?(:view_dmsf_folders, resource.project) && User.current.allowed_to?(:view_dmsf_files, project) && @@ -356,7 +356,7 @@ module RedmineDmsf return PreconditionFailed unless exist? && file return InternalServerError unless file.copy_to(resource.project, f) - # Update Revision and names of file [We can link to old physical resource, as it's not changed] + # Update Revision and names of file [We can link to old physical resource, as it's not changed] file.last_revision.name = resource.basename if file.last_revision file.name = resource.basename @@ -368,7 +368,7 @@ module RedmineDmsf # Lock Check # Check for the existence of locks - # At present as deletions of folders are not recursive, we do not need to extend + # At present as deletions of folders are not recursive, we do not need to extend # this to cover every file, just queried def lock_check(lock_scope = nil) if file @@ -381,7 +381,7 @@ module RedmineDmsf # Lock def lock(args) return Conflict unless (parent.projectless_path == '/' || parent_exists?) - lock_check(args[:scope]) + lock_check(args[:scope]) unless self.exist? Rails.logger.warn "Path doesn't exist: #{@path}" return super @@ -392,7 +392,7 @@ module RedmineDmsf raise DAV4Rack::LockFailure.new("Failed to lock: #{@path}") else # If scope and type are not defined, the only thing we can - # logically assume is that the lock is being refreshed (office loves + # logically assume is that the lock is being refreshed (office loves # to do this for example, so we do a few checks, try to find the lock # and ultimately extend it, otherwise we return Conflict for any failure if (!args[:scope] && !args[:type]) #Perhaps a lock refresh @@ -459,19 +459,19 @@ module RedmineDmsf raise Forbidden end - # HTTP POST request. - def put(request, response) - raise BadRequest if collection? - raise Forbidden unless User.current.admin? || User.current.allowed_to?(:file_manipulation, project) - - # Ignore Mac OS X resource forks and special Windows files. - if basename.match(/^\._/i) || basename.match(/^Thumbs.db$/i) + # HTTP POST request. + def put(request, response) + raise BadRequest if collection? + raise Forbidden unless User.current.admin? || User.current.allowed_to?(:file_manipulation, project) + + # Ignore Mac OS X resource forks and special Windows files. + if basename.match(/^\._/i) || basename.match(/^\.DS_Store$/i) || basename.match(/^Thumbs.db$/i) Rails.logger.info "#{basename} ignored" return NoContent end new_revision = DmsfFileRevision.new - + if exist? # We're over-writing something, so ultimately a new revision f = file last_revision = file.last_revision @@ -486,21 +486,21 @@ module RedmineDmsf f = DmsfFile.new f.project = project f.name = basename - f.folder = parent.folder + f.dmsf_folder = parent.folder f.notification = !Setting.plugin_redmine_dmsf['dmsf_default_notifications'].blank? new_revision.minor_version = 0 new_revision.major_version = 0 end - - new_revision.file = f + + new_revision.dmsf_file = f new_revision.user = User.current new_revision.name = basename new_revision.title = DmsfFileRevision.filename_to_title(basename) new_revision.description = nil - new_revision.comment = nil - new_revision.increase_version(1, true) + new_revision.comment = nil + new_revision.increase_version(1) new_revision.mime_type = Redmine::MimeType.of(new_revision.name) - + # Phusion passenger does not have a method "length" in its model # however, includes a size method - so we instead use reflection # to determine best approach to problem @@ -512,14 +512,8 @@ module RedmineDmsf new_revision.size = request.content_length # Bad Guess end - # Ignore Mac OS X resource forks and special Windows files. - unless new_revision.size > 0 - Rails.logger.info "#{basename} #{new_revision.size}b ignored" - return Created - end - raise InternalServerError unless new_revision.valid? && f.save - + new_revision.disk_filename = new_revision.new_storage_filename if new_revision.save @@ -538,18 +532,18 @@ module RedmineDmsf def get_property(element) raise NotImplemented if (element[:ns_href] != 'DAV:') unless folder - return NotFound unless (file && file.last_revision && File.exist?(file.last_revision.disk_file)) + return NotFound unless (file && file.last_revision) end case element[:name] when 'supportedlock' - supported_lock - when 'lockdiscovery' - discover_lock - else + supportedlock + when 'lockdiscovery' + lockdiscovery + else super end end - + # Available properties def properties %w(creationdate displayname getlastmodified getetag resourcetype getcontenttype getcontentlength supportedlock lockdiscovery).collect do |prop| @@ -559,7 +553,7 @@ module RedmineDmsf private # Prepare file for download using Rack functionality: - # Download (see RedmineDmsf::Webdav::Download) extends Rack::File to allow single-file + # Download (see RedmineDmsf::Webdav::Download) extends Rack::File to allow single-file # implementation of service for request, which allows for us to pipe a single file through # also best-utilising DAV4Rack's implementation. def download @@ -567,37 +561,34 @@ module RedmineDmsf # If there is no range (start of ranged download, or direct download) then we log the # file access, so we can properly keep logged information - if @request.env['HTTP_RANGE'].nil? + if @request.env['HTTP_RANGE'].nil? access = DmsfFileRevisionAccess.new access.user = User.current - access.revision = file.last_revision + access.dmsf_file_revision = file.last_revision access.action = DmsfFileRevisionAccess::DownloadAction access.save! end Download.new(file.last_revision.disk_file) end - # discover_lock # As the name suggests, we're returning lock recovery information for requested resource - def discover_lock + def lockdiscovery x = Nokogiri::XML::DocumentFragment.parse '' entity = file || folder return nil unless entity.locked? - if !entity.folder.nil? && entity.folder.locked? - locks = entity.lock.reverse[0].folder.locks(false)# longwinded way of getting base items locks + if entity.dmsf_folder && entity.dmsf_folder.locked? + locks = entity.lock.reverse[0].dmsf_folder.locks(false) # longwinded way of getting base items locks else locks = entity.lock(false) end - + Nokogiri::XML::Builder.with(x) do |doc| doc.lockdiscovery { - locks.each {|lock| + locks.each do |lock| next if lock.expired? doc.activelock { - doc.locktype { - doc.write - } + doc.locktype { doc.write } doc.lockscope { if lock.lock_scope == :scope_exclusive doc.exclusive @@ -605,37 +596,30 @@ module RedmineDmsf doc.shared end } - if lock.folder.nil? - doc.depth '0' - else - doc.depth 'infinity' - end + doc.depth lock.folder.nil? ? '0' : 'infinity' doc.owner lock.user.to_s if lock.expires_at.nil? - doc.timeout = 'Infinite' + doc.timeout 'Infinite' else doc.timeout "Second-#{(lock.expires_at.to_i - Time.now.to_i)}" end - lock_entity = lock.folder || lock.file lock_path = "#{request.scheme}://#{request.host}:#{request.port}#{path_prefix}#{URI.escape(lock_entity.project.identifier)}/" lock_path << lock_entity.dmsf_path.map {|x| URI.escape(x.respond_to?('name') ? x.name : x.title) }.join('/') lock_path << '/' if lock_entity.is_a?(DmsfFolder) && lock_path[-1,1] != '/' doc.lockroot { doc.href lock_path } - if (lock.user.id == User.current.id || User.current.allowed_to?(:force_file_unlock, self.project)) + if ((lock.user.id == User.current.id) || User.current.allowed_to?(:force_file_unlock, self.project)) doc.locktoken { doc.href lock.uuid } end } - } + end } end - x end - # supported_lock # As the name suggests, we're returning locks supported by our implementation - def supported_lock + def supportedlock x = Nokogiri::XML::DocumentFragment.parse '' Nokogiri::XML::Builder.with(x) do |doc| doc.supportedlock { diff --git a/lib/redmine_dmsf/webdav/download.rb b/lib/redmine_dmsf/webdav/download.rb index f7e2698d..4a2cb945 100644 --- a/lib/redmine_dmsf/webdav/download.rb +++ b/lib/redmine_dmsf/webdav/download.rb @@ -45,7 +45,7 @@ module RedmineDmsf if available serving(env) else - raise NotFound + fail(404, "File not found: #{@path}") end end end diff --git a/lib/redmine_dmsf/webdav/index_resource.rb b/lib/redmine_dmsf/webdav/index_resource.rb index bc622ef3..bc39e8b4 100644 --- a/lib/redmine_dmsf/webdav/index_resource.rb +++ b/lib/redmine_dmsf/webdav/index_resource.rb @@ -26,7 +26,8 @@ module RedmineDmsf def children unless @projects @projects = [] - Project.has_module(:dmsf).where(Project.allowed_to_condition( + Project.select(:identifier).has_module(:dmsf).where( + Project.allowed_to_condition( User.current, :view_dmsf_folders)).order('lft').all.each do |p| @projects << child(p.identifier) end diff --git a/lib/redmine_dmsf/webdav/no_parse.rb b/lib/redmine_dmsf/webdav/no_parse.rb deleted file mode 100644 index bef4080c..00000000 --- a/lib/redmine_dmsf/webdav/no_parse.rb +++ /dev/null @@ -1,51 +0,0 @@ -# Redmine plugin for Document Management System "Features" -# -# Copyright (C) 2012 Daniel Munn -# -# 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 - class NoParse - def initialize(app, options={}) - @app = app - @urls = options[:urls] - end - - def call(env) - if env['REQUEST_METHOD'] == "PUT" && env.has_key?('CONTENT_TYPE') then - if (@urls.dup.delete_if {|x| !env['PATH_INFO'].starts_with? x}.length > 0) then - logger "RedmineDmsf::NoParse prevented mime parsing for PUT #{env['PATH_INFO']}" - env['CONTENT_TYPE'] = 'text/plain' - end - end - @app.call(env) - end - - private - - def logger(env) - env['action_dispatch.logger'] || Logger.new($stdout) - end - - end -end - -# Todo: -# This should probably be configurable somehow or better have the module hunt for the correct pathing -# automatically without the need to add a "/dmsf/webdav" configuration to it, as if the route is changed -# the functonality of this patch will effectively break. -Rails.configuration.middleware.insert_before( - ActionDispatch::ParamsParser, - RedmineDmsf::NoParse, :urls => ["#{Redmine::Utils::relative_url_root}/dmsf/webdav"]) \ No newline at end of file diff --git a/lib/redmine_dmsf/webdav/project_resource.rb b/lib/redmine_dmsf/webdav/project_resource.rb index 73d19be4..459f0d96 100644 --- a/lib/redmine_dmsf/webdav/project_resource.rb +++ b/lib/redmine_dmsf/webdav/project_resource.rb @@ -27,10 +27,10 @@ module RedmineDmsf unless @children @children = [] if project - project.dmsf_folders.visible.map do |p| + project.dmsf_folders.select(:title).visible.map do |p| @children.push child(p.title) end - project.dmsf_files.visible.map do |p| + project.dmsf_files.select(:name).visible.map do |p| @children.push child(p.name) end end diff --git a/lib/redmine_dmsf/webdav/resource_proxy.rb b/lib/redmine_dmsf/webdav/resource_proxy.rb index fa1f446f..15b933d4 100644 --- a/lib/redmine_dmsf/webdav/resource_proxy.rb +++ b/lib/redmine_dmsf/webdav/resource_proxy.rb @@ -43,7 +43,7 @@ module RedmineDmsf end @resource_c.accessor = self if @resource_c end - + def authenticate(username, password) # Bugfix: Current DAV4Rack (including production) authenticate against ALL requests # Microsoft Web Client will not attempt any authentication (it'd seem) until it's acknowledged @@ -52,11 +52,11 @@ module RedmineDmsf # seems the next best step, if the request method is OPTIONS return true, controller will simply # call the options method within, which accesses nothing, just returns headers about dav env. return true if @request.request_method.downcase == 'options' && (path == '/' || path.empty?) - User.current = User.try_to_login(username, password) || nil - return !User.current.anonymous? unless User.current.nil? - false + return false unless username && password + User.current = User.try_to_login(username, password) + return User.current && !User.current.anonymous? end - + def supports_locking? true end @@ -100,30 +100,30 @@ module RedmineDmsf def get(request, response) @resource_c.get(request, response) end - - def put(request, response) + + def put(request, response) @resource_c.put(request, response) end - + def delete @resource_c.delete end - + def copy(dest, overwrite = false) @resource_c.copy(dest, overwrite) end - + def move(dest, overwrite = false) @resource_c.move(dest, overwrite) - end + end def make_collection @resource_c.make_collection - end + end def special_type @resource_c.special_type - end + end def lock(args) @resource_c.lock(args) @@ -136,7 +136,7 @@ module RedmineDmsf def unlock(token) @resource_c.unlock(token) end - + def name @resource_c.name end @@ -144,7 +144,7 @@ module RedmineDmsf def long_name @resource_c.long_name end - + def resource @resource_c end diff --git a/lib/tasks/dmsf_convert_documents.rake b/lib/tasks/dmsf_convert_documents.rake index 4210dc74..30f7a61e 100644 --- a/lib/tasks/dmsf_convert_documents.rake +++ b/lib/tasks/dmsf_convert_documents.rake @@ -3,7 +3,7 @@ # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -31,6 +31,7 @@ Available options: Example: rake redmine:dmsf_convert_documents project=test RAILS_ENV="production" + rake redmine:dmsf_convert_documents project=test dry=true RAILS_ENV="production" END_DESC class DmsfConvertDocuments @@ -66,8 +67,13 @@ class DmsfConvertDocuments folder = DmsfFolder.new - folder.project = project - folder.user = document.attachments.reorder("#{Attachment.table_name}.created_on ASC").first.try(:author) + folder.project = project + attachment = document.attachments.reorder("#{Attachment.table_name}.created_on ASC").first + if attachment + folder.user = attachment.author + else + folder.user = Users.active.where(:admin => true).first + end folder.title = document.title folder.title.gsub!(/[\/\\\?":<>]/, '-') if replace @@ -106,7 +112,7 @@ class DmsfConvertDocuments begin file = DmsfFile.new file.project = project - file.folder = folder + file.dmsf_folder = folder file.name = attachment.filename i = 1 @@ -137,7 +143,7 @@ class DmsfConvertDocuments end revision = DmsfFileRevision.new - revision.file = file + revision.dmsf_file = file revision.name = file.name revision.title = DmsfFileRevision.filename_to_title(attachment.filename) revision.description = attachment.description diff --git a/lib/tasks/dmsf_create_digests.rake b/lib/tasks/dmsf_create_digests.rake new file mode 100644 index 00000000..628be4eb --- /dev/null +++ b/lib/tasks/dmsf_create_digests.rake @@ -0,0 +1,63 @@ +# encoding: utf-8 +# +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-16 Karel Pičman +# +# 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. + +desc <<-END_DESC +DMSF maintenance task + * Create missing MD5 digest for all file revisions + +Available options: + *dry_run - test, no changes to the database + +Example: + bundle exec rake redmine:dmsf_create_digests RAILS_ENV="production" + bundle exec rake redmine:dmsf_create_digests dry_run=1 RAILS_ENV="production" +END_DESC + +namespace :redmine do + task :dmsf_create_digests => :environment do + m = DmsfDigest.new + m.create_digests + end +end + +class DmsfDigest + + def initialize + @dry_run = ENV['dry_run'] + end + + def create_digests + revisions = DmsfFileRevision.where("digest IS NULL OR digest = ''").all + count = revisions.count + n = 0 + revisions.each_with_index do |rev, i| + if rev.create_digest + rev.save unless @dry_run + n += 1 + end + # Progress bar + print "\r#{i * 100 / count}%" + end + print "\r100%\n" + # Result + puts "#{n}/#{DmsfFileRevision.count} revisions updated." + end + +end diff --git a/test/fixtures/dmsf_workflows.yml b/test/fixtures/dmsf_workflows.yml index ebb4d1cf..ed0981cb 100644 --- a/test/fixtures/dmsf_workflows.yml +++ b/test/fixtures/dmsf_workflows.yml @@ -6,4 +6,9 @@ wf1: wf2: id: 2 - name: wf2 \ No newline at end of file + name: wf2 + +wf3: + id: 3 + name: wf3 + status: 0 \ No newline at end of file diff --git a/test/functional/dmsf_controller_test.rb b/test/functional/dmsf_controller_test.rb index ad21abb7..e6872de2 100644 --- a/test/functional/dmsf_controller_test.rb +++ b/test/functional/dmsf_controller_test.rb @@ -20,14 +20,14 @@ require File.expand_path('../../test_helper', __FILE__) -class DmsfControllerTest < RedmineDmsf::Test::TestCase - include Redmine::I18n - - fixtures :users, :email_addresses, :dmsf_folders, :custom_fields, - :custom_values, :projects, :roles, :members, :member_roles, :dmsf_links, +class DmsfControllerTest < RedmineDmsf::Test::TestCase + include Redmine::I18n + + fixtures :users, :email_addresses, :dmsf_folders, :custom_fields, + :custom_values, :projects, :roles, :members, :member_roles, :dmsf_links, :dmsf_files, :dmsf_file_revisions - def setup + def setup @project = Project.find_by_id 1 assert_not_nil @project @project.enable_module! :dmsf @@ -39,11 +39,12 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase @folder_link1 = DmsfLink.find_by_id 1 @role = Role.find_by_id 1 @custom_field = CustomField.find_by_id 21 - @custom_value = CustomValue.find_by_id 21 + @custom_value = CustomValue.find_by_id 21 + @user_member = User.find_by_id 2 User.current = nil - @request.session[:user_id] = 2 + @request.session[:user_id] = @user_member.id end - + def test_truth assert_kind_of Project, @project assert_kind_of DmsfFolder, @folder1 @@ -54,16 +55,17 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase assert_kind_of DmsfLink, @folder_link1 assert_kind_of Role, @role assert_kind_of CustomField, @custom_field - assert_kind_of CustomValue, @custom_value + assert_kind_of CustomValue, @custom_value + assert_kind_of User, @user_member end - + def test_edit_folder_forbidden - # Missing permissions + # Missing permissions get :edit, :id => @project, :folder_id => @folder1 - assert_response :forbidden + assert_response :forbidden end - - def test_edit_folder_allowed + + def test_edit_folder_allowed # Permissions OK @role.add_permission! :view_dmsf_folders @role.add_permission! :folder_manipulation @@ -72,58 +74,58 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase assert_select 'label', { :text => @custom_field.name } assert_select 'option', { :value => @custom_value.value } end - + def test_trash_forbidden # Missing permissions get :trash, :id => @project - assert_response :forbidden + assert_response :forbidden end - - def test_trash_allowed + + def test_trash_allowed # Permissions OK @role.add_permission! :file_delete get :trash, :id => @project assert_response :success assert_select 'h2', { :text => l(:link_trash_bin) } end - + def test_delete_forbidden # Missing permissions get :delete, :id => @project, :folder_id => @folder1.id, :commit => false assert_response :forbidden end - + def test_delete_not_empty # Permissions OK but the folder is not empty @role.add_permission! :folder_manipulation get :delete, :id => @project, :folder_id => @folder1.id, :commit => false - assert_response :redirect + assert_response :redirect assert_include l(:error_folder_is_not_empty), flash[:error] end - + def test_delete_locked # Permissions OK but the folder is locked @role.add_permission! :folder_manipulation get :delete, :id => @project, :folder_id => @folder2.id, :commit => false - assert_response :redirect + assert_response :redirect assert_include l(:error_folder_is_locked), flash[:error] end - + def test_delete_ok - # Empty and not locked folder + # Empty and not locked folder @role.add_permission! :folder_manipulation get :delete, :id => @project, :folder_id => @folder4.id, :commit => false - assert_response :redirect + assert_response :redirect end - - def test_restore_forbidden - # Missing permissions + + def test_restore_forbidden + # Missing permissions @folder4.deleted = 1 @folder4.save get :restore, :id => @project, :folder_id => @folder4.id assert_response :forbidden end - + def test_restore_ok # Permissions OK @request.env['HTTP_REFERER'] = trash_dmsf_path(:id => @project.id) @@ -131,49 +133,67 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase @folder4.deleted = 1 @folder4.save get :restore, :id => @project, :folder_id => @folder4.id - assert_response :redirect + assert_response :redirect end - + 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], + 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] - assert_response :forbidden + assert_response :forbidden end - + def test_delete_restore_not_empty - # Permissions OK but the folder is not empty - @request.env['HTTP_REFERER'] = dmsf_folder_path(:id => @project.id) + # Permissions OK but the folder is not empty + @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], + 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] assert_response :redirect assert_equal flash[:error].to_s, l(:error_folder_is_not_empty) end - + def test_delete_restore_entries_ok # Permissions OK @request.env['HTTP_REFERER'] = dmsf_folder_path(:id => @project.id) @role.add_permission! :view_dmsf_files flash[:error] = nil - get :entries_operation, :id => @project, :delete_entries => 'Delete', - :subfolders => [], :files => [@file1.id.to_s], + get :entries_operation, :id => @project, :delete_entries => 'Delete', + :subfolders => [], :files => [@file1.id.to_s], :dir_links => [], :file_links => [@file_link2.id.to_s] assert_response :redirect assert_nil flash[:error] end - + def test_restore_entries # Restore @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], + @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] assert_response :redirect assert_nil flash[:error] end - + + def test_show + @role.add_permission! :view_dmsf_files + @role.add_permission! :view_dmsf_folders + get :show, :id => @project.id + assert_response :success + assert_select 'tr.dmsf_tree', :count => 0 + end + + 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 + get :show, :id => @project.id + assert_response :success + assert_select 'tr.dmsf_tree' + end + end \ No newline at end of file diff --git a/test/functional/dmsf_workflow_controller_test.rb b/test/functional/dmsf_workflow_controller_test.rb index 0564d1ee..07e3360c 100644 --- a/test/functional/dmsf_workflow_controller_test.rb +++ b/test/functional/dmsf_workflow_controller_test.rb @@ -22,15 +22,15 @@ require File.expand_path('../../test_helper', __FILE__) class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase include Redmine::I18n - - fixtures :users, :email_addresses, :dmsf_workflows, :dmsf_workflow_steps, - :projects, :roles, :members, :member_roles, :dmsf_workflow_step_assignments, + + fixtures :users, :email_addresses, :dmsf_workflows, :dmsf_workflow_steps, + :projects, :roles, :members, :member_roles, :dmsf_workflow_step_assignments, :dmsf_file_revisions, :dmsf_files - + def setup - @user_admin = User.find_by_id 1 # Redmine admin - @user_member = User.find_by_id 2 # John Smith - manager - @user_non_member = User.find_by_id 3 # Dave Lopper + @user_admin = User.find_by_id 1 # Redmine admin + @user_member = User.find_by_id 2 # John Smith - manager + @user_non_member = User.find_by_id 3 # Dave Lopper @role_manager = Role.find_by_name('Manager') @role_manager.add_permission! :file_manipulation @role_manager.add_permission! :manage_workflows @@ -39,21 +39,22 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase @wfs2 = DmsfWorkflowStep.find_by_id 2 # step 2 @wfs3 = DmsfWorkflowStep.find_by_id 3 # step 1 @wfs4 = DmsfWorkflowStep.find_by_id 4 # step 2 - @wfs5 = DmsfWorkflowStep.find_by_id 5 # step 3 - @project1 = Project.find_by_id 1 + @wfs5 = DmsfWorkflowStep.find_by_id 5 # step 3 + @project1 = Project.find_by_id 1 @project1.enable_module! :dmsf @wf1 = DmsfWorkflow.find_by_id 1 + @wf3 = DmsfWorkflow.find_by_id 3 @wfsa2 = DmsfWorkflowStepAssignment.find_by_id 2 @revision1 = DmsfFileRevision.find_by_id 1 @revision2 = DmsfFileRevision.find_by_id 2 - @revision3 = DmsfFileRevision.find_by_id 3 + @revision3 = DmsfFileRevision.find_by_id 3 @file1 = DmsfFile.find_by_id 1 @file2 = DmsfFile.find_by_id 2 @request.env['HTTP_REFERER'] = dmsf_folder_path(:id => @project1.id) User.current = nil - @request.session[:user_id] = @user_member.id + @request.session[:user_id] = @user_member.id end - + def test_truth assert_kind_of User, @user_admin assert_kind_of User, @user_member @@ -64,8 +65,9 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase assert_kind_of DmsfWorkflowStep, @wfs3 assert_kind_of DmsfWorkflowStep, @wfs4 assert_kind_of DmsfWorkflowStep, @wfs5 - assert_kind_of Project, @project1 + assert_kind_of Project, @project1 assert_kind_of DmsfWorkflow, @wf1 + assert_kind_of DmsfWorkflow, @wf3 assert_kind_of DmsfWorkflowStepAssignment, @wfsa2 assert_kind_of DmsfFileRevision, @revision1 assert_kind_of DmsfFileRevision, @revision2 @@ -73,50 +75,50 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase assert_kind_of DmsfFile, @file1 assert_kind_of DmsfFile, @file2 end - + def test_authorize_admin # Admin @request.session[:user_id] = @user_admin.id get :index assert_response :success - assert_template 'index' + assert_template 'index' end - + def test_authorize_member # Non member - @request.session[:user_id] = @user_non_member.id + @request.session[:user_id] = @user_non_member.id get :index, :project_id => @project1.id - assert_response :forbidden + assert_response :forbidden end - - def test_authorize_administration + + def test_authorize_administration # Administration get :index - assert_response :forbidden + assert_response :forbidden end - - def test_authorize_projects - # Project + + def test_authorize_projects + # Project get :index, :project_id => @project1.id assert_response :success assert_template 'index' end - + def test_authorize_manage_workflows_forbidden # Without permissions @role_manager.remove_permission! :manage_workflows get :index, :project_id => @project1.id assert_response :forbidden end - + def test_authorization_file_approval_ok - @role_manager.add_permission! :file_approval + @role_manager.add_permission! :file_approval @revision2.dmsf_workflow_id = @wf1.id get :start, :id => @revision2.dmsf_workflow_id, :dmsf_file_revision_id => @revision2.id assert_response :redirect end - + def test_authorization_file_approval_forbidden @role_manager.remove_permission! :file_approval @revision2.dmsf_workflow_id = @wf1.id @@ -124,67 +126,87 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase :dmsf_file_revision_id => @revision2.id assert_response :forbidden end - + def test_authorization_no_module - # Without the module + # Without the module @role_manager.add_permission! :file_manipulation - @project1.disable_module!(:dmsf) + @project1.disable_module!(:dmsf) get :index, :project_id => @project1.id - assert_response :forbidden + assert_response :forbidden end - - def test_index + + def test_index_administration + @request.session[:user_id] = @user_admin.id + get :index + assert_response :success + assert_template 'index' + end + + def test_index_project get :index, :project_id => @project1.id assert_response :success assert_template 'index' end - def test_new + def test_new get :new, :project_id => @project1.id assert_response :success assert_template 'new' - end + end + + def test_lock + put :update, :id => @wf1.id, :dmsf_workflow => { :status => DmsfWorkflow::STATUS_LOCKED } + @wf1.reload + assert @wf1.locked?, "#{@wf1.name} status is #{@wf1.status}" + end + + def test_unlock + @request.session[:user_id] = @user_admin.id + put :update, :id => @wf3.id, :dmsf_workflow => { :status => DmsfWorkflow::STATUS_ACTIVE } + @wf3.reload + assert @wf3.active?, "#{@wf3.name} status is #{@wf3.status}" + end def test_show get :show, :id => @wf1.id assert_response :success - assert_template 'show' + assert_template 'show' end - - def test_create - assert_difference 'DmsfWorkflow.count', +1 do - post :create, :dmsf_workflow => {:name => 'wf3'}, :project_id => @project1.id - end - assert_redirected_to settings_project_path(@project1, :tab => 'dmsf_workflow') + + def test_create + assert_difference 'DmsfWorkflow.count', +1 do + post :create, :dmsf_workflow => {:name => 'wf4', :project_id => @project1.id} + end + assert_redirected_to settings_project_path(@project1, :tab => 'dmsf_workflow') end - - def test_update + + def test_update put :update, :id => @wf1.id, :dmsf_workflow => {:name => 'wf1a'} @wf1.reload - assert_equal 'wf1a', @wf1.name + assert_equal 'wf1a', @wf1.name end - - def test_destroy + + def test_destroy assert_difference 'DmsfWorkflow.count', -1 do delete :destroy, :id => @wf1.id end - assert_redirected_to settings_project_path(@project1, :tab => 'dmsf_workflow') - assert_equal 0, DmsfWorkflowStep.where(:dmsf_workflow_id => @wf1.id).all.count + assert_redirected_to settings_project_path(@project1, :tab => 'dmsf_workflow') + assert_equal 0, DmsfWorkflowStep.where(:dmsf_workflow_id => @wf1.id).all.count end - - def test_add_step - assert_difference 'DmsfWorkflowStep.count', +1 do + + def test_add_step + assert_difference 'DmsfWorkflowStep.count', +1 do post :add_step, :commit => l(:dmsf_or), :step => 1, :id => @wf1.id, :user_ids => [@user_non_member.id] end assert_response :success - ws = DmsfWorkflowStep.order('id DESC').first + ws = DmsfWorkflowStep.order('id DESC').first assert_equal @wf1.id, ws.dmsf_workflow_id assert_equal 1, ws.step assert_equal @user_non_member.id, ws.user_id assert_equal DmsfWorkflowStep::OPERATOR_OR, ws.operator end - - def test_remove_step + + def test_remove_step n = DmsfWorkflowStep.where(:dmsf_workflow_id => @wf1.id, :step => 1).count assert_difference 'DmsfWorkflowStep.count', -n do delete :remove_step, :step => @wfs1.id, :id => @wf1.id @@ -193,158 +215,158 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase ws = DmsfWorkflowStep.where(:dmsf_workflow_id => @wf1.id).order('id ASC').first assert_equal 1, ws.step end - - def test_reorder_steps_to_lower + + def test_reorder_steps_to_lower put :reorder_steps, :step => 1, :id => @wf1.id, :workflow_step => {:move_to => 'lower'} - assert_response :success + assert_response :success @wfs1.reload @wfs2.reload @wfs3.reload @wfs4.reload - @wfs5.reload + @wfs5.reload assert_equal 1, @wfs2.step assert_equal 1, @wfs3.step assert_equal 2, @wfs1.step assert_equal 2, @wfs4.step assert_equal 3, @wfs5.step end - - def test_reorder_steps_to_lowest + + def test_reorder_steps_to_lowest put :reorder_steps, :step => 1, :id => @wf1.id, :workflow_step => {:move_to => 'lowest'} - assert_response :success + assert_response :success @wfs1.reload @wfs2.reload @wfs3.reload @wfs4.reload - @wfs5.reload - assert_equal 1, @wfs2.step + @wfs5.reload + assert_equal 1, @wfs2.step assert_equal 1, @wfs3.step assert_equal 2, @wfs5.step assert_equal 3, @wfs1.step assert_equal 3, @wfs4.step - end - - def test_reorder_steps_to_higher + end + + def test_reorder_steps_to_higher put :reorder_steps, :step => 3, :id => @wf1.id, :workflow_step => {:move_to => 'higher'} - assert_response :success + assert_response :success @wfs1.reload @wfs2.reload @wfs3.reload @wfs4.reload - @wfs5.reload + @wfs5.reload assert_equal 1, @wfs1.step assert_equal 1, @wfs4.step assert_equal 2, @wfs5.step - assert_equal 3, @wfs2.step - assert_equal 3, @wfs3.step + assert_equal 3, @wfs2.step + assert_equal 3, @wfs3.step end - - def test_reorder_steps_to_highest + + def test_reorder_steps_to_highest put :reorder_steps, :step => 3, :id => @wf1.id, :workflow_step => {:move_to => 'highest'} - assert_response :success + assert_response :success @wfs1.reload @wfs2.reload @wfs3.reload @wfs4.reload - @wfs5.reload + @wfs5.reload assert_equal 1, @wfs5.step assert_equal 2, @wfs1.step assert_equal 2, @wfs4.step - assert_equal 3, @wfs2.step - assert_equal 3, @wfs3.step + assert_equal 3, @wfs2.step + assert_equal 3, @wfs3.step end - - def test_action_approve - post( - :new_action, - :commit => l(:button_submit), - :id => @wf1.id, - :dmsf_workflow_step_assignment_id => @wfsa2.id, + + def test_action_approve + post( + :new_action, + :commit => l(:button_submit), + :id => @wf1.id, + :dmsf_workflow_step_assignment_id => @wfsa2.id, :dmsf_file_revision_id => @revision1.id, :step_action => DmsfWorkflowStepAction::ACTION_APPROVE, :user_id => nil, - :note => '') + :note => '') assert_redirected_to dmsf_folder_path(:id => @project1.id) assert DmsfWorkflowStepAction.where( - :dmsf_workflow_step_assignment_id => @wfsa2.id, - :action => DmsfWorkflowStepAction::ACTION_APPROVE).first - end - - def test_action_reject - post( - :new_action, - :commit => l(:button_submit), - :id => @wf1.id, - :dmsf_workflow_step_assignment_id => @wfsa2.id, - :dmsf_file_revision_id => @revision2.id, - :step_action => DmsfWorkflowStepAction::ACTION_REJECT, - :note => 'Rejected because...') - assert_response :redirect - assert DmsfWorkflowStepAction.where( - :dmsf_workflow_step_assignment_id => @wfsa2.id, - :action => DmsfWorkflowStepAction::ACTION_REJECT).first + :dmsf_workflow_step_assignment_id => @wfsa2.id, + :action => DmsfWorkflowStepAction::ACTION_APPROVE).first end - def test_action + def test_action_reject + post( + :new_action, + :commit => l(:button_submit), + :id => @wf1.id, + :dmsf_workflow_step_assignment_id => @wfsa2.id, + :dmsf_file_revision_id => @revision2.id, + :step_action => DmsfWorkflowStepAction::ACTION_REJECT, + :note => 'Rejected because...') + assert_response :redirect + assert DmsfWorkflowStepAction.where( + :dmsf_workflow_step_assignment_id => @wfsa2.id, + :action => DmsfWorkflowStepAction::ACTION_REJECT).first + end + + def test_action xhr( :get, :action, - :project_id => @project1.id, - :id => @wf1.id, + :project_id => @project1.id, + :id => @wf1.id, :dmsf_workflow_step_assignment_id => @wfsa2.id, :dmsf_file_revision_id => @revision2.id, - :title => l(:title_waiting_for_approval)) + :title => l(:title_waiting_for_approval)) assert_response :success assert_match /ajax-modal/, response.body assert_template 'action' end - - def test_new_action_delegate - post( - :new_action, - :commit => l(:button_submit), - :id => @wf1.id, - :dmsf_workflow_step_assignment_id => @wfsa2.id, + + def test_new_action_delegate + post( + :new_action, + :commit => l(:button_submit), + :id => @wf1.id, + :dmsf_workflow_step_assignment_id => @wfsa2.id, :dmsf_file_revision_id => @revision2.id, :step_action => @user_admin.id * 10, - :note => 'Delegated because...') + :note => 'Delegated because...') assert_redirected_to dmsf_folder_path(:id => @project1.id) assert DmsfWorkflowStepAction.where( - :dmsf_workflow_step_assignment_id => @wfsa2.id, - :action => DmsfWorkflowStepAction::ACTION_DELEGATE).first + :dmsf_workflow_step_assignment_id => @wfsa2.id, + :action => DmsfWorkflowStepAction::ACTION_DELEGATE).first @wfsa2.reload - assert_equal @wfsa2.user_id, @user_admin.id + assert_equal @wfsa2.user_id, @user_admin.id end - - def test_assign - xhr( - :get, - :assign, + + def test_assign + xhr( + :get, + :assign, :project_id => @project1.id, - :id => @wf1.id, - :dmsf_file_revision_id => @revision1.id, + :id => @wf1.id, + :dmsf_file_revision_id => @revision1.id, :title => l(:label_dmsf_wokflow_action_assign)) assert_response :success assert_match /ajax-modal/, response.body - assert_template 'assign' + assert_template 'assign' end - + def test_start - @revision2.dmsf_workflow_id = @wf1.id + @revision2.dmsf_workflow_id = @wf1.id get :start, :id => @revision2.dmsf_workflow_id,:dmsf_file_revision_id => @revision2.id assert_redirected_to dmsf_folder_path(:id => @project1.id) end - - def test_assignment - post( - :assignment, - :commit => l(:button_submit), - :id => @wf1.id, - :dmsf_workflow_id => @wf1.id, + + def test_assignment + post( + :assignment, + :commit => l(:button_submit), + :id => @wf1.id, + :dmsf_workflow_id => @wf1.id, :dmsf_file_revision_id => @revision2.id, :action => 'assignment', - :project_id => @project1.id) - assert_response :redirect + :project_id => @project1.id) + assert_response :redirect end end diff --git a/test/integration/dmsf_webdav_delete_test.rb b/test/integration/dmsf_webdav_delete_test.rb index ce4da8d4..3dfe6a91 100644 --- a/test/integration/dmsf_webdav_delete_test.rb +++ b/test/integration/dmsf_webdav_delete_test.rb @@ -104,7 +104,7 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest delete "/dmsf/webdav/#{@project1.identifier}/#{@file1.name}", nil, @admin assert_response :success # If its in the 20x range it's acceptable, should be 204. @file1.reload - assert @file1.deleted, "File #{@file1.name} hasn't been deleted" + assert @file1.deleted?, "File #{@file1.name} hasn't been deleted" end def test_unathorized_user @@ -112,7 +112,7 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest delete "/dmsf/webdav/#{@project1.identifier}/#{@file1.name}", nil, @jsmith assert_response :missing # Without folder_view permission, he will not even be aware of its existence. @file1.reload - assert !@file1.deleted, "File #{@file1.name} is expected to exist" + assert !@file1.deleted?, "File #{@file1.name} is expected to exist" end def test_unathorized_user_forbidden @@ -121,7 +121,7 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest delete "/dmsf/webdav/#{@project1.identifier}/#{@file1.name}", nil, @jsmith assert_response :forbidden # Now jsmith's role has view_folder rights, however they do not hold file manipulation rights. @file1.reload - assert !@file1.deleted, "File #{@file1.name} is expected to exist" + assert !@file1.deleted?, "File #{@file1.name} is expected to exist" end def test_view_folder_not_allowed @@ -130,7 +130,7 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest delete "/dmsf/webdav/#{@project1.identifier}/#{@folder1.title}", nil, @jsmith assert_response :missing # Without folder_view permission, he will not even be aware of its existence. @folder1.reload - assert !@folder1.deleted, "Folder #{@folder1.title} is expected to exist" + assert !@folder1.deleted?, "Folder #{@folder1.title} is expected to exist" end def test_folder_manipulation_not_allowed @@ -139,7 +139,7 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest delete "/dmsf/webdav/#{@project1.identifier}/#{@folder1.title}", nil, @jsmith assert_response :forbidden # Without manipulation permission, action is forbidden. @folder1.reload - assert !@folder1.deleted, "Foler #{@folder1.title} is expected to exist" + assert !@folder1.deleted?, "Foler #{@folder1.title} is expected to exist" end def test_folder_delete_by_admin @@ -147,7 +147,7 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest delete "/dmsf/webdav/#{@project1.identifier}/#{@folder6.title}", nil, @admin assert_response :success @folder6.reload - assert @folder6.deleted, "Folder #{@folder1.title} is not expected to exist" + assert @folder6.deleted?, "Folder #{@folder1.title} is not expected to exist" end def test_folder_delete_by_user @@ -157,7 +157,7 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest delete "/dmsf/webdav/#{@project1.identifier}/#{@folder6.title}", nil, @jsmith assert_response :success @folder6.reload - assert @folder6.deleted, "Folder #{@folder1.title} is not expected to exist" + assert @folder6.deleted?, "Folder #{@folder1.title} is not expected to exist" end def test_file_delete_by_administrator @@ -165,7 +165,7 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest delete "/dmsf/webdav/#{@project1.identifier}/#{@file1.name}", nil, @admin assert_response :success @file1.reload - assert @file1.deleted, "File #{@file1.name} is not expected to exist" + assert @file1.deleted?, "File #{@file1.name} is not expected to exist" end def test_file_delete_by_user @@ -175,7 +175,7 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest delete "/dmsf/webdav/#{@project1.identifier}/#{@file1.name}", nil, @jsmith assert_response :success @file1.reload - assert @file1.deleted, "File #{@file1.name} is not expected to exist" + assert @file1.deleted?, "File #{@file1.name} is not expected to exist" end def test_locked_folder @@ -186,7 +186,7 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest delete "/dmsf/webdav/#{@project1.identifier}/#{@folder6.title}", nil, @jsmith assert_response 423 # Locked @folder6.reload - assert !@folder6.deleted, "Folder #{@folder6.title} is expected to exist" + assert !@folder6.deleted?, "Folder #{@folder6.title} is expected to exist" end def test_locked_file @@ -197,7 +197,7 @@ class DmsfWebdavDeleteTest < RedmineDmsf::Test::IntegrationTest delete "/dmsf/webdav/#{@project1.identifier}/#{@file1.name}", nil, @jsmith assert_response 423 # Locked @file1.reload - assert !@file1.deleted, "File #{@file1.name} is expected to exist" + assert !@file1.deleted?, "File #{@file1.name} is expected to exist" end end \ No newline at end of file diff --git a/test/integration/dmsf_webdav_get_test.rb b/test/integration/dmsf_webdav_get_test.rb index a3ad1353..20295236 100644 --- a/test/integration/dmsf_webdav_get_test.rb +++ b/test/integration/dmsf_webdav_get_test.rb @@ -23,7 +23,7 @@ require File.expand_path('../../test_helper', __FILE__) class DmsfWebdavGetTest < RedmineDmsf::Test::IntegrationTest - fixtures :projects, :users, :email_addresses, :members, :member_roles, :roles, + fixtures :projects, :users, :email_addresses, :members, :member_roles, :roles, :enabled_modules, :dmsf_folders, :dmsf_files, :dmsf_file_revisions def setup @@ -33,12 +33,12 @@ class DmsfWebdavGetTest < RedmineDmsf::Test::IntegrationTest @project2 = Project.find_by_id 2 @role = Role.find_by_id 1 # Manager Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' - Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' + Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' DmsfFile.storage_path = File.expand_path '../../fixtures/files', __FILE__ User.current = nil end - - def test_truth + + def test_truth assert_kind_of Project, @project1 assert_kind_of Project, @project2 assert_kind_of Role, @role @@ -67,26 +67,26 @@ class DmsfWebdavGetTest < RedmineDmsf::Test::IntegrationTest def test_should_not_list_non_dmsf_enabled_project get '/dmsf/webdav', nil, @jsmith - assert_response :success + assert_response :success assert response.body.match(@project2.name).nil?, "Unexpected find of project #{@project2.name} in return data" end def test_should_return_status_404_when_project_does_not_exist @project1.enable_module! :dmsf # Flag module enabled - get '/dmsf/webdav/project_does_not_exist', nil, @jsmith + get '/dmsf/webdav/project_does_not_exist', nil, @jsmith assert_response :missing end def test_should_return_status_404_when_dmsf_not_enabled get "/dmsf/webdav/#{@project2.identifier}", nil, @jsmith assert_response :missing - end + end def test_download_file_from_dmsf_enabled_project - #@project1.enable_module! :dmsf # Flag module enabled - get "/dmsf/webdav/#{@project1.identifier}/test.txt", nil, @admin + #@project1.enable_module! :dmsf # Flag module enabled + get "/dmsf/webdav/#{@project1.identifier}/test.txt", nil, @admin assert_response :success - assert_equal response.body, '1234', + assert_equal response.body, '1234', "File downloaded with unexpected contents: '#{response.body}'" end @@ -95,19 +95,19 @@ class DmsfWebdavGetTest < RedmineDmsf::Test::IntegrationTest assert_response :success folder = DmsfFolder.find_by_id 1 assert folder - assert response.body.match(folder.title), + assert response.body.match(folder.title), "Expected to find #{folder.title} in return data" file = DmsfFile.find_by_id 1 assert file - assert response.body.match(file.name), + assert response.body.match(file.name), "Expected to find #{file.name} in return data" end - def test_user_assigned_to_project_dmsf_module_not_enabled + def test_user_assigned_to_project_dmsf_module_not_enabled get "/dmsf/webdav/#{@project1.identifier}", nil, @jsmith assert_response :missing end - + def test_user_assigned_to_project_folder_forbidden @project2.enable_module! :dmsf # Flag module enabled get "/dmsf/webdav/#{@project1.identifier}", nil, @jsmith @@ -121,21 +121,21 @@ class DmsfWebdavGetTest < RedmineDmsf::Test::IntegrationTest get "/dmsf/webdav/#{@project1.identifier}", nil, @jsmith assert_response :success end - + def test_user_assigned_to_project_file_forbidden @project1.enable_module! :dmsf # Flag module enabled - @role.add_permission! :view_dmsf_folders + @role.add_permission! :view_dmsf_folders get "/dmsf/webdav/#{@project1.identifier}/test.txt", nil, @jsmith assert_response :forbidden end - + def test_user_assigned_to_project_file_ok @project1.enable_module! :dmsf # Flag module enabled @role.add_permission! :view_dmsf_folders - @role.add_permission! :view_dmsf_files + @role.add_permission! :view_dmsf_files get "/dmsf/webdav/#{@project1.identifier}/test.txt", nil, @jsmith assert_response :success - assert_equal response.body, '1234', + assert_equal response.body, '1234', "File downloaded with unexpected contents: '#{response.body}'" end diff --git a/test/integration/dmsf_webdav_put_test.rb b/test/integration/dmsf_webdav_put_test.rb index 2ddc10df..f0f603bf 100644 --- a/test/integration/dmsf_webdav_put_test.rb +++ b/test/integration/dmsf_webdav_put_test.rb @@ -24,7 +24,7 @@ require 'fileutils' class DmsfWebdavPutTest < RedmineDmsf::Test::IntegrationTest - fixtures :projects, :users, :email_addresses, :members, :member_roles, :roles, + fixtures :projects, :users, :email_addresses, :members, :member_roles, :roles, :enabled_modules, :dmsf_folders, :dmsf_files, :dmsf_file_revisions def setup @@ -36,13 +36,13 @@ class DmsfWebdavPutTest < RedmineDmsf::Test::IntegrationTest @jsmith = credentials 'jsmith' @project1 = Project.find_by_id 1 @project2 = Project.find_by_id 2 - @role = Role.find 1 # + @role = Role.find 1 # Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' super end - def teardown + def teardown # Delete our tmp folder begin FileUtils.rm_rf DmsfFile.storage_path @@ -50,8 +50,8 @@ class DmsfWebdavPutTest < RedmineDmsf::Test::IntegrationTest error e.message end end - - def test_truth + + def test_truth assert_kind_of Project, @project1 assert_kind_of Project, @project2 assert_kind_of Role, @role @@ -61,7 +61,7 @@ class DmsfWebdavPutTest < RedmineDmsf::Test::IntegrationTest put '/dmsf/webdav' assert_response 401 end - + def test_put_denied_unless_authenticated put "/dmsf/webdav/#{@project1.identifier}" assert_response 401 @@ -113,43 +113,43 @@ class DmsfWebdavPutTest < RedmineDmsf::Test::IntegrationTest put "/dmsf/webdav/#{@project1.identifier}/test-1234.txt", '1234', @jsmith.merge!({:content_type => :text}) assert_response 409 # We don't hold the permission view_dmsf_folders, and thus project 2 doesn't exist to us. end - + def test_put_failed_when_no_file_manipulation_permission @project1.enable_module! :dmsf # Flag module enabled - @role.add_permission! :view_dmsf_folders + @role.add_permission! :view_dmsf_folders put "/dmsf/webdav/#{@project1.identifier}/test-1234.txt", '1234', @jsmith.merge!({:content_type => :text}) assert_response :forbidden # We don't hold the permission file_manipulation - so we're unable to do anything with files end - + def test_put_failed_when_no_view_dmsf_folders_permission - @project1.enable_module! :dmsf # Flag module enabled + @project1.enable_module! :dmsf # Flag module enabled @role.add_permission! :file_manipulation # Check we don't have write access even if we do have the file_manipulation permission put "/dmsf/webdav/#{@project1.identifier}/test-1234.txt", '1234', @jsmith.merge!({:content_type => :text}) assert_response 409 # We don't hold the permission view_dmsf_folders, and thus project 2 doesn't exist to us. # Lets check for our file file = DmsfFile.find_file_by_name @project1, nil, 'test-1234.txt' - assert_nil file, 'File test-1234 was found in projects dmsf folder.' + assert_nil file, 'File test-1234 was found in projects dmsf folder.' end def test_put_succeeds_for_non_admin_with_correct_permissions - @project1.enable_module! :dmsf # Flag module enabled + @project1.enable_module! :dmsf # Flag module enabled @role.add_permission! :view_dmsf_folders - @role.add_permission! :file_manipulation + @role.add_permission! :file_manipulation put "/dmsf/webdav/#{@project1.identifier}/test-1234.txt", '1234', @jsmith.merge!({:content_type => :text}) assert_response :success # 201 - Now we have permissions # Lets check for our file file = DmsfFile.find_file_by_name @project1, nil, 'test-1234.txt' - assert file, 'File test-1234 was not found in projects dmsf folder.' + assert file, 'File test-1234 was not found in projects dmsf folder.' end def test_put_writes_revision_successfully_for_unlocked_file - @project1.enable_module! :dmsf #Flag module enabled + @project1.enable_module! :dmsf #Flag module enabled @role.add_permission! :view_dmsf_folders @role.add_permission! :file_manipulation file = DmsfFile.find_file_by_name @project1, nil, 'test.txt' assert_not_nil file, 'test.txt file not found' - assert_difference 'file.revisions.count', +1 do + assert_difference 'file.dmsf_file_revisions.count', +1 do put "/dmsf/webdav/#{@project1.identifier}/test.txt", '1234', @jsmith.merge!({:content_type => :text}) assert_response :success # 201 - Created end @@ -163,10 +163,10 @@ class DmsfWebdavPutTest < RedmineDmsf::Test::IntegrationTest assert !User.current.anonymous?, 'Current user is not anonymous' file = DmsfFile.find_file_by_name @project1, nil, 'test.txt' assert file.lock!, "File failed to be locked by #{User.current.name}" - assert_no_difference 'file.revisions.count' do + assert_no_difference 'file.dmsf_file_revisions.count' do put "/dmsf/webdav/#{@project1.identifier}/test.txt", '1234', @jsmith.merge!({:content_type => :text}) assert_response 423 # Locked - end + end end def test_put_fails_revision_when_file_is_locked_and_user_is_administrator @@ -177,10 +177,10 @@ class DmsfWebdavPutTest < RedmineDmsf::Test::IntegrationTest assert !User.current.anonymous?, 'Current user is not anonymous' file = DmsfFile.find_file_by_name @project1, nil, 'test.txt' assert file.lock!, "File failed to be locked by #{User.current.name}" - assert_no_difference 'file.revisions.count' do + assert_no_difference 'file.dmsf_file_revisions.count' do put "/dmsf/webdav/#{@project1.identifier}/test.txt", '1234', @admin.merge!({:content_type => :text}) - assert_response 423 # Locked - end + assert_response 423 # Locked + end end def test_put_accepts_revision_when_file_is_locked_and_user_is_same_as_lock_holder @@ -191,10 +191,10 @@ class DmsfWebdavPutTest < RedmineDmsf::Test::IntegrationTest assert !User.current.anonymous?, 'Current user is not anonymous' file = DmsfFile.find_file_by_name @project1, nil, 'test.txt' assert file.lock!, "File failed to be locked by #{User.current.name}" - assert_difference 'file.revisions.count', +1 do + assert_difference 'file.dmsf_file_revisions.count', +1 do put "/dmsf/webdav/#{@project1.identifier}/test.txt", '1234', @jsmith.merge!({:content_type => :text}) assert_response :success # 201 - Created - end + end end - + end \ No newline at end of file diff --git a/test/unit/dmsf_file_revision_test.rb b/test/unit/dmsf_file_revision_test.rb index 61bdead4..7b14d211 100644 --- a/test/unit/dmsf_file_revision_test.rb +++ b/test/unit/dmsf_file_revision_test.rb @@ -35,10 +35,10 @@ class DmsfFileRevisionTest < RedmineDmsf::Test::UnitTest def test_delete_restore @revision5.delete false - assert @revision5.deleted, + assert @revision5.deleted?, "File revision #{@revision5.name} hasn't been deleted" @revision5.restore - assert !@revision5.deleted, + assert !@revision5.deleted?, "File revision #{@revision5.name} hasn't been restored" end diff --git a/test/unit/dmsf_file_test.rb b/test/unit/dmsf_file_test.rb index 9ffdf3ee..0e1c9299 100644 --- a/test/unit/dmsf_file_test.rb +++ b/test/unit/dmsf_file_test.rb @@ -22,44 +22,44 @@ require File.expand_path('../../test_helper', __FILE__) class DmsfFileTest < RedmineDmsf::Test::UnitTest - fixtures :projects, :users, :email_addresses, :dmsf_folders, :dmsf_files, - :dmsf_file_revisions, :roles, :members, :member_roles, :dmsf_locks, + fixtures :projects, :users, :email_addresses, :dmsf_folders, :dmsf_files, + :dmsf_file_revisions, :roles, :members, :member_roles, :dmsf_locks, :dmsf_links - + def setup @admin = User.find_by_id 1 - @jsmith = User.find_by_id 2 + @jsmith = User.find_by_id 2 @project1 = Project.find_by_id 1 @file1 = DmsfFile.find_by_id 1 @file2 = DmsfFile.find_by_id 2 @file3 = DmsfFile.find_by_id 3 @file4 = DmsfFile.find_by_id 4 - @file5 = DmsfFile.find_by_id 5 + @file5 = DmsfFile.find_by_id 5 User.current = nil end - + def test_truth assert_kind_of User, @admin - assert_kind_of User, @jsmith + assert_kind_of User, @jsmith assert_kind_of Project, @project1 assert_kind_of DmsfFile, @file1 assert_kind_of DmsfFile, @file2 assert_kind_of DmsfFile, @file3 assert_kind_of DmsfFile, @file4 assert_kind_of DmsfFile, @file5 - end + end def test_project_file_count_differs_from_project_visibility_count assert_not_same(@project1.dmsf_files.count, @project1.dmsf_files.visible.count) end - def test_project_dmsf_file_listing_contains_deleted_items - assert @project1.dmsf_files.index{ |f| f.deleted }, - 'Expected at least one deleted item in ' + def test_project_dmsf_file_listing_contains_deleted_items + assert @project1.dmsf_files.index{ |f| f.deleted? }, + 'Expected at least one deleted item in ' end - def test_project_dmsf_file_visible_listing_contains_no_deleted_items - assert @project1.dmsf_files.visible.index{ |f| f.deleted }.nil?, + def test_project_dmsf_file_visible_listing_contains_no_deleted_items + assert @project1.dmsf_files.visible.index{ |f| f.deleted? }.nil?, 'There is a deleted file, this was unexpected' end @@ -71,67 +71,68 @@ class DmsfFileTest < RedmineDmsf::Test::UnitTest assert @file4.locked?, "#{@file4.name} is not locked" end - def test_file_with_folder_up_heirarchy_locked_is_reported_as_locked + def test_file_with_folder_up_heirarchy_locked_is_reported_as_locked assert @file5.locked?, "#{@file5.name} is not locked" end def test_file_locked_is_not_locked_for_user_who_locked User.current = @admin @file1.lock! - assert !@file1.locked_for_user?, + assert !@file1.locked_for_user?, "#{@file1.name} is locked for #{User.current.name}" - @file1.unlock! + @file1.unlock! end - + def test_file_locked_is_locked_for_user_who_didnt_lock User.current = @admin @file1.lock! User.current = @jsmith - assert @file1.locked_for_user?, + assert @file1.locked_for_user?, "#{@file1.name} is locked for #{User.current.name}" User.current = @admin - @file1.unlock! + @file1.unlock! end - + def test_file_with_no_locks_reported_unlocked assert !@file1.locked? end - - def test_delete_restore - assert_equal 1, @file4.revisions.visible.count + + def test_delete_restore + assert_equal 1, @file4.dmsf_file_revisions.visible.count assert_equal 2, @file4.referenced_links.visible.count end - + def test_delete User.current = @admin - @file4.folder.unlock! + @file4.dmsf_folder.unlock! assert @file4.delete(false), @file4.errors.full_messages.to_sentence - assert @file4.deleted, "File #{@file4.name} is not deleted" - assert_equal 0, @file4.revisions.visible.count - assert_equal 0, @file4.referenced_links.visible.count - @file4.folder.lock! + assert @file4.deleted?, "File #{@file4.name} is not deleted" + assert_equal 0, @file4.dmsf_file_revisions.visible.count + # Links should not be deleted + assert_equal 2, @file4.referenced_links.visible.count + @file4.dmsf_folder.lock! end - + def test_restore User.current = @admin - @file4.folder.unlock! + @file4.dmsf_folder.unlock! assert @file4.delete(false), @file4.errors.full_messages.to_sentence - @file4.restore - assert !@file4.deleted, "File #{@file4} hasn't been restored" - assert_equal 1, @file4.revisions.visible.count + @file4.restore + assert !@file4.deleted?, "File #{@file4} hasn't been restored" + assert_equal 1, @file4.dmsf_file_revisions.visible.count assert_equal 2, @file4.referenced_links.visible.count - @file4.folder.lock! + @file4.dmsf_folder.lock! end - - def test_destroy + + def test_destroy User.current = @admin - @file4.folder.unlock! - assert_equal 1, @file4.revisions.visible.count + @file4.dmsf_folder.unlock! + assert_equal 1, @file4.dmsf_file_revisions.visible.count assert_equal 2, @file4.referenced_links.visible.count - @file4.delete true - assert_equal 0, @file4.revisions.count + @file4.delete true + assert_equal 0, @file4.dmsf_file_revisions.count assert_equal 0, @file4.referenced_links.count - @file4.folder.lock! + @file4.dmsf_folder.lock! end end \ No newline at end of file diff --git a/test/unit/dmsf_folder_test.rb b/test/unit/dmsf_folder_test.rb index 8116a6a8..17217f79 100644 --- a/test/unit/dmsf_folder_test.rb +++ b/test/unit/dmsf_folder_test.rb @@ -35,14 +35,14 @@ class DmsfFolderTest < RedmineDmsf::Test::UnitTest def test_delete assert @folder6.delete(false), @folder6.errors.full_messages.to_sentence - assert @folder6.deleted, "Folder #{@folder6} hasn't been deleted" + assert @folder6.deleted?, "Folder #{@folder6} hasn't been deleted" end def test_restore assert @folder6.delete(false), @folder6.errors.full_messages.to_sentence - assert @folder6.deleted, "Folder #{@folder6} hasn't been deleted" + assert @folder6.deleted?, "Folder #{@folder6} hasn't been deleted" assert @folder6.restore, @folder6.errors.full_messages.to_sentence - assert !@folder6.deleted, "Folder #{@folder6} hasn't been restored" + assert !@folder6.deleted?, "Folder #{@folder6} hasn't been restored" end def test_destroy diff --git a/test/unit/dmsf_link_test.rb b/test/unit/dmsf_link_test.rb index 66b41a48..a21ad8a9 100644 --- a/test/unit/dmsf_link_test.rb +++ b/test/unit/dmsf_link_test.rb @@ -2,7 +2,7 @@ # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-15 Karel Pičman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -94,11 +94,28 @@ class DmsfLinksTest < RedmineDmsf::Test::UnitTest "Folder link #{@folder_link.name} should have not been saved" assert_equal 1, @folder_link.errors.count end + + def test_validate_external_url_length + @file_link.target_type = 'DmsfUrl' + @file_link.external_url = 'https://localhost/' + 'a' * 256 + assert !@file_link.save, + "External URL link #{@file_link.name} should have not been saved" + assert_equal 1, @file_link.errors.count + end + + def test_validate_external_url_presence + @file_link.target_type = 'DmsfUrl' + @file_link.external_url = '' + assert !@file_link.save, + "External URL link #{@file_link.name} should have not been saved" + assert_equal 1, @file_link.errors.count + end def test_validate_external_url @file_link.target_type = 'DmsfUrl' - @file_link.external_url = nil - assert !@file_link.save, 'External link should have not been saved' + @file_link.external_url = 'htt ps://abc.xyz' + assert !@file_link.save, + "External URL link #{@file_link.name} should have not been saved" assert_equal 1, @file_link.errors.count end @@ -185,28 +202,28 @@ class DmsfLinksTest < RedmineDmsf::Test::UnitTest def test_delete_file_link assert @file_link.delete(false), @file_link.errors.full_messages.to_sentence - assert @file_link.deleted, "File link hasn't been deleted" + assert @file_link.deleted?, "File link hasn't been deleted" end def test_restore_file_link assert @file_link.delete(false), @file_link.errors.full_messages.to_sentence - assert @file_link.deleted, "File link hasn't been deleted" + assert @file_link.deleted?, "File link hasn't been deleted" assert @file_link.restore, @file_link.errors.full_messages.to_sentence - assert !@file_link.deleted, "File link hasn't been restored" + assert !@file_link.deleted?, "File link hasn't been restored" end def test_delete_folder_link assert @folder_link.delete(false), @folder_link.errors.full_messages.to_sentence - assert @folder_link.deleted, "Folder link hasn't been deleted" + assert @folder_link.deleted?, "Folder link hasn't been deleted" end def test_restore_folder_link assert @folder_link.delete(false), @folder_link.errors.full_messages.to_sentence - assert @folder_link.deleted, "Folder link hasn't been deleted" + assert @folder_link.deleted?, "Folder link hasn't been deleted" assert @folder_link.restore, @folder_link.errors.full_messages.to_sentence - assert !@folder_link.deleted, "Folder link hasn't been restored" + assert !@folder_link.deleted?, "Folder link hasn't been restored" end def test_destroy_file_link diff --git a/test/unit/dmsf_lock_test.rb b/test/unit/dmsf_lock_test.rb index 211f97c6..e8fb63e2 100644 --- a/test/unit/dmsf_lock_test.rb +++ b/test/unit/dmsf_lock_test.rb @@ -21,8 +21,7 @@ require File.expand_path('../../test_helper.rb', __FILE__) -class DmsfLockTest < RedmineDmsf::Test::UnitTest - #attr_reader :lock +class DmsfLockTest < RedmineDmsf::Test::UnitTest fixtures :projects, :users, :email_addresses, :dmsf_folders, :dmsf_files, :dmsf_file_revisions, :roles, :members, :member_roles, :enabled_modules, :enumerations, :dmsf_locks @@ -77,6 +76,7 @@ class DmsfLockTest < RedmineDmsf::Test::UnitTest end def test_locked_folder_cannot_be_unlocked_by_someone_without_rights_or_anon + User.current = nil assert_no_difference ('@folder2.lock.count') do assert_raise DmsfLockError do @folder2.unlock! diff --git a/test/unit/dmsf_workflow_test.rb b/test/unit/dmsf_workflow_test.rb index a56b7e71..3364cdb7 100644 --- a/test/unit/dmsf_workflow_test.rb +++ b/test/unit/dmsf_workflow_test.rb @@ -2,7 +2,7 @@ # # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011-16 Karel Picman +# Copyright (C) 2011-16 Karel Pičman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -27,15 +27,16 @@ class DmsfWorkflowTest < RedmineDmsf::Test::UnitTest :dmsf_workflow_step_assignments, :dmsf_workflow_step_actions def setup - @wf1 = DmsfWorkflow.find_by_id(1) - @wf2 = DmsfWorkflow.find_by_id(2) - @wfs1 = DmsfWorkflowStep.find_by_id(1) - @wfs2 = DmsfWorkflowStep.find_by_id(2) - @wfs3 = DmsfWorkflowStep.find_by_id(3) - @wfs4 = DmsfWorkflowStep.find_by_id(4) - @wfs5 = DmsfWorkflowStep.find_by_id(5) - @wfsa1 = DmsfWorkflowStepAssignment.find_by_id(1) - @wfsac1 = DmsfWorkflowStepAction.find_by_id(1) + @wf1 = DmsfWorkflow.find_by_id 1 + @wf2 = DmsfWorkflow.find_by_id 2 + @wf3 = DmsfWorkflow.find_by_id 3 + @wfs1 = DmsfWorkflowStep.find_by_id 1 + @wfs2 = DmsfWorkflowStep.find_by_id 2 + @wfs3 = DmsfWorkflowStep.find_by_id 3 + @wfs4 = DmsfWorkflowStep.find_by_id 4 + @wfs5 = DmsfWorkflowStep.find_by_id 5 + @wfsa1 = DmsfWorkflowStepAssignment.find_by_id 1 + @wfsac1 = DmsfWorkflowStepAction.find_by_id 1 @revision1 = DmsfFileRevision.find_by_id 1 @revision2 = DmsfFileRevision.find_by_id 2 @project = Project.find_by_id 2 @@ -45,6 +46,7 @@ class DmsfWorkflowTest < RedmineDmsf::Test::UnitTest def test_truth assert_kind_of DmsfWorkflow, @wf1 assert_kind_of DmsfWorkflow, @wf2 + assert_kind_of DmsfWorkflow, @wf3 assert_kind_of DmsfWorkflowStep, @wfs1 assert_kind_of DmsfWorkflowStep, @wfs2 assert_kind_of DmsfWorkflowStep, @wfs3 @@ -85,12 +87,19 @@ class DmsfWorkflowTest < RedmineDmsf::Test::UnitTest assert_equal 1, @wf1.errors.count end - def test_validate_name_uniqueness + def test_validate_name_uniqueness_globaly @wf2.name = @wf1.name assert !@wf2.save assert_equal 1, @wf2.errors.count end + def test_validate_name_uniqueness_localy + @wf2.name = @wf1.name + @wf2.project_id = @wf1.project_id + assert !@wf2.save + assert_equal 1, @wf2.errors.count + end + def test_destroy @wf1.destroy assert_nil DmsfWorkflow.find_by_id(1) @@ -210,4 +219,20 @@ class DmsfWorkflowTest < RedmineDmsf::Test::UnitTest assert_equal participiants.count, 2 end + def test_locked + assert @wf3.locked?, "#{@wf2.name} status is #{@wf3.status}" + end + + def test_active + assert @wf1.active?, "#{@wf1.name} status is #{@wf1.status}" + end + + def test_scope_active + assert_equal DmsfWorkflow.count, (DmsfWorkflow.active.count + 1) + end + + def test_scope_status + assert_equal 1, DmsfWorkflow.status(DmsfWorkflow::STATUS_LOCKED).count + end + end
          <%=l(:field_project)%> <%=l(:field_label_dmsf_workflow)%> <%=l(:label_document)%>
          - <% if assignment.dmsf_file_revision.file.project %> - <%= link_to_project(assignment.dmsf_file_revision.file.project) %> + <% if assignment.dmsf_file_revision.dmsf_file.project %> + <%= link_to_project(assignment.dmsf_file_revision.dmsf_file.project) %> <% end %> - <% if assignment.dmsf_workflow_step && assignment.dmsf_workflow_step.dmsf_workflow %> - <%= link_to(h(assignment.dmsf_workflow_step.dmsf_workflow.name), - edit_dmsf_workflow_path(assignment.dmsf_workflow_step.dmsf_workflow)) %> + <% if assignment.dmsf_workflow_step && assignment.dmsf_workflow_step.dmsf_workflow %> + <%= link_to(h(assignment.dmsf_workflow_step.dmsf_workflow.name), + edit_dmsf_workflow_path(assignment.dmsf_workflow_step.dmsf_workflow)) %> <% end %> - <% if assignment.dmsf_file_revision && assignment.dmsf_file_revision.file %> - <%= link_to(h(assignment.dmsf_file_revision.title), - {:controller => 'dmsf_files', :action => :show, :id => assignment.dmsf_file_revision.file }) %> + <% if assignment.dmsf_file_revision && assignment.dmsf_file_revision.dmsf_file %> + <%= link_to(h(assignment.dmsf_file_revision.title), + {:controller => 'dmsf_files', :action => :show, :id => assignment.dmsf_file_revision.dmsf_file }) %> <% end %> <% if assignment.dmsf_file_revision %> - <% if assignment.dmsf_file_revision.file.folder %> - <%= link_to(h(assignment.dmsf_file_revision.file.folder.title), - {:controller => 'dmsf', :action => 'show', :id => assignment.dmsf_file_revision.file.project, :folder_id => assignment.dmsf_file_revision.file.folder}) %> - <% elsif assignment.dmsf_file_revision.file.project %> - <%= link_to(l(:link_documents), {:controller => 'dmsf', :action => 'show', :id => assignment.dmsf_file_revision.file.project }) %> + <% if assignment.dmsf_file_revision.dmsf_file.dmsf_folder %> + <%= link_to(h(assignment.dmsf_file_revision.dmsf_file.dmsf_folder.title), + {:controller => 'dmsf', :action => 'show', :id => assignment.dmsf_file_revision.dmsf_file.project, :folder_id => assignment.dmsf_file_revision.dmsf_file.dmsf_folder}) %> + <% elsif assignment.dmsf_file_revision.dmsf_file.project %> + <%= link_to(l(:link_documents), {:controller => 'dmsf', :action => 'show', :id => assignment.dmsf_file_revision.dmsf_file.project }) %> <% end %> <% end %>