diff --git a/CHANGELOG.md b/CHANGELOG.md index ad55cb8b..56b92501 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,34 @@ Changelog for Redmine DMSF ========================== +1.4.8: ** +----------------------- + + Symbolic links + Document tagging + Localization of email notifications + An option to send document links by email + + * New: Issue #19 - Documentation? + * Update: Issue #106 - [Feature Request] Save files in folder structure defined via DMSF + * Fix: Issue #107 - Problems upgrading redmine 1.3 to 2.23 regarding DMFS + * Fix: Issue #111 - Cannot sort files in folders by date, size, etc + * Update: Issue #139 - Error 500 on click on "details" icon + * New: Issue #183 - Create document links + * New: Issue #201 - Download link by email + * Fix: Issue #205 - Ampersand shows up in displayed filenames as "&" instead of "&" + * Fix: Issue #212 - Incorrect revision information in email notification + * Fix: Issue #214 - Required DMSF custom field prevents documents to be saved + * Update: Issue #216 - Enhancement : having notification emails translated + * Update: Issue #224 - Setup/Upgrade documentation + * Fix: Issue #226 - undefined method `custom_fields_tabs' for module `CustomFieldsHelper' + * Fix: Issue #233 - Failed Travis builds + * Update: Issue #235 - "You are not member of the project" when changing project notification. + * New: Issue #236 - Documents tagging + * Fix: Issue #240 - Internal server error, redmine 2.5.1-devel.13064, PostgreSQL, dmsf 1.4.8-devel + * Fix: Issue #242 - dsmf 1.4.8 minor ... "link form" tab + * Fix: Issue #246 - "File storage directory" does not default properly when setting is empty + 1.4.7: *2014-01-02* ----------------------- diff --git a/Gemfile b/Gemfile index 781bcbf1..838ae835 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,7 @@ gem 'rubyzip', '>= 1.0.0' gem 'zip-zip' # Just to avoid 'cannot load such file -- zip/zip' error gem 'simple_enum' gem 'uuidtools', '~> 2.1.1' +gem 'dav4rack', '=0.2.11' group :production do gem 'nokogiri', '>= 1.5.10' diff --git a/README.md b/README.md index 58c62b41..a0e0e8e4 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ Redmine DMSF Plugin =================== -The current version of Redmine DMSF is **1.4.7** +The current version of Redmine DMSF is **1.4.8** [![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. Redmine DMSF now comes bundled with Webdav functionality: if switched on within plugin settings this will be accessible from /dmsf/webdav. -Webdav functionality is provided through DAV4Rack, however is provided bundled due to modifications made, DAV4Rack is released under the terms of the -MIT license, more information can be found at +Webdav functionality is provided through DAV4Rack gem. Initial development was for Kontron AG R&D department and it is released as open source thanks to their generosity. Project home: @@ -28,26 +27,21 @@ Features * Document locking * Multi (drag/drop depending on browser) upload/download * Multi download via zip - * Direct document sending via email + * Direct document or document link sending via email * Configurable document approval workflow * Document access auditing * Integration with Redmine's activity feed * Wiki macros for quick content linking * Full read/write webdav functionality * Optional document content fulltext search - * Compatible with redmine 2.0.x + * Documents and folders symbolic links + * Document tagging + * Compatible with redmine 2.5.x Dependencies ------------ - -As of version 1.4.4 of this plugin: - - * Bundler 1.1 or greater (Gem) - * Redmine 2.0.x - * Rails 3.2.x (Inline with Redmine installation requirement) - * rubyzip 1.0.0 (Gem) - * UUIDTools 2.1.1 or greater (less than 2.2.0) (Gem) - * simple_enum (Gem) + + * Redmine 2.3.x or higher ### Fulltext search (optional) @@ -117,7 +111,9 @@ The DMSF file/revision id can be found in link for file/revision download from w The DMSF folder id can be found in the link when opening folders within Redmine. -You can also publish Wiki help description: In the file /public/help/wiki_syntax_detailed.html, after the document link description/definition: +You can also publish Wiki help description: + +In the file /public/help//wiki_syntax_detailed.html, after the document link description/definition:
  • @@ -134,6 +130,11 @@ You can also publish Wiki help description: In the file /public/he
+In the file /public/help//wiki_syntax.html, at the end of the Redmine links section: + + {{dmsf(83)}}Document #83 + + Setup / Upgrade --------------- @@ -142,16 +143,21 @@ 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. Initialize/Update database: `rake redmine:plugins:migrate RAILS_ENV="production"` -4. The access rights must be set for web server, example: `chown -R www-data:www-data /opt/redmine/plugins/redmine_dmsf` -*Ensure that the path used in the above is adjusted for your installation* +4. The access rights must be set for web server, example: `chown -R www-data:www-data plugins/redmine_dmsf` 5. Restart web server 6. You should configure plugin via Redmine interface: Administration -> Plugins -> DMSF -> Configure 7. Assign DMSF permissions to appropriate roles 8. There are two rake tasks: a) To convert documents from the standard Redmine document module - rake redmine:dmsf_convert_documents project=test RAILS_ENV="production" + Available options: + * project => id or identifier of project (defaults to all projects) + * dry => true or false (default false) to perform just check without any conversion + * invalid=replace => to perform document title invalid characters replacement for '-' + Example: + rake redmine:dmsf_convert_documents project=test RAILS_ENV="production" b) To alert all users who are expected to do an approval in the current approval steps - rake redmine:dmsf_alert_approvals RAILS_ENV="production" + Example: + rake redmine:dmsf_alert_approvals RAILS_ENV="production" ### Fulltext search (optional) If you want to use fulltext search features, you must setup file content indexing. @@ -188,4 +194,4 @@ Changes with tests, and full documentation are preferred. Additional Documentation ------------------------ -[CHANGELOG.md](CHANGELOG.md) - Project changelog +[CHANGELOG.md](CHANGELOG.md) - Project changelog \ No newline at end of file diff --git a/app/controllers/dmsf_controller.rb b/app/controllers/dmsf_controller.rb index 29b272fa..a3f361fd 100644 --- a/app/controllers/dmsf_controller.rb +++ b/app/controllers/dmsf_controller.rb @@ -1,8 +1,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-14 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,7 @@ class DmsfController < ApplicationController class FileNotFound < StandardError; end before_filter :find_project - before_filter :authorize, :except => [:delete_entries] + before_filter :authorize before_filter :find_folder, :except => [:new, :create, :edit_root, :save_root] before_filter :find_parent, :only => [:new, :create] @@ -37,36 +37,116 @@ class DmsfController < ApplicationController @file_manipulation_allowed = User.current.allowed_to?(:file_manipulation, @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 + unless @folder - @subfolders = @project.dmsf_folders.visible - @files = @project.dmsf_files.visible + 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).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).visible.each do |l| + r = l.target_file.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]) + @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 + end + @locked_for_user = false else @subfolders = @folder.subfolders.visible - @files = @folder.files.visible + @files = @folder.files.visible + @dir_links = @folder.folder_links.visible + @file_links = @folder.file_links.visible @locked_for_user = @folder.locked_for_user? end - @files.sort! do |a,b| - if a.last_revision && b.last_revision - a.last_revision.title <=> b.last_revision.title - else - 0 - 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 end + + def download_email_entries + send_file( + params[:path], + :filename => 'Documents.zip', + :type => 'application/zip', + :disposition => 'attachment') + rescue Exception => e + flash[:error] = e.message + end - def entries_operation - selected_folders = params[:subfolders] - selected_files = params[:files] + def entries_operation + # Download/Email + selected_folders = params[:subfolders].present? ? params[:subfolders] : [] + selected_files = params[:files].present? ? params[:files] : [] + selected_dir_links = params[:dir_links] + selected_file_links = params[:file_links] - if selected_folders.nil? && selected_files.nil? - flash[:warning] = l(:warning_no_entries_selected) - redirect_to :action => 'show', :id => @project, :folder_id => @folder + if selected_folders.blank? && selected_files.blank? && + selected_dir_links.blank? && selected_file_links.blank? + flash[:warning] = l(:warning_no_entries_selected) + redirect_to :back 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 + 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 + end + end + if params[:email_entries].present? email_entries(selected_folders, selected_files) else @@ -74,74 +154,117 @@ class DmsfController < ApplicationController end rescue ZipMaxFilesError flash[:error] = l(:error_max_files_exceeded, :number => Setting.plugin_redmine_dmsf['dmsf_max_file_download']) - redirect_to({:controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @folder}) + redirect_to :back rescue EmailMaxFileSize flash[:error] = l(:error_max_email_filesize_exceeded, :number => Setting.plugin_redmine_dmsf['dmsf_max_email_filesize']) - redirect_to({:controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @folder}) + redirect_to :back rescue FileNotFound render_404 rescue DmsfAccessError render_403 end + + def tag_changed + # Tag filter + if params[:dmsf_folder] && params[:dmsf_folder][:custom_field_values].present? + redirect_to dmsf_folder_path( + :id => @project, + :custom_field_id => params[:dmsf_folder][:custom_field_values].first[0], + :custom_value => params[:dmsf_folder][:custom_field_values].first[1]) + else + redirect_to :back + end + end def entries_email @email_params = params[:email] - if @email_params['to'].strip.blank? + if @email_params[:to].strip.blank? flash.now[:error] = l(:error_email_to_must_be_entered) render :action => 'email_entries' return - end - DmsfMailer.send_documents(User.current, @email_params['to'], @email_params['cc'], - @email_params['subject'], @email_params['zipped_content'], @email_params['body']).deliver + end + DmsfMailer.send_documents(@project, User.current, @email_params).deliver File.delete(@email_params['zipped_content']) flash[:notice] = l(:notice_email_sent, @email_params['to']) - redirect_to({:controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @folder}) + + redirect_to dmsf_folder_path(:id => @project, :folder_id => @folder) end def delete_entries selected_folders = params[:subfolders] selected_files = params[:files] - if selected_folders.nil? && selected_files.nil? + selected_dir_links = params[:dir_links] + selected_file_links = params[:file_links] + if selected_folders.blank? && selected_files.blank? && + selected_dir_links.blank? && selected_file_links.blank? flash[:warning] = l(:warning_no_entries_selected) else - failed_entries = [] - deleted_files = [] - deleted_folders = [] - if selected_folders - if User.current.allowed_to?(:folder_manipulation, @project) + failed_entries = [] + + if User.current.allowed_to?(:folder_manipulation, @project) + # Folders + if selected_folders.present? selected_folders.each do |subfolderid| - subfolder = DmsfFolder.visible.find(subfolderid) - next if subfolder.nil? + subfolder = DmsfFolder.visible.find_by_id subfolderid + next unless subfolder if subfolder.project != @project || !subfolder.delete - failed_entries.push(subfolder) - else - deleted_folders.push(subfolder) + failed_entries.push(subfolder) end - end - else - flash[:error] = l(:error_user_has_not_right_delete_folder) + end end + # Folder links + if selected_dir_links.present? + selected_dir_links.each do |dir_link_id| + link_folder = DmsfLink.visible.find_by_id dir_link_id + next unless link_folder + if link_folder.project != @project || !link_folder.delete + failed_entries.push(link_folder) + end + end + end + else + flash[:error] = l(:error_user_has_not_right_delete_folder) end - if selected_files - if User.current.allowed_to?(:file_manipulation, @project) + + deleted_files = [] + if User.current.allowed_to?(:file_manipulation, @project) + # Files + if selected_files.present? selected_files.each do |fileid| - file = DmsfFile.visible.find(fileid) + file = DmsfFile.visible.find_by_id fileid next unless file if file.project != @project || !file.delete failed_entries.push(file) else deleted_files.push(file) end - end - else - flash[:error] = l(:error_user_has_not_right_delete_file) + end end + # File links + if selected_file_links.present? + selected_file_links.each do |file_link_id| + file_link = DmsfLink.visible.find_by_id file_link_id + next unless file_link + if file_link.project != @project || !file_link.delete + failed_entries.push(file_link) + end + end + end + else + flash[:error] = l(:error_user_has_not_right_delete_file) end + unless deleted_files.empty? deleted_files.each do |f| log_activity(f, 'deleted') end - DmsfMailer.files_deleted(User.current, deleted_files).deliver + begin + DmsfMailer.get_notify_users(User.current, deleted_files).each do |u| + DmsfMailer.files_deleted(u, @project, deleted_files).deliver + end + rescue Exception => e + Rails.logger.error "Could not send email notifications: #{e.message}" + end end if failed_entries.empty? flash[:notice] = l(:notice_entries_deleted) @@ -149,8 +272,8 @@ class DmsfController < ApplicationController flash[:warning] = l(:warning_some_entries_were_not_deleted, :entries => failed_entries.map{|e| e.title}.join(', ')) end end - - redirect_to :controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @folder + + redirect_to :back end # Folder manipulation @@ -194,16 +317,16 @@ class DmsfController < ApplicationController end end - def delete - check_project(@delete_folder = DmsfFolder.visible.find(params[:delete_folder_id])) + def delete + @delete_folder = DmsfFolder.visible.find(params[:delete_folder_id]) if @delete_folder if @delete_folder.delete flash[:notice] = l(:notice_folder_deleted) else flash[:error] = @delete_folder.errors[:base][0] end - end - redirect_to :controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @folder + end + redirect_to dmsf_folder_path(:id => @project, :folder_id => @delete_folder.dmsf_folder_id) rescue DmsfAccessError render_403 end @@ -229,14 +352,8 @@ class DmsfController < ApplicationController @project.save end flash[:notice] = l(:notice_folder_notifications_activated) - end - if params[:current] - redirect_to params[:current] - elsif @folder - redirect_to({:controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @folder.folder}) - else - redirect_to({:controller => 'dmsf', :action => 'show', :id => @project}) end + redirect_to :back end def notify_deactivate @@ -250,17 +367,10 @@ class DmsfController < ApplicationController @project.save end flash[:notice] = l(:notice_folder_notifications_deactivated) - end - if params[:current] - redirect_to params[:current] - elsif @folder - redirect_to({:controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @folder.folder}) - else - redirect_to({:controller => 'dmsf', :action => 'show', :id => @project}) end + redirect_to :back end - def lock if @folder.nil? flash[:warning] = l(:warning_foler_unlockable) @@ -269,9 +379,8 @@ class DmsfController < ApplicationController else @folder.lock! flash[:notice] = l(:notice_folder_locked) - end - redirect_to params[:current] ? params[:current] : - {:controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @folder.folder} + end + redirect_to :back end def unlock @@ -286,9 +395,8 @@ class DmsfController < ApplicationController else flash[:error] = l(:error_only_user_that_locked_folder_can_unlock_it) end - end - redirect_to params[:current] ? params[:current] : - {:controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @folder.folder} + end + redirect_to :back end private @@ -302,9 +410,9 @@ class DmsfController < ApplicationController zip = DmsfZip.new zip_entries(zip, selected_folders, selected_files) - ziped_content = "#{DmsfHelper.temp_dir}/#{DmsfHelper.temp_filename('dmsf_email_sent_documents.zip')}"; + zipped_content = "#{DmsfHelper.temp_dir}/#{DmsfHelper.temp_filename('dmsf_email_sent_documents.zip')}"; - File.open(ziped_content, 'wb') do |f| + File.open(zipped_content, 'wb') do |f| zip_file = File.open(zip.finish, 'rb') while (buffer = zip_file.read(8192)) f.write(buffer) @@ -312,7 +420,7 @@ class DmsfController < ApplicationController end max_filesize = Setting.plugin_redmine_dmsf['dmsf_max_email_filesize'].to_f - if max_filesize > 0 && File.size(ziped_content) > max_filesize * 1048576 + if max_filesize > 0 && File.size(zipped_content) > max_filesize * 1048576 raise EmailMaxFileSize end @@ -323,7 +431,11 @@ class DmsfController < ApplicationController audit.save! end - @email_params = {'zipped_content' => ziped_content} + @email_params = { + :zipped_content => zipped_content, + :folders => selected_folders, + :files => selected_files + } render :action => 'email_entries' rescue Exception => e flash[:error] = e.message @@ -357,14 +469,14 @@ class DmsfController < ApplicationController def zip_entries(zip, selected_folders, selected_files) if selected_folders && selected_folders.is_a?(Array) - selected_folders.each do |selected_folder_id| - check_project(folder = DmsfFolder.visible.find(selected_folder_id)) + selected_folders.each do |selected_folder_id| + folder = DmsfFolder.visible.find(selected_folder_id) zip.add_folder(folder, (@folder.dmsf_path_str if @folder)) if folder end end if selected_files && selected_files.is_a?(Array) - selected_files.each do |selected_file_id| - check_project(file = DmsfFile.visible.find(selected_file_id)) + selected_files.each do |selected_file_id| + file = DmsfFile.visible.find(selected_file_id) if file && file.last_revision && File.exists?(file.last_revision.disk_file) zip.add_file(file, (@folder.dmsf_path_str if @folder)) if file else @@ -379,34 +491,22 @@ class DmsfController < ApplicationController zip end - def find_project - @project = Project.find(params[:id]) - end - def find_folder - @folder = DmsfFolder.visible.find(params[:folder_id]) if params.keys.include?('folder_id') - check_project(@folder) + @folder = DmsfFolder.visible.find(params[:folder_id]) if params.keys.include?('folder_id') rescue DmsfAccessError render_403 end def find_parent - @parent = DmsfFolder.visible.find(params[:parent_id]) if params.keys.include?('parent_id') - check_project(@parent) + @parent = DmsfFolder.visible.find(params[:parent_id]) if params.keys.include?('parent_id') rescue DmsfAccessError render_403 end - def check_project(entry) - if entry && entry.project != @project - raise DmsfAccessError, l(:error_entry_project_does_not_match_current_project) - end - end - def copy_folder(folder) copy = folder.clone copy.id = folder.id copy end -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 c612b455..16dabde0 100644 --- a/app/controllers/dmsf_files_controller.rb +++ b/app/controllers/dmsf_files_controller.rb @@ -59,97 +59,104 @@ class DmsfFilesController < ApplicationController end @revision = @file.last_revision - # TODO: line bellow is to handle old installations with errors in data handling - @revision.name = @file.name @revision_pages = Paginator.new @file.revisions.visible.count, params['per_page'] ? params['per_page'].to_i : 25, params['page'] render :layout => !request.xhr? end - - #TODO: don't create revision if nothing change + def create_revision - unless params[:dmsf_file_revision] - redirect_to :action => 'show', :id => @file - return - end - if @file.locked_for_user? - flash[:error] = l(:error_file_is_locked) - redirect_to :action => 'show', :id => @file - else - #TODO: validate folder_id - @revision = DmsfFileRevision.new(params[:dmsf_file_revision]) - - @revision.file = @file - @revision.project = @file.project - last_revision = @file.last_revision - @revision.source_revision = last_revision - @revision.user = User.current - - @revision.major_version = last_revision.major_version - @revision.minor_version = last_revision.minor_version - version = params[:version].to_i - file_upload = params[:file_upload] - if file_upload.nil? - @revision.disk_filename = last_revision.disk_filename - @revision.increase_version(version, false) - @revision.mime_type = last_revision.mime_type - @revision.size = last_revision.size - else - @revision.increase_version(version, true) - @revision.size = file_upload.size - @revision.disk_filename = @revision.new_storage_filename - @revision.mime_type = Redmine::MimeType.of(file_upload.original_filename) - end - - @file.name = @revision.name - @file.folder = @revision.folder - - if @revision.valid? && @file.valid? - @revision.save! - @revision.assign_workflow(params[:dmsf_workflow_id]) - if file_upload - @revision.copy_file_content(file_upload) - end - - if @file.locked? && !@file.locks.empty? - begin - @file.unlock! - flash[:notice] = "#{l(:notice_file_unlocked)}, " - rescue Exception => e - logger.error "Cannot unlock the file: #{e.message}" + if params[:dmsf_file_revision] + if @file.locked_for_user? + flash[:error] = l(:error_file_is_locked) + else + @revision = DmsfFileRevision.new(params[:dmsf_file_revision]) + + @revision.file = @file + @revision.project = @file.project + last_revision = @file.last_revision + @revision.source_revision = last_revision + @revision.user = User.current + + @revision.major_version = last_revision.major_version + @revision.minor_version = last_revision.minor_version + version = params[:version].to_i + file_upload = params[:file_upload] + unless file_upload + @revision.disk_filename = last_revision.disk_filename + @revision.increase_version(version, false) + @revision.mime_type = last_revision.mime_type + @revision.size = last_revision.size + else + @revision.increase_version(version, true) + @revision.size = file_upload.size + @revision.disk_filename = @revision.new_storage_filename + @revision.mime_type = Redmine::MimeType.of(file_upload.original_filename) + end + + @file.name = @revision.name + @file.folder = @revision.folder + + if @revision.valid? && @file.valid? + @revision.save! + @revision.assign_workflow(params[:dmsf_workflow_id]) + if file_upload + @revision.copy_file_content(file_upload) end + + if @file.locked? && !@file.locks.empty? + begin + @file.unlock! + flash[:notice] = "#{l(:notice_file_unlocked)}, " + rescue Exception => e + logger.error "Cannot unlock the file: #{e.message}" + end + end + @file.save! + @file.set_last_revision @revision + + flash[:notice] = (flash[:notice].nil? ? '' : flash[:notice]) + l(:notice_file_revision_created) + log_activity('new revision') + begin + recipients = DmsfMailer.get_notify_users(User.current, [@file]) + recipients.each do |u| + DmsfMailer.files_updated(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 + logger.error "Could not send email notifications: #{e.message}" + end end - @file.save! - @file.reload - - flash[:notice] = (flash[:notice].nil? ? '' : flash[:notice]) + l(:notice_file_revision_created) - log_activity('new revision') - begin - DmsfMailer.files_updated(User.current, [@file]).deliver - rescue Exception => e - logger.error "Could not send email notifications: #{e.message}" - end - redirect_to :action => 'show', :id => @file - else - render :action => 'show' end end + redirect_to :back end def delete if @file if @file.delete flash[:notice] = l(:notice_file_deleted) - log_activity('deleted') - DmsfMailer.files_deleted(User.current, [@file]).deliver + log_activity('deleted') + begin + DmsfMailer.get_notify_users(User.current, [@file]).each do |u| + DmsfMailer.files_deleted(u, @project, [@file]).deliver + end + rescue Exception => e + Rails.logger.error "Could not send email notifications: #{e.message}" + end else @file.errors.each do |e, msg| flash[:error] = msg end end end - redirect_to :controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @file.folder + redirect_to dmsf_folder_path(:id => @project, :folder_id => @file.folder) end def delete_revision @@ -172,9 +179,8 @@ class DmsfFilesController < ApplicationController else @file.lock! flash[:notice] = l(:notice_file_locked) - end - redirect_to params[:current] ? params[:current] : - {:controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @file.folder} + end + redirect_to :back end def unlock @@ -187,9 +193,8 @@ class DmsfFilesController < ApplicationController else flash[:error] = l(:error_only_user_that_locked_file_can_unlock_it) end - end - redirect_to params[:current] ? params[:current] : - {:controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @file.folder} + end + redirect_to :back end def notify_activate @@ -198,9 +203,8 @@ class DmsfFilesController < ApplicationController else @file.notify_activate flash[:notice] = l(:notice_file_notifications_activated) - end - redirect_to params[:current] ? params[:current] : - {:controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @file.folder} + end + redirect_to :back end def notify_deactivate @@ -209,9 +213,8 @@ class DmsfFilesController < ApplicationController else @file.notify_deactivate flash[:notice] = l(:notice_file_notifications_deactivated) - end - redirect_to params[:current] ? params[:current] : - {:controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @file.folder} + end + redirect_to :back end private @@ -234,13 +237,16 @@ class DmsfFilesController < ApplicationController def find_file @file = DmsfFile.visible.find(params[:id]) @project = @file.project - rescue + rescue ActiveRecord::RecordNotFound + render_404 end def find_revision @revision = DmsfFileRevision.visible.find(params[:id]) @file = @revision.file @project = @file.project + rescue ActiveRecord::RecordNotFound + render_404 end def check_project(entry) diff --git a/app/controllers/dmsf_files_copy_controller.rb b/app/controllers/dmsf_files_copy_controller.rb index e080c5a8..5213e3ff 100644 --- a/app/controllers/dmsf_files_copy_controller.rb +++ b/app/controllers/dmsf_files_copy_controller.rb @@ -68,14 +68,9 @@ class DmsfFilesCopyController < ApplicationController end flash[:notice] = l(:notice_file_copied) - log_activity(new_file, 'was copied (is copy)') - begin - DmsfMailer.files_updated(User.current, [new_file]).deliver - rescue ActionView::MissingTemplate => e - Rails.logger.error "Could not send email notifications: #{e.message}" - end + log_activity(new_file, 'was copied (is copy)') - redirect_to :controller => 'dmsf_files', :action => 'show', :id => new_file + redirect_to dmsf_file_path(new_file) end def move @@ -105,15 +100,9 @@ class DmsfFilesCopyController < ApplicationController @file.reload flash[:notice] = l(:notice_file_moved) - log_activity(@file, 'was moved (is copy)') - begin - # TODO: implement proper mail notification - DmsfMailer.files_updated(User.current, [@file]).deliver - rescue ActionView::MissingTemplate => e - Rails.logger.error "Could not send email notifications: #{e.message}" - end + log_activity(@file, 'was moved (is copy)') - redirect_to :controller => 'dmsf_files', :action => 'show', :id => @file + redirect_to dmsf_file_path(@file) end private diff --git a/app/controllers/dmsf_folders_copy_controller.rb b/app/controllers/dmsf_folders_copy_controller.rb index dde421a5..3b53ae82 100644 --- a/app/controllers/dmsf_folders_copy_controller.rb +++ b/app/controllers/dmsf_folders_copy_controller.rb @@ -69,15 +69,8 @@ class DmsfFoldersCopyController < ApplicationController flash[:notice] = l(:notice_folder_copied) log_activity(new_folder, 'was copied (is copy)') - - #TODO: implement proper notification for all new files - #begin - # DmsfMailer.files_updated(User.current, [new_file]).deliver - #rescue ActionView::MissingTemplate => e - # Rails.logger.error "Could not send email notifications: " + e - #end - - redirect_to :controller => 'dmsf', :action => 'show', :id => @target_project, :folder_id => new_folder + + redirect_to dmsf_folder_path(:id => @target_project, :folder_id => new_folder) end private diff --git a/app/controllers/dmsf_links_controller.rb b/app/controllers/dmsf_links_controller.rb new file mode 100644 index 00000000..92beba27 --- /dev/null +++ b/app/controllers/dmsf_links_controller.rb @@ -0,0 +1,170 @@ +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-14 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 DmsfLinksController < ApplicationController + unloadable + + model_object DmsfLink + before_filter :find_model_object, :only => [:destroy] + before_filter :find_link_project + before_filter :authorize + + def new + @dmsf_link = DmsfLink.new(:project_id => params[:project_id]) + + if params[:dmsf_link].present? + # Reload + @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] + @dmsf_link.target_project_id = params[:dmsf_link][:target_project_id] + @target_folder_id = params[:dmsf_link][:target_folder_id].to_i if params[:reload].blank? && DmsfLinksHelper.is_a_number?(params[:dmsf_link][:target_folder_id]) + if @type == 'link_to' + if params[:dmsf_link][:dmsf_file_id].present? + file = DmsfFile.find_by_id params[:dmsf_link][:dmsf_file_id] + @dmsf_link.name = file.title if file + else + folder = DmsfFolder.find_by_id params[:dmsf_link][:dmsf_folder_id] + @dmsf_link.name = folder.title if folder + end + else + if params[:dmsf_link][:target_file_id].present? + @target_file_id = params[:dmsf_link][:target_file_id] + file = DmsfFile.find_by_id @target_file_id + + 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? + @dmsf_link.name = file.title + end + end + else + folder = DmsfFolder.find_by_id params[:dmsf_link][:target_folder_id] + + if folder + if folder.project_id == @dmsf_link.target_project_id + @dmsf_link.name = folder.title if folder + end + end + end + end + else + # Link from/to + @dmsf_link.dmsf_folder_id = params[:dmsf_folder_id] + @dmsf_file_id = params[:dmsf_file_id] + @type = params[:type] + @dmsf_link.target_project_id = params[:project_id] + @target_folder_id = params[:dmsf_folder_id].to_i if params[:dmsf_folder_id].present? + if @type == 'link_to' + if @dmsf_file_id + file = DmsfFile.find_by_id @dmsf_file_id + @dmsf_link.name = file.title if file + else + folder = DmsfFolder.find_by_id @target_folder_id + @dmsf_link.name = folder.title if folder + end + end + end + + render :layout => !request.xhr? + end + + def create + @dmsf_link = DmsfLink.new + + if params[:dmsf_link][:type] == 'link_from' + # Link from + @dmsf_link.project_id = params[:dmsf_link][:project_id] + @dmsf_link.dmsf_folder_id = params[:dmsf_link][:dmsf_folder_id] + @dmsf_link.target_project_id = params[:dmsf_link][:target_project_id] + if params[:dmsf_link][:target_file_id].present? + @dmsf_link.target_id = params[:dmsf_link][:target_file_id] + @dmsf_link.target_type = DmsfFile.model_name + else + @dmsf_link.target_id = DmsfLinksHelper.is_a_number?(params[:dmsf_link][:target_folder_id]) ? params[:dmsf_link][:target_folder_id].to_i : nil + @dmsf_link.target_type = DmsfFolder.model_name + 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) + else + @dmsf_file_id = params[:dmsf_link][:dmsf_file_id] + @type = params[:dmsf_link][:type] + @target_folder_id = params[:dmsf_link][:target_folder_id].to_i if DmsfLinksHelper.is_a_number?(params[:dmsf_link][:target_folder_id]) + @target_file_id = @dmsf_link.target_id if @dmsf_link.target_type == DmsfFile.model_name + render :action => 'new' + end + else + # Link to + @dmsf_link.project_id = params[:dmsf_link][:target_project_id] + @dmsf_link.dmsf_folder_id = DmsfLinksHelper.is_a_number?(params[:dmsf_link][:target_folder_id]) ? params[:dmsf_link][:target_folder_id].to_i : nil + @dmsf_link.target_project_id = params[:dmsf_link][:project_id] + if params[:dmsf_link][:dmsf_file_id].present? + @dmsf_link.target_id = params[:dmsf_link][:dmsf_file_id] + @dmsf_link.target_type = DmsfFile.model_name + else + @dmsf_link.target_id = params[:dmsf_link][:dmsf_folder_id] + @dmsf_link.target_type = DmsfFolder.model_name + end + @dmsf_link.name = params[:dmsf_link][:name] + + if @dmsf_link.save + flash[:notice] = l(:notice_successful_create) + if params[:dmsf_link][:dmsf_file_id].present? + redirect_to dmsf_file_path(@dmsf_link.target_file) + else + redirect_to edit_dmsf_path(:id => params[:dmsf_link][:project_id], :folder_id => params[:dmsf_link][:dmsf_folder_id]) + end + else + @dmsf_file_id = params[:dmsf_link][:dmsf_file_id] + @type = params[:dmsf_link][:type] + @target_folder_id = @dmsf_link.dmsf_folder_id + @dmsf_link.target_project_id = @dmsf_link.project.id + @dmsf_link.project_id = params[:dmsf_link][:project_id] + @dmsf_link.dmsf_folder_id = params[:dmsf_link][:dmsf_folder_id] + render :action => 'new' + end + end + end + + def destroy + begin + @dmsf_link.destroy + flash[:notice] = l(:notice_successful_delete) + rescue + flash[:error] = l(:error_unable_delete_dmsf_workflow) + end + + redirect_to :back + end + + private + + def find_link_project + if @dmsf_link + @project = @dmsf_link.project + else + @project = Project.find(params[:dmsf_link].present? ? params[:dmsf_link][:project_id] : params[:project_id]) + end + rescue ActiveRecord::RecordNotFound + render_404 + end + +end \ No newline at end of file diff --git a/app/controllers/dmsf_state_controller.rb b/app/controllers/dmsf_state_controller.rb index 69b6bc52..19eaf5e9 100644 --- a/app/controllers/dmsf_state_controller.rb +++ b/app/controllers/dmsf_state_controller.rb @@ -1,6 +1,7 @@ # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011-14 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,32 +23,18 @@ class DmsfStateController < ApplicationController menu_item :dmsf before_filter :find_project - before_filter :authorize - - helper :all + before_filter :authorize def user_pref_save - member = @project.members.find(:first, :conditions => {:user_id => User.current.id}) + member = @project.members.where(:user_id => User.current.id).first if member - member.dmsf_mail_notification = params[:email_notify]; + member.dmsf_mail_notification = params[:email_notify] member.save! flash[:notice] = l(:notice_your_preferences_were_saved) else flash[:warning] = l(:user_is_not_project_member) - end - redirect_to :controller => 'projects', :action => 'settings', :tab => 'dmsf', :id => @project + end + redirect_to settings_project_path(@project, :tab => 'dmsf') end - - private - - def find_project - @project = Project.find(params[:id]) - end - - def check_project(entry) - if entry && entry.project != @project - raise DmsfAccessError, l(:error_entry_project_does_not_match_current_project) - end - end - -end + +end \ No newline at end of file diff --git a/app/controllers/dmsf_upload_controller.rb b/app/controllers/dmsf_upload_controller.rb index 1109e28e..fa1e0347 100644 --- a/app/controllers/dmsf_upload_controller.rb +++ b/app/controllers/dmsf_upload_controller.rb @@ -1,6 +1,7 @@ # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2014 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 @@ -77,25 +78,28 @@ class DmsfUploadController < ApplicationController render :layout => false end end - - #TODO: flash notice when files saved and unlocked - #TODO: separate control for approval + def commit_files commited_files = params[:commited_files] if commited_files && commited_files.is_a?(Hash) files = [] failed_uploads = [] commited_files.each_value do |commited_file| - name = commited_file['name']; + name = commited_file[:name] new_revision = DmsfFileRevision.new file = DmsfFile.visible.find_file_by_name(@project, @folder, name) - if file.nil? + 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.notification = Setting.plugin_redmine_dmsf[:dmsf_default_notifications].present? new_revision.minor_version = 0 new_revision.major_version = 0 @@ -110,17 +114,17 @@ class DmsfUploadController < ApplicationController new_revision.minor_version = last_revision.minor_version end - commited_disk_filepath = "#{DmsfHelper.temp_dir}/#{commited_file['disk_filename'].gsub(/[\/\\]/,'')}" + commited_disk_filepath = "#{DmsfHelper.temp_dir}/#{commited_file[:disk_filename].gsub(/[\/\\]/,'')}" - new_revision.project = @project - new_revision.folder = @folder + new_revision.project = link ? link.target_project : @project + new_revision.folder = link ? link.target_folder : @folder new_revision.file = file new_revision.user = User.current new_revision.name = name - new_revision.title = commited_file['title'] - new_revision.description = commited_file['description'] - new_revision.comment = commited_file['comment'] - new_revision.increase_version(commited_file['version'].to_i, true) + new_revision.title = commited_file[:title] + new_revision.description = commited_file[:description] + new_revision.comment = commited_file[:comment] + new_revision.increase_version(commited_file[:version].to_i, true) new_revision.mime_type = Redmine::MimeType.of(new_revision.name) new_revision.size = File.size(commited_disk_filepath) @@ -142,7 +146,13 @@ class DmsfUploadController < ApplicationController 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 + # 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 @@ -151,33 +161,31 @@ class DmsfUploadController < ApplicationController end if new_revision.save - new_revision.assign_workflow(commited_file[:dmsf_workflow_id]) - file.reload - + new_revision.assign_workflow(commited_file[:dmsf_workflow_id]) new_revision.copy_file_content(file_upload) file_upload.close - File.delete(commited_disk_filepath) - + File.delete(commited_disk_filepath) + file.set_last_revision new_revision files.push(file) - - if commited_file['dmsf_file_revision'].present? - commited_file['dmsf_file_revision']['custom_field_values'].each do |v| - cv = CustomValue.where(:customized_id => new_revision.id, :custom_field_id => v[0]).first - if cv - cv.value = v[1] - cv.save - end - end - end else failed_uploads.push(commited_file) end end - unless files.empty? - files.each { |file| log_activity(file, 'uploaded') if file } - begin - DmsfMailer.files_updated(User.current, files).deliver - rescue ActionView::MissingTemplate => e + unless files.empty? + files.each { |file| log_activity(file, 'uploaded') if file } + begin + recipients = DmsfMailer.get_notify_users(User.current, files) + recipients.each do |u| + DmsfMailer.files_updated(u, @project, 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 end @@ -185,7 +193,7 @@ class DmsfUploadController < ApplicationController flash[:warning] = l(:warning_some_files_were_not_commited, :files => failed_uploads.map{|u| u['name']}.join(', ')) end end - redirect_to :controller => 'dmsf', :action => 'show', :id => @project, :folder_id => @folder + redirect_to dmsf_folder_path(:id => @project, :folder_id => @folder) end private @@ -193,22 +201,11 @@ class DmsfUploadController < ApplicationController 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_project - @project = Project.find(params[:id]) - end - + def find_folder - @folder = DmsfFolder.visible.find(params[:folder_id]) if params.keys.include?('folder_id') - check_project(@folder) + @folder = DmsfFolder.visible.find(params[:folder_id]) if params.keys.include?('folder_id') rescue DmsfAccessError render_403 end - - def check_project(entry) - if entry && entry.project != @project - raise DmsfAccessError, l(:error_entry_project_does_not_match_current_project) - end - end - -end + +end \ No newline at end of file diff --git a/app/controllers/dmsf_workflows_controller.rb b/app/controllers/dmsf_workflows_controller.rb index 1e8aae9c..b874ef29 100644 --- a/app/controllers/dmsf_workflows_controller.rb +++ b/app/controllers/dmsf_workflows_controller.rb @@ -1,6 +1,6 @@ # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2013 Karel Picman +# Copyright (C) 2011-14 Karel Picman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,13 +17,14 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class DmsfWorkflowsController < ApplicationController - unloadable - layout :workflows_layout + unloadable + model_object DmsfWorkflow + + before_filter :find_model_object, :except => [:create, :new, :index, :assign, :assignment] + before_filter :find_project + before_filter :authorize_custom - before_filter :find_workflow, :except => [:create, :new, :index, :assign, :assignment] - before_filter :find_project, :except => [:start] - before_filter :authorize_global - before_filter :authorize_custom, :except => [:assignment, :start, :new_action] + layout :workflows_layout def index if @project @@ -46,7 +47,7 @@ class DmsfWorkflowsController < ApplicationController if action.save revision = DmsfFileRevision.find_by_id params[:dmsf_file_revision_id] if revision - if @workflow.try_finish revision, action, (params[:step_action].to_i / 10) + 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 begin @@ -57,64 +58,106 @@ class DmsfWorkflowsController < ApplicationController end if revision.workflow == DmsfWorkflow::STATE_APPROVED # Just approved - revision.file.project.members.each do |member| + recipients = revision.file.project.members.collect{ |m| m.user } + if User.current && User.current.logged? && User.current.pref.no_self_notified + recipients.delete User.current + end + recipients.each do |user| DmsfMailer.workflow_notification( - member.user, - @workflow, + user, + @dmsf_workflow, revision, - l(:text_email_subject_approved, :name => @workflow.name), - l(:text_email_finished_approved, :name => @workflow.name, :filename => revision.file.name), - l(:text_email_to_see_history)).deliver if member.user + :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' + recipients = revision.file.project.members.collect{ |m| m.user } + if User.current && User.current.logged? && User.current.pref.no_self_notified + recipients.delete User.current + end + 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 else # Just rejected - recipients = @workflow.participiants + recipients = @dmsf_workflow.participiants recipients.push User.find_by_id revision.dmsf_workflow_assigned_by + recipients.uniq! + if User.current && User.current.logged? && User.current.pref.no_self_notified + recipients.delete User.current + end recipients.each do |user| DmsfMailer.workflow_notification( user, - @workflow, + @dmsf_workflow, revision, - l(:text_email_subject_rejected, :name => @workflow.name), - l(:text_email_finished_rejected, :name => @workflow.name, :filename => revision.file.name, :notice => action.note), - l(:text_email_to_see_history)).deliver if user + :text_email_subject_rejected, + :text_email_finished_rejected, + :text_email_to_see_history, + action.note).deliver if user + 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 end else if action.action == DmsfWorkflowStepAction::ACTION_DELEGATE # Delegation - delegate = User.find_by_id params[:step_action].to_i / 10 - DmsfMailer.workflow_notification( - delegate, - @workflow, - revision, - l(:text_email_subject_delegated, :name => @workflow.name), - l(:text_email_finished_delegated, :name => @workflow.name, :filename => revision.file.name, :notice => action.note), - l(:text_email_to_proceed)).deliver if delegate + delegate = User.find_by_id params[:step_action].to_i / 10 + if delegate + DmsfMailer.workflow_notification( + 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) + end + end else # Next step - assignments = @workflow.next_assignments revision.id + assignments = @dmsf_workflow.next_assignments revision.id unless assignments.empty? if assignments.first.dmsf_workflow_step.step != action.dmsf_workflow_step_assignment.dmsf_workflow_step.step # Next step assignments.each do |assignment| DmsfMailer.workflow_notification( assignment.user, - @workflow, + @dmsf_workflow, revision, - l(:text_email_subject_requires_approval, :name => @workflow.name), - l(:text_email_finished_step, :name => @workflow.name, :filename => revision.file.name), - l(:text_email_to_proceed)).deliver if assignment.user + :text_email_subject_requires_approval, + :text_email_finished_step, + :text_email_to_proceed).deliver if assignment.user end to = User.find_by_id revision.dmsf_workflow_assigned_by DmsfMailer.workflow_notification( to, - @workflow, + @dmsf_workflow, revision, - l(:text_email_subject_updated, :name => @workflow.name), - l(:text_email_finished_step_short, :name => @workflow.name, :filename => revision.file.name), - l(:text_email_to_see_status)).deliver if to - end + :text_email_subject_updated, + :text_email_finished_step_short, + :text_email_to_see_status).deliver if to + if Setting.plugin_redmine_dmsf[:dmsf_display_notified_recipients] == '1' + recipients = assignments.collect{ |a| a.user } + recipients << to if to && !recipients.include?(to) + 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 + end end end end @@ -163,12 +206,12 @@ class DmsfWorkflowsController < ApplicationController end def new - @workflow = DmsfWorkflow.new + @dmsf_workflow = DmsfWorkflow.new end def create - @workflow = DmsfWorkflow.new(:name => params[:name], :project_id => params[:project_id]) - if request.post? && @workflow.save + @dmsf_workflow = DmsfWorkflow.new(:name => params[:name], :project_id => params[:project_id]) + if request.post? && @dmsf_workflow.save flash[:notice] = l(:notice_successful_create) if @project redirect_to settings_project_path(@project, :tab => 'dmsf_workflow') @@ -180,11 +223,8 @@ class DmsfWorkflowsController < ApplicationController end end - def edit - end - def update - if request.put? && @workflow.update_attributes({:name => params[:name]}) + if request.put? && @dmsf_workflow.update_attributes({:name => params[:name]}) flash[:notice] = l(:notice_successful_update) if @project redirect_to settings_project_path(@project, :tab => 'dmsf_workflow') @@ -198,7 +238,7 @@ class DmsfWorkflowsController < ApplicationController def destroy begin - @workflow.destroy + @dmsf_workflow.destroy flash[:notice] = l(:notice_successful_delete) rescue flash[:error] = l(:error_unable_delete_dmsf_workflow) @@ -218,14 +258,14 @@ class DmsfWorkflowsController < ApplicationController if request.post? users = User.find_all_by_id(params[:user_ids]) if params[:step] == '0' - step = @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.each do |user| - @workflow.dmsf_workflow_steps << DmsfWorkflowStep.new( - :dmsf_workflow_id => @workflow.id, + @dmsf_workflow.dmsf_workflow_steps << DmsfWorkflowStep.new( + :dmsf_workflow_id => @dmsf_workflow.id, :step => step, :user_id => user.id, :operator => operator) @@ -238,10 +278,10 @@ class DmsfWorkflowsController < ApplicationController def remove_step if request.delete? - DmsfWorkflowStep.where(:dmsf_workflow_id => @workflow.id, :step => params[:step]).each do |ws| - @workflow.dmsf_workflow_steps.delete(ws) + DmsfWorkflowStep.where(:dmsf_workflow_id => @dmsf_workflow.id, :step => params[:step]).each do |ws| + @dmsf_workflow.dmsf_workflow_steps.delete(ws) end - @workflow.dmsf_workflow_steps.each do |ws| + @dmsf_workflow.dmsf_workflow_steps.each do |ws| n = ws.step.to_i if n > params[:step].to_i ws.step = n - 1 @@ -258,7 +298,7 @@ class DmsfWorkflowsController < ApplicationController def reorder_steps if request.put? - unless @workflow.reorder_steps(params[:step].to_i, params[:workflow_step][:move_to]) + unless @dmsf_workflow.reorder_steps(params[:step].to_i, params[:workflow_step][:move_to]) flash[:error] = l(:notice_cannot_renumber_steps) end end @@ -269,49 +309,67 @@ class DmsfWorkflowsController < ApplicationController def start revision = DmsfFileRevision.find_by_id(params[:dmsf_file_revision_id]) - if revision - if request.post? - revision.set_workflow(@workflow.id, params[:action]) - if revision.save - assignments = @workflow.next_assignments revision.id - assignments.each do |assignment| - DmsfMailer.workflow_notification( - assignment.user, - @workflow, - revision, - l(:text_email_subject_started, :name => @workflow.name), - l(:text_email_started, :name => @workflow.name, :filename => revision.file.name), - l(:text_email_to_proceed)).deliver if assignment.user - end - flash[:notice] = l(:notice_workflow_started) - else - flash[:error] = l(:notice_cannot_start_workflow) + if revision + revision.set_workflow(@dmsf_workflow.id, params[:action]) + if revision.save + assignments = @dmsf_workflow.next_assignments revision.id + assignments.each do |assignment| + DmsfMailer.workflow_notification( + assignment.user, + @dmsf_workflow, + revision, + :text_email_subject_started, + :text_email_started, + :text_email_to_proceed).deliver if assignment.user end - end + if Setting.plugin_redmine_dmsf[:dmsf_display_notified_recipients] == '1' + recipients = assignments.collect { |a| a.user } + recipients.uniq! + if User.current && User.current.logged? && User.current.pref.no_self_notified + recipients.delete User.current + end + 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 + flash[:notice] = l(:notice_workflow_started) + else + flash[:error] = l(:notice_cannot_start_workflow) + end end redirect_to :back end private - def find_workflow - @workflow = DmsfWorkflow.find_by_id(params[:id]) - end - + def find_project - if @workflow && @workflow.project - @project = @workflow.project - elsif params[:project_id].present? - @project = Project.find_by_id params[:project_id] + 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.project if revision + end + else + if params[:project_id].present? + @project = Project.find_by_id params[:project_id] + else + @project = Project.find_by_identifier params[:id] + end end end - def workflows_layout - find_workflow - find_project + def workflows_layout @project ? 'base' : 'admin' end - def authorize_custom - require_admin unless @project + def authorize_custom + if @project + authorize + else + require_admin + end end -end +end \ No newline at end of file diff --git a/app/helpers/dmsf_helper.rb b/app/helpers/dmsf_helper.rb index 08f28bf0..e06e403d 100644 --- a/app/helpers/dmsf_helper.rb +++ b/app/helpers/dmsf_helper.rb @@ -74,6 +74,6 @@ module DmsfHelper # 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 end diff --git a/app/helpers/dmsf_links_helper.rb b/app/helpers/dmsf_links_helper.rb new file mode 100644 index 00000000..9a1a89a7 --- /dev/null +++ b/app/helpers/dmsf_links_helper.rb @@ -0,0 +1,40 @@ +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2014 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 DmsfLinksHelper + + def folder_tree_options_for_select(folder_tree, options = {}) + s = '' + folder_tree.each do |name, id| + tag_options = {:value => id} + if id == options[:selected] + tag_options[:selected] = 'selected' + else + tag_options[:selected] = nil + end + s << content_tag('option', name, tag_options) + end + s.html_safe + end + + # An integer test + def self.is_a_number?(s) + s.to_s.match(/\A[+-]?\d+?(\.\d+)?\Z/) == nil ? false : true + end + +end diff --git a/app/models/dmsf_file.rb b/app/models/dmsf_file.rb index 221bb126..8d591f05 100644 --- a/app/models/dmsf_file.rb +++ b/app/models/dmsf_file.rb @@ -1,6 +1,7 @@ # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011-14 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,14 +32,16 @@ class DmsfFile < ActiveRecord::Base belongs_to :project 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 :revisions, :class_name => 'DmsfFileRevision', :foreign_key => 'dmsf_file_id', :order => "#{DmsfFileRevision.table_name}.major_version DESC, #{DmsfFileRevision.table_name}.minor_version DESC, #{DmsfFileRevision.table_name}.updated_at DESC", :dependent => :destroy has_many :locks, :class_name => 'DmsfLock', :foreign_key => 'entity_id', :order => "#{DmsfLock.table_name}.updated_at DESC", :conditions => {:entity_type => 0}, - :dependent => :destroy - belongs_to :deleted_by_user, :class_name => 'User', :foreign_key => 'deleted_by_user_id' + :dependent => :destroy + has_many :referenced_links, :class_name => 'DmsfLink', :foreign_key => 'target_id', + :conditions => {:target_type => DmsfFile.model_name}, :dependent => :destroy scope :visible, lambda {|*args| where(DmsfFile.visible_condition(args.shift || User.current, *args)).readonly(false)} @@ -64,14 +67,15 @@ class DmsfFile < ActiveRecord::Base :url => Proc.new {|o| {:controller => 'dmsf_files', :action => 'show', :id => o}}, :datetime => Proc.new {|o| o.updated_at }, :author => Proc.new {|o| o.last_revision.user } - - - @@storage_path = Setting.plugin_redmine_dmsf['dmsf_storage_directory'].strip + + @@storage_path = nil def self.storage_path - if !File.exists?(@@storage_path) - Dir.mkdir(@@storage_path) - end + unless @@storage_path.present? + @@storage_path = Setting.plugin_redmine_dmsf['dmsf_storage_directory'].strip + @@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 end @@ -81,16 +85,11 @@ class DmsfFile < ActiveRecord::Base @@storage_path = obj end - def self.project_root_files(project) - visible.where(:project_id => project.id, :dmsf_folder_id => nil).order('name ASC') - end - - def self.find_file_by_name(project, folder, name) - if folder - visible.where(:project_id => project, :dmsf_folder_id => folder.id, :name => name).first - else - visible.where(:project_id => project, :dmsf_folder_id => nil, :name => name).first - end + def self.find_file_by_name(project, folder, name) + where( + :project_id => project, + :dmsf_folder_id => folder ? folder.id : nil, + :name => name).visible.first end def last_revision @@ -99,14 +98,19 @@ class DmsfFile < ActiveRecord::Base end @last_revision end + + def set_last_revision(new_revision) + @last_revision = new_revision + end def delete if locked_for_user? + Rails.logger.info l(:error_file_is_locked) errors[:base] << l(:error_file_is_locked) return false end begin - if Setting.plugin_redmine_dmsf['dmsf_really_delete_files'] + if Setting.plugin_redmine_dmsf['dmsf_really_delete_files'] self.revisions.visible.each {|r| r.delete(true)} self.destroy else @@ -167,11 +171,7 @@ class DmsfFile < ActiveRecord::Base def notify_activate self.notification = true self.save! - end - - def display_name - return self.name - end + end # Returns an array of projects that current user can copy file to def self.allowed_target_projects_on_copy @@ -190,7 +190,7 @@ class DmsfFile < ActiveRecord::Base return false end - new_revision = self.last_revision.clone + new_revision = self.last_revision.clone new_revision.folder = folder new_revision.project = folder ? folder.project : project @@ -201,10 +201,10 @@ class DmsfFile < ActiveRecord::Base new_revision.custom_values << CustomValue.new({:custom_field => cv.custom_field, :value => cv.value}) end - # If the target project differs from the source project we must physically move the file + # If the target project differs from the source project we must physically copy the file if self.project != new_revision.project if File.exist? self.last_revision.disk_file - FileUtils.mv self.last_revision.disk_file, new_revision.disk_file + FileUtils.cp self.last_revision.disk_file, new_revision.disk_file end end @@ -221,9 +221,8 @@ class DmsfFile < ActiveRecord::Base file.name = self.name file.notification = Setting.plugin_redmine_dmsf['dmsf_default_notifications'].present? - if file.save + if file.save && self.last_revision new_revision = self.last_revision.clone - new_revision.file = file new_revision.folder = folder new_revision.project = folder ? folder.project : project @@ -376,4 +375,11 @@ class DmsfFile < ActiveRecord::Base [results, results_count] end + def display_name + if self.name.length > 50 + return "#{self.name[0, 25]}...#{self.name[-25, 25]}" + end + self.name + end + end diff --git a/app/models/dmsf_file_revision.rb b/app/models/dmsf_file_revision.rb index 42a4b479..3ab0f8ea 100644 --- a/app/models/dmsf_file_revision.rb +++ b/app/models/dmsf_file_revision.rb @@ -33,21 +33,21 @@ class DmsfFileRevision < ActiveRecord::Base 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}}, - :datetime => Proc.new {|o| o.updated_at }, - :description => Proc.new {|o| o.comment }, - :author => Proc.new {|o| o.user } + :url => Proc.new {|o| {:controller => 'dmsf_files', :action => 'show', :id => o.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_files', - :timestamp => "#{DmsfFileRevision.table_name}.updated_at", - :author_key => "#{DmsfFileRevision.table_name}.user_id", - :permission => :view_dmsf_files, - :find_options => {:select => "#{DmsfFileRevision.table_name}.*", - :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", - :conditions => ["#{DmsfFile.table_name}.deleted = :false", {:false => false}] - } + :timestamp => "#{DmsfFileRevision.table_name}.updated_at", + :author_key => "#{DmsfFileRevision.table_name}.user_id", + :permission => :view_dmsf_files, + :find_options => {:select => "#{DmsfFileRevision.table_name}.*", + :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", + :conditions => ["#{DmsfFile.table_name}.deleted = :false", {:false => false}] + } validates :title, :name, :presence => true validates_format_of :name, :with => DmsfFolder.invalid_characters, @@ -56,8 +56,7 @@ class DmsfFileRevision < ActiveRecord::Base def self.remove_extension(filename) filename[0, (filename.length - File.extname(filename).length)] end - - # TODO: check if better to move to dmsf_upload class + def self.filename_to_title(filename) remove_extension(filename).gsub(/_+/, ' '); end @@ -130,8 +129,7 @@ class DmsfFileRevision < ActiveRecord::Base content_type = 'application/octet-stream' if content_type.blank? content_type.to_s end - - # TODO: use standard clone method + def clone new_revision = DmsfFileRevision.new new_revision.file = self.file @@ -205,11 +203,7 @@ class DmsfFileRevision < ActiveRecord::Base when 2 then self.major_version + 1 else self.major_version end - end - - def display_title - return self.title - end + end def new_storage_filename raise DmsfAccessError, 'File id is not set' unless self.file.id @@ -233,5 +227,10 @@ class DmsfFileRevision < ActiveRecord::Base def available_custom_fields DmsfFileRevisionCustomField.all end + + def iversion + parts = self.version.split '.' + parts.size == 2 ? parts[0].to_i * 1000 + parts[1].to_i : 0 + end end diff --git a/app/models/dmsf_file_revision_custom_field.rb b/app/models/dmsf_file_revision_custom_field.rb index 9f6b564a..2793b424 100644 --- a/app/models/dmsf_file_revision_custom_field.rb +++ b/app/models/dmsf_file_revision_custom_field.rb @@ -18,7 +18,17 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 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 == y + end + end + end \ No newline at end of file diff --git a/app/models/dmsf_folder.rb b/app/models/dmsf_folder.rb index 0869505e..23011c42 100644 --- a/app/models/dmsf_folder.rb +++ b/app/models/dmsf_folder.rb @@ -1,6 +1,7 @@ # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011-14 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,18 +27,23 @@ class DmsfFolder < ActiveRecord::Base belongs_to :project belongs_to :folder, :class_name => 'DmsfFolder', :foreign_key => 'dmsf_folder_id' - has_many :subfolders, :class_name => 'DmsfFolder', :foreign_key => 'dmsf_folder_id', :order => "#{DmsfFolder.table_name}.title ASC", - :dependent => :destroy - has_many :files, :class_name => 'DmsfFile', :foreign_key => 'dmsf_folder_id', - :dependent => :destroy + 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 belongs_to :user - + has_many :folder_links, :class_name => 'DmsfLink', :foreign_key => 'dmsf_folder_id', + :conditions => {:target_type => DmsfFolder.model_name}, :dependent => :destroy + has_many :file_links, :class_name => 'DmsfLink', :foreign_key => 'dmsf_folder_id', + :conditions => {:target_type => DmsfFile.model_name}, :dependent => :destroy + has_many :referenced_links, :class_name => 'DmsfLink', :foreign_key => 'target_id', + :conditions => {:target_type => DmsfFolder.model_name}, :dependent => :destroy has_many :locks, :class_name => 'DmsfLock', :foreign_key => 'entity_id', :order => "#{DmsfLock.table_name}.updated_at DESC", :conditions => {:entity_type => 1}, :dependent => :destroy - scope :visible, lambda {|*args| {:conditions => '' }} #For future use, however best to be referenced now + scope :visible, lambda {|*args| {:conditions => '' }} #For future use, however best to be referenced now acts_as_customizable @@ -68,10 +74,6 @@ class DmsfFolder < ActiveRecord::Base return true end - def self.project_root_folders(project) - visible.where(:project_id => project.id, :dmsf_folder_id => nil, ).order('title ASC').all - end - def self.find_by_title(project, folder, title) if folder visible.where(:project_id => project.id, :dmsf_folder_id => nil, :title => title).first @@ -126,27 +128,42 @@ class DmsfFolder < ActiveRecord::Base def self.directory_tree(project, current_folder = nil) tree = [[l(:link_documents), nil]] - DmsfFolder.visible.project_root_folders(project).each do |folder| + project.dmsf_folders.visible.each do |folder| unless folder == current_folder tree.push(["...#{folder.title}", folder.id]) directory_subtree(tree, folder, 2, current_folder) end end return tree - end + end + + def folder_tree + tree = [[self.title, self.id]] + DmsfFolder.directory_subtree(tree, self, 2, nil) + return tree + end + + def self.file_list(files) + options = Array.new + options.push ['', nil] + files.each do |f| + options.push [f.title, f.id] + end + options + end def deep_file_count file_count = self.files.visible.count self.subfolders.visible.each {|subfolder| file_count += subfolder.deep_file_count} - file_count + file_count + self.file_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 - end - + folder_count + self.folder_links.visible.count + end + def deep_size size = 0 self.files.visible.each {|file| size += file.size} @@ -181,11 +198,19 @@ class DmsfFolder < ActiveRecord::Base return new_folder unless new_folder.save self.files.visible.each do |f| - f.copy_to(project, new_folder) + f.copy_to project, new_folder end - self.subfolders.each do |s| - s.copy_to(project, new_folder) + self.subfolders.visible.each do |s| + s.copy_to project, new_folder + end + + self.folder_links.visible.each do |l| + l.copy_to project, new_folder + end + + self.file_links.visible.each do |l| + l.copy_to project, new_folder end return new_folder @@ -239,7 +264,7 @@ class DmsfFolder < ActiveRecord::Base def self.directory_subtree(tree, folder, level, current_folder) folder.subfolders.visible.each do |subfolder| unless subfolder == current_folder - tree.push(["#{"..." * level}#{subfolder.title}", subfolder.id]) + tree.push(["#{'...' * level}#{subfolder.title}", subfolder.id]) directory_subtree(tree, subfolder, level + 1, current_folder) end end diff --git a/app/models/dmsf_link.rb b/app/models/dmsf_link.rb new file mode 100644 index 00000000..a216fed1 --- /dev/null +++ b/app/models/dmsf_link.rb @@ -0,0 +1,100 @@ +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-14 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 DmsfLink < ActiveRecord::Base + unloadable + + belongs_to :project + belongs_to :dmsf_folder + validates :name, :presence => true + validates :target_id, :presence => true + validates_length_of :name, :maximum => 255 + scope :visible, where('NOT deleted') + + def target_folder_id + if self.target_type == DmsfFolder.model_name + self.target_id + else + f = DmsfFile.find_by_id self.target_id + f.dmsf_folder_id if f + end + end + + def target_folder + DmsfFolder.find_by_id self.target_folder_id if self.target_folder_id + end + + def target_file_id + self.target_id if self.target_type == DmsfFile.model_name + end + + def target_file + DmsfFile.find_by_id self.target_file_id if self.target_file_id + end + + def target_project + Project.find_by_id self.target_project_id + end + + def folder + DmsfFolder.find_by_id self.dmsf_folder_id + end + + def title + self.name + end + + def self.find_link_by_file_name(project, folder, filename) + links = DmsfLink.where( + :project_id => project.id, + :dmsf_folder_id => folder ? folder.id : nil, + :target_type => DmsfFile.model_name).visible.all + links.each do |link| + return link if link.target_file.name == filename + end + nil + end + + def path + if self.target_type == DmsfFile.model_name + file = self.target_file + path = file.dmsf_path.map { |element| element.is_a?(DmsfFile) ? element.name : element.title }.join('/') if file + else + folder = self.target_folder + path = folder.dmsf_path_str if folder + end + path.insert(0, "#{self.target_project.name}:") if self.project_id != self.target_project_id && path + if path.length > 50 + return "#{path[0, 25]}...#{path[-25, 25]}" + end + path + end + + def copy_to(project, folder) + link = DmsfLink.new( + :target_project_id => self.target_project_id, + :target_id => self.target_id, + :target_type => self.target_type, + :name => self.name, + :project_id => project.id, + :dmsf_folder_id => folder ? folder.id : nil) + link.save + link + end + +end \ No newline at end of file diff --git a/app/models/dmsf_mailer.rb b/app/models/dmsf_mailer.rb index cba1d8cc..4a669856 100644 --- a/app/models/dmsf_mailer.rb +++ b/app/models/dmsf_mailer.rb @@ -1,6 +1,7 @@ # Redmine plugin for Document Management System "Features" # # Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011-14 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,72 +20,83 @@ require 'mailer' class DmsfMailer < Mailer + layout 'mailer' - def files_updated(user, files) - project = files[0].project - files = files.select { |file| file.notify? } + def files_updated(user, project, files) + if user && project && files.count > 0 + files = files.select { |file| file.notify? } + + redmine_headers 'Project' => project.identifier if project + + @files = files + @project = project + + set_language_if_valid user.language + mail :to => user.mail, + :subject => l(:text_email_doc_updated_subject, :project => project.name) + end + end + + def files_deleted(user, project, files) + if user && files.count > 0 + files = files.select { |file| file.notify? } + + redmine_headers 'Project' => project.identifier if project + + @files = files + @project = project + + set_language_if_valid user.language + mail :to => user.mail, + :subject => l(:text_email_doc_deleted_subject, :project => project.name) + end + end - redmine_headers "Project" => project.identifier - - @user = user - @files = files - @project = project - - mail :to => get_notify_user_emails(user, files), - :subject => "#{project.name}: Dmsf files updated" - end - - def files_deleted(user, files) - project = files[0].project - files = files.select { |file| file.notify? } + def send_documents(project, user, email_params) + zipped_content_data = open(email_params[:zipped_content], 'rb') { |io| io.read } - redmine_headers 'Project' => project.identifier + redmine_headers 'Project' => project.identifier if project - @user = user - @files = files - @project = project - - mail :to => get_notify_user_emails(user, files), - :subject => "#{project.name}: Dmsf files deleted" + @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 end - def send_documents(user, email_to, email_cc, email_subject, zipped_content, email_plain_body) - zipped_content_data = open(zipped_content, 'rb') {|io| io.read } - - @body = email_plain_body - - attachments['Documents.zip'] = {:content_type => 'application/zip', :content => zipped_content_data} - mail(:to => email_to, :cc => email_cc, :subject => email_subject, :from => user.mail) - end - - def workflow_notification(user, workflow, revision, subject, text1, text2) + def workflow_notification(user, workflow, revision, subject_id, text1_id, text2_id, notice = nil) if user && workflow && revision - set_language_if_valid user.language + if revision.file && revision.file.project + @project = revision.file.project + redmine_headers 'Project' => @project.identifier + end + set_language_if_valid user.language @user = user @workflow = workflow - @revision = revision - @text1 = text1 - @text2 = text2 - mail :to => user.mail, :subject => subject + @revision = revision + @text1 = l(text1_id, :name => workflow.name, :filename => revision.file.name, :notice => notice) + @text2 = l(text2_id) + @notice = notice + mail :to => user.mail, :subject => l(subject_id, :name => workflow.name) end - end - - private + end - def get_notify_user_emails(user, files) - if files.empty? - return [] - end - - project = files[0].project - + def self.get_notify_users(user, files) + notify_files = files.select { |file| file.notify? } + return [] if notify_files.empty? + project = notify_files[0].project notify_members = project.members notify_members = notify_members.select do |notify_member| - notify_user = notify_member.user - if notify_user.pref[:no_self_notified] && notify_user == user + notify_user = notify_member.user + if notify_user == user && user.pref.no_self_notified false else - if notify_member.dmsf_mail_notification.nil? + unless notify_member.dmsf_mail_notification case notify_user.mail_notification when 'all' true @@ -101,7 +113,7 @@ class DmsfMailer < Mailer end end - notify_members.collect {|m| m.user.mail } + notify_members.collect { |m| m.user } end -end +end \ No newline at end of file diff --git a/app/models/dmsf_upload.rb b/app/models/dmsf_upload.rb index a5e3bc8a..5fb2161a 100644 --- a/app/models/dmsf_upload.rb +++ b/app/models/dmsf_upload.rb @@ -1,8 +1,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-14 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,12 +38,12 @@ class DmsfUpload def self.create_from_uploaded_file(project, folder, uploaded_file) uploaded = { - 'disk_filename' => DmsfHelper.temp_filename(uploaded_file.original_filename), - 'content_type' => uploaded_file.content_type.to_s, - 'original_filename' => uploaded_file.original_filename, + :disk_filename => DmsfHelper.temp_filename(uploaded_file.original_filename), + :content_type => uploaded_file.content_type.to_s, + :original_filename => uploaded_file.original_filename, } - File.open("#{DmsfHelper.temp_dir}/#{uploaded["disk_filename"]}", "wb") do |f| + File.open("#{DmsfHelper.temp_dir}/#{uploaded[:disk_filename]}", 'wb') do |f| while (buffer = uploaded_file.read(8192)) f.write(buffer) end @@ -52,15 +52,19 @@ class DmsfUpload end def initialize(project, folder, uploaded) - @name = uploaded['original_filename'] + @name = uploaded[:original_filename] - dmsf_file = DmsfFile.visible.find_file_by_name(project, folder, @name) + 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'] + @disk_filename = uploaded[:disk_filename] + @mime_type = uploaded[:content_type] @size = File.size(disk_file) - if dmsf_file.nil? || dmsf_file.last_revision.nil? + if file.nil? || file.last_revision.nil? @title = DmsfFileRevision.filename_to_title(@name) @description = nil @major_version = 0 @@ -68,24 +72,24 @@ class DmsfUpload @workflow = nil @custom_values = DmsfFileRevision.new(:file => DmsfFile.new(:project => @project)).custom_field_values else - last_revision = dmsf_file.last_revision + last_revision = file.last_revision @title = last_revision.title @description = last_revision.description @major_version = last_revision.major_version @minor_version = last_revision.minor_version @workflow = last_revision.workflow - @custom_values = Array.new(dmsf_file.last_revision.custom_values) + @custom_values = Array.new(file.last_revision.custom_values) # Add default value for CFs not existing - present_custom_fields = dmsf_file.last_revision.custom_values.collect(&:custom_field).uniq - dmsf_file.last_revision.available_custom_fields.each do |cf| + present_custom_fields = file.last_revision.custom_values.collect(&:custom_field).uniq + file.last_revision.available_custom_fields.each do |cf| unless present_custom_fields.include?(cf) @custom_values << CustomValue.new({:custom_field => cf, :value => cf.default_value}) if cf.default_value end end end - @locked = dmsf_file && dmsf_file.locked_for_user? + @locked = file && file.locked_for_user? end -end +end \ No newline at end of file diff --git a/app/models/dmsf_workflow.rb b/app/models/dmsf_workflow.rb index 7478ae68..480e6b82 100644 --- a/app/models/dmsf_workflow.rb +++ b/app/models/dmsf_workflow.rb @@ -32,7 +32,7 @@ class DmsfWorkflow < ActiveRecord::Base def participiants users = Array.new self.dmsf_workflow_steps.each do |step| - users << step.user + users << step.user unless users.include? step.user end users end @@ -110,7 +110,7 @@ class DmsfWorkflow < ActiveRecord::Base sql = '1=1' end - unless q.nil? || q.empty? + if q.present? User.active.sorted.where(sql).like(q) else User.active.sorted.where(sql) diff --git a/app/views/dmsf/_dir.html.erb b/app/views/dmsf/_dir.html.erb new file mode 100644 index 00000000..eda59eb0 --- /dev/null +++ b/app/views/dmsf/_dir.html.erb @@ -0,0 +1,108 @@ +<%#= +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2014 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. +%> + +<% locked_for_user = subfolder.locked_for_user? %> +<% locked = subfolder.locked? %> + +<%= check_box_tag(name, id, false, + :title => l(:title_check_for_zip_download_or_email)) %> + + <%= link_to(h(title), + dmsf_folder_path(:id => project, :folder_id => subfolder), + :class => 'icon icon-folder') %> + <% if link %> +
<%= link.path %>
+ <% else %> +
[<%= subfolder.files.visible.count + subfolder.file_links.visible.count %>]
+ <% end %> + + +<%= format_time(subfolder.updated_at) %> + <% if locked_for_user %> + <% if subfolder.lock.reverse[0].user %> + <%= link_to('', + user_path(subfolder.lock.reverse[0].user), + :title => l(:title_locked_by_user, :user => subfolder.lock.reverse[0].user), + :class => 'icon icon-dmsf-locked') %> + <% else %> + <%= content_tag(:span, '', :title => l(:notice_account_unknown_email), + :class => 'icon icon-dmsf-locked') %> + <% end %> + <% elsif locked %> + <%= content_tag(:span, '', :title => l(:title_locked_by_you), + :class => 'icon icon-dmsf-lockedbycurrent') %> + <% end %> + + + +<%= h(subfolder.user) %> + + <% if @folder_manipulation_allowed %> +
+ <% unless locked %> + <%= link_to('', + edit_dmsf_path(:id => project, :folder_id => subfolder), + :title => l(:link_edit, :title => h(subfolder.title)), + :class => 'icon icon-edit') %> + <%= link_to('', + lock_dmsf_path(:id => project, :folder_id => subfolder), + :title => l(:title_lock_file), + :class => 'icon icon-dmsf-lock') %> + <% if subfolder.notification %> + <%= link_to('', + notify_deactivate_dmsf_path(:id => project, :folder_id => subfolder), + :title => l(:title_notifications_active_deactivate), + :class => 'icon icon-notification-on') %> + <% else %> + <%= link_to('', + notify_activate_dmsf_path(:id => project, :folder_id => subfolder), + :title => l(:title_notifications_not_active_activate), + :class => 'icon icon-notification-off') %> + <% end %> + <% if link %> + <%= link_to('', + dmsf_link_path(link), + :data => {:confirm => l(:text_are_you_sure)}, + :method => :delete, + :title => l(:title_delete), + :class => 'icon icon-del') %> + <% else %> + <%= link_to('', + delete_dmsf_path(:id => project, :folder_id => @folder, :delete_folder_id => subfolder), + :data => {:confirm => l(:text_are_you_sure)}, + :title => l(:title_delete), + :class => 'icon icon-del') %> + <% end %> + <% else %> + + <% if (!locked_for_user || @force_file_unlock_allowed) && subfolder.unlockable? %> + <%= link_to('', + unlock_dmsf_path(:id => project, :folder_id => subfolder), + :title => l(:title_unlock_file), + :class => 'icon icon-dmsf-unlock')%> + <% end %> + <% end %> +
+ <% end %> + +0 +0 +<%= subfolder.updated_at.to_i %> +0 diff --git a/app/views/dmsf/_file.html.erb b/app/views/dmsf/_file.html.erb new file mode 100644 index 00000000..3c22b7c2 --- /dev/null +++ b/app/views/dmsf/_file.html.erb @@ -0,0 +1,183 @@ +<%#= +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-14 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. +%> + +<% 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, + :title => l(:title_check_for_zip_download_or_email)) %> + + <% file_download_url = url_for({:only_path => false, :controller => :dmsf_files, :action => 'show', :id => file, :download => ''}) %> + <%= link_to(h(title), + file_download_url, + :class => "icon icon-file #{DmsfHelper.filetype_css(file.name)}", + :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_download_url}") %> +
<%= h(link ? link.path : file.display_name) %>
+ +<%= 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 %> + <%= link_to('', + user_path(file.lock.reverse[0].user), + :title => l(:title_locked_by_user, :user => file.lock.reverse[0].user), + :class => 'icon icon-dmsf-locked') %> + <% else %> + <%= content_tag(:span, '', + :title => l(:notice_account_unknown_email), + :class => 'icon icon-dmsf-locked') %> + <% end %> + <% elsif locked %> + <%= content_tag(:span, '', :title => l(:title_locked_by_you), + :class => 'icon icon-dmsf-lockedbycurrent') %> + <% end %> + +<%= file.last_revision.version %> + + <% if wf && @file_manipulation_allowed %> + <%= link_to( + file.last_revision.workflow_str(false), + log_dmsf_workflow_path( + :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) %> + <% else %> + <%= file.last_revision.workflow_str(false) %> + <% end %> + +<%= h(file.last_revision.user) %> + + <% if @file_manipulation_allowed %> +
+ <% unless locked %> + <%= link_to('', + dmsf_file_path(:id => file), + :title => l(:link_details, :title => h(file.last_revision.title)), + :class => 'icon icon-dmsf-file-details') %> + <%= link_to('', + lock_dmsf_files_path(:id => file), + :title => l(:title_lock_file), + :class => 'icon icon-dmsf-lock') %> + <% if file.notification %> + <%= link_to('', + notify_deactivate_dmsf_files_path(:id => file), + :title => l(:title_notifications_active_deactivate), + :class => 'icon icon-notification-on') %> + <% else %> + <%= link_to('', + notify_activate_dmsf_files_path(:id => file), + :title => l(:title_notifications_not_active_activate), + :class => 'icon icon-notification-off') %> + <% end %> + <% if link %> + <%= link_to('', + dmsf_link_path(link), + :data => {:confirm => l(:text_are_you_sure)}, + :method => :delete, + :title => l(:title_delete), + :class => 'icon icon-del') %> + <% else %> + <%= link_to('', + dmsf_file_path(:id => file), + :data => {:confirm => l(:text_are_you_sure)}, + :method => :delete, + :title => l(:title_delete), + :class => 'icon icon-del') unless locked_for_user %> + <% end %> + <% else %> + + <% if (!locked_for_user || @force_file_unlock_allowed) && file.unlockable? %> + <%= link_to('', + unlock_dmsf_files_path(:id => file), + :title => l(:title_unlock_file), + :class => 'icon icon-dmsf-unlock')%> + <% else %> + + <% end %> + + + <% end %> + <% 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('', + 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), + :class => 'icon icon-dmsf-waiting-for-approval', + :remote => true) %> + <% else %> + <%= content_tag(:span, '', + :title => "#{l(:label_dmsf_wokflow_action_approve)} #{l(:label_dmsf_wokflow_action_reject)} #{l(:label_dmsf_wokflow_action_delegate)}", + :class => 'icon icon-dmsf-waiting-for-approval') %> + <% end %> + <% else %> + <%= content_tag(:span, '', + :title => "#{l(:label_dmsf_wokflow_action_approve)} #{l(:label_dmsf_wokflow_action_reject)} #{l(:label_dmsf_wokflow_action_delegate)}", + :class => 'icon icon-dmsf-waiting-for-approval') %> + <% end %> + <% when DmsfWorkflow::STATE_APPROVED %> + <%= content_tag(:span, '', :title => l(:title_approved), + :class => 'icon icon-dmsf-approved') %> + <% when DmsfWorkflow::STATE_ASSIGNED %> + <% if User.current && (file.last_revision.dmsf_workflow_assigned_by == User.current.id) && wf %> + <%= link_to('', + 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), + :class => 'icon icon-dmsf-assigned') %> + <% else %> + <%= content_tag(:span, '', + title => l(:label_dmsf_wokflow_action_start), + :class => 'icon icon-dmsf-assigned') %> + <% end %> + <% when DmsfWorkflow::STATE_REJECTED %> + <%= content_tag(:span, '', :title => l(:title_rejected), + :class => 'icon icon-dmsf-rejected') %> + <% else %> + <% if @workflows_available %> + <%= link_to('', + assign_dmsf_workflow_path( + :project_id => project.id, + :dmsf_file_revision_id => file.last_revision.id), + :title => l(:label_dmsf_wokflow_action_assign), + :class => 'icon icon-dmsf-none', + :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 diff --git a/app/views/dmsf/_path.html.erb b/app/views/dmsf/_path.html.erb index 2019d776..720314d4 100644 --- a/app/views/dmsf/_path.html.erb +++ b/app/views/dmsf/_path.html.erb @@ -18,17 +18,17 @@

<% if folder %> - <%= link_to l(:link_documents), dmsf_path(:id => @project) %> + <%= 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 %> <%= h(path_element.title) %> <% else %> - <%= link_to h(path_element.title), dmsf_path(:id => @project, :folder_id => path_element) %> + <%= link_to h(path_element.title), dmsf_folder_path(:id => @project, :folder_id => path_element) %> <% end %> <% end %> <% else %> - <%= link_to l(:link_documents), dmsf_path(:id => @project) %> + <%= link_to l(:link_documents), dmsf_folder_path(:id => @project) %> <% end %> <% if filename %> / diff --git a/app/views/dmsf/edit.html.erb b/app/views/dmsf/edit.html.erb index 6e537eb5..ce151d15 100644 --- a/app/views/dmsf/edit.html.erb +++ b/app/views/dmsf/edit.html.erb @@ -20,9 +20,43 @@ <% html_title(l(:dmsf)) %>
- <%= link_to(image_tag('copy.png'), - {:controller => :dmsf_folders_copy, :action => 'new', :id => @folder }, - :title => l(:title_copy)) if @folder.id %> + <% if User.current.allowed_to?(:folder_manipulation, @project) && params[:action] == 'edit' %> + <% 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') %> + <% 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 %> + <%= 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') %> + <%= 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.dmsf_folder_id, :delete_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 %> + <% end %> + <% end %>
<% create = @pathfolder == @parent %> @@ -64,7 +98,3 @@ <% end %> <%= wikitoolbar_for 'dmsf_folder_description' %> - -<% content_for :header_tags do %> - <%= stylesheet_link_tag 'dmsf', :plugin => 'redmine_dmsf' %> -<% end %> diff --git a/app/views/dmsf/edit_root.html.erb b/app/views/dmsf/edit_root.html.erb index a665999b..c16163be 100644 --- a/app/views/dmsf/edit_root.html.erb +++ b/app/views/dmsf/edit_root.html.erb @@ -20,11 +20,22 @@ <% html_title(l(:dmsf)) %>
+ <% if User.current.allowed_to?(:folder_manipulation, @project) %> + <% 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 %> + <% end %>
-

- <%= link_to l(:link_documents), {:controller => 'dmsf', :action => 'show', :id => @project } %> -

+<%= render(:partial => 'path', :locals => {:folder => nil, :filename => nil}) %> <%= form_for(@project, :url => {:action => 'save_root', :id => @project}, :html => {:method=>:post}) do |f| %> @@ -39,8 +50,4 @@ <%= submit_tag(l(:submit_save)) %> <% end %> -<%= wikitoolbar_for 'dmsf_folder_description' %> - -<% content_for :header_tags do %> - <%= stylesheet_link_tag 'dmsf', :plugin => 'redmine_dmsf' %> -<% 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 522f2c13..a4b180e0 100644 --- a/app/views/dmsf/email_entries.html.erb +++ b/app/views/dmsf/email_entries.html.erb @@ -1,7 +1,8 @@ <%# Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 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 @@ -28,10 +29,13 @@ <%= form_tag({:action => 'entries_email', :id => @project, :folder_id => @folder}, { :method=>:post, :class => 'tabular'}) 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(User.current.mail) %> + <%= h(Setting.mail_from) %>

<%= label_tag('email[to]', "#{l(:label_email_to)}:") %> @@ -47,8 +51,8 @@

<%= label_tag('', "#{l(:label_email_documents)}:") %> - Documents.zip - <%= hidden_field_tag('email[zipped_content]', @email_params['zipped_content']) %> + <%= 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)}:") %> @@ -57,3 +61,5 @@

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

<% end %> + +<%= wikitoolbar_for 'email_body' %> diff --git a/app/views/dmsf/show.html.erb b/app/views/dmsf/show.html.erb index d18d9a9c..965b6fc6 100644 --- a/app/views/dmsf/show.html.erb +++ b/app/views/dmsf/show.html.erb @@ -1,9 +1,9 @@ <%#= # 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-14 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,47 +22,73 @@ <% html_title(l(:dmsf)) %>
- <% if User.current.allowed_to?(:folder_manipulation, @project) %> - <% if @folder.nil? %> -   - <%= link_to('', {:action => 'edit_root', :id => @project}, - :title => l(:link_edit, :title => l(:link_documents)), :class => 'icon icon-edit') %> - <% elsif @locked_for_user %> -   - <%= link_to('', {:action => 'edit', :id => @project, :folder_id => @folder }, - :title => l(:link_edit, :title => h(@folder.title)), :class => 'icon icon-edit') %> + <% 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), + edit_dmsf_path(:id => @project, :folder_id => @folder), + :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? %> - <%= image_tag('locked.png', :plugin => :redmine_dmsf, :title => l(:title_folder_parent_locked, :name => @folder.folder.lock.reverse[0].folder.title)) unless @folder.nil?%> + <%= 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_function(image_tag('unlock.png', :plugin => 'redmine_dmsf'), - "manipulation_link('#{url_for(:action => 'unlock', :id => @project, :folder_id => @folder, :current => request.url)}')", - :title => l(:title_unlock_folder)) if @folder %> + <%= 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 %> <% else %> - <%= link_to_function(image_tag('lock.png', :plugin => 'redmine_dmsf'), - "manipulation_link('#{url_for(:action => 'lock', :id => @project, :folder_id => @folder, :current => request.url)}')", - :title => l(:title_lock_folder)) if @folder %> + <%= 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 %> <% end %> <% end %> - <% unless @folder %> -   - <% if @project.dmsf_notification %> - <%= link_to_function(image_tag('notify.png', :plugin => :redmine_dmsf), - "manipulation_link('#{url_for(:action => 'notify_deactivate', :id => @project)}')", - :title => l(:title_notifications_active_deactivate)) %> - <% else %> - <%= link_to_function(image_tag('notifynot.png', :plugin => :redmine_dmsf), - "manipulation_link('#{url_for(:action => 'notify_activate', :id => @project)}')", - :title => l(:title_notifications_not_active_activate)) %> - <% end %> - <% end %> -   - <%= link_to('', {:action => 'new', :id => @project, :parent_id => @folder }, - :title => l(:link_create_folder), :class => 'icon icon-add') unless @locked_for_user %> - <% 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 %> + <% 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), + 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), + new_dmsf_path(:id => @project, :parent_id => @folder), + :title => l(:link_create_folder), + :class => 'icon icon-add') unless @locked_for_user %> + <% end %>
<%= render(:partial => 'path', :locals => {:folder => @folder, :filename => nil}) %> @@ -81,10 +107,18 @@
<%= submit_tag(l(:submit_download), :title => l(:title_download_checked), :name => 'download_entries') %> <%= submit_tag(l(:submit_email), :title => l(:title_send_checked_by_email), :name => 'email_entries') %> - <% if User.current.allowed_to?(:file_manipulation, @project) && @folder && !@locked_for_user %> + <% if @file_manipulation_allowed && @folder_manipulation_allowed && !@locked_for_user %> - <% end %> + <% end %>
+ <% 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, + CustomValue.new(:custom_field_id => params[:custom_field_id].present? ? params[:custom_field_id] : values.first.custom_field_id, :value => params[:custom_value])) %> +
+ <% end %> @@ -100,250 +134,84 @@ + + - - <% @subfolders.each do |subfolder| %> - <% locked_for_user = subfolder.locked_for_user? %> - <% locked = subfolder.locked? %> + + <% @subfolders.each do |subfolder| %> - - - - - - - - - - + <%= render(:partial => 'dir', + :locals => { + :project => @project, + :subfolder => subfolder, + :link => nil, + :id => subfolder.id, + :name => 'subfolders[]', + :title => subfolder.title }) %> <% end %> - <% workflows_available = DmsfWorkflow.where(['project_id = ? OR project_id IS NULL', @project.id]).count > 0 %> + <% @dir_links.each do |link| %> + + <%= 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 %> - <% locked_for_user = file.locked_for_user? %> - <% locked = file.locked? %> - <% wf = DmsfWorkflow.find_by_id(file.last_revision.dmsf_workflow_id) %> + <% 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 }) %> <% end %> -
<%= check_box_tag('subfolders[]', subfolder.id, false, :title => l(:title_check_for_zip_download_or_email)) %> - <%= link_to(h(subfolder.title), - {:action => 'show', :id => @project, :folder_id => subfolder}, :class => 'icon icon-folder') %> -
[<%= subfolder.files.visible.count %>]
-
<%= format_time(subfolder.updated_at) %> - <% if locked_for_user %> - <% if subfolder.lock.reverse[0].user %> - <%= link_to(image_tag('locked.png', :plugin => :redmine_dmsf), - { :controller => 'users', :action => 'show', :id => subfolder.lock.reverse[0].user }, - :title => l(:title_locked_by_user, :user => subfolder.lock.reverse[0].user.to_s)) %> - <% else %> - <%= image_tag('locked.png', :title => l(:notice_account_unknown_email), :plugin => :redmine_dmsf) %> - <% end %> - <% elsif locked %> - <%= image_tag('lockedbycurrent.png', :title => l(:title_locked_by_you), :plugin => :redmine_dmsf) %> - <% end %> - <%= h(subfolder.user) %> - <% if @folder_manipulation_allowed %> -
- <% if subfolder.notification %> - <%= link_to_function(image_tag('notify.png', :plugin => :redmine_dmsf), - "manipulation_link('#{url_for(:action => 'notify_deactivate', :id => @project, :folder_id => subfolder)}')", - :title => l(:title_notifications_active_deactivate)) %> - <% else %> - <%= link_to_function(image_tag('notifynot.png', :plugin => :redmine_dmsf), - "manipulation_link('#{url_for(:action => 'notify_activate', :id => @project, :folder_id => subfolder)}')", - :title => l(:title_notifications_not_active_activate)) %> - <% end %> -
-
-
- <%= link_to(image_tag('edit.png', :class =>'detail_icon'), - {:action => 'edit', :id => @project, :folder_id => subfolder }, - :title => l(:link_edit, :title => h(subfolder.title))) unless locked_for_user %> -
-
- <% unless locked_for_user && !User.current.allowed_to?(:force_file_unlock, @project)%> - <% if locked %> - <% if subfolder.unlockable? %> - <%= link_to_function(image_tag('unlock.png', :plugin => :redmine_dmsf), - "manipulation_link('#{url_for(:controller => 'dmsf', :action => 'unlock', - :id => @project, :folder_id => subfolder)}')", - :title => l(:title_unlock_file))%> - <% else %> - <%= image_tag('locked.png', :plugin => :redmine_dmsf, - :title => l(:title_folder_parent_locked, :name => subfolder.lock.reverse[0].folder.title)) %> - <% end %> - <% else %> - <%= link_to_function(image_tag('lock.png', :plugin => :redmine_dmsf), - "manipulation_link('#{url_for(:controller => 'dmsf', :action => 'lock', - :id => @project, :folder_id => subfolder)}')", - :title => l(:title_lock_file)) %> - <% end %> -   - <% end %> - <%= link_to_function(image_tag('delete.png', :plugin => :redmine_dmsf), - "confirmation_link('#{url_for(:action => 'delete', :id => @project, :folder_id => @folder, :delete_folder_id => subfolder)}', '#{l(:question_do_you_really_want_to_delete_this_entry)}')", - :title => l(:title_delete)) unless locked_for_user %> -
-
-
- <% end %> -
<%= check_box_tag('files[]', file.id, false, :title => l(:title_check_for_zip_download_or_email)) %> - <% file_download_url = url_for({:only_path => false, :controller => :dmsf_files, :action => 'show', :id => file, :download => ''}) %> - <%= link_to(h(file.last_revision.display_title), - file_download_url, - :class => "icon icon-file #{DmsfHelper.filetype_css(file.name)}", - :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_download_url}") %> -
<%= h(file.display_name) %>
-
<%= 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 %> - <%= link_to(image_tag('locked.png', :plugin => :redmine_dmsf), - {:controller => 'users', :action => 'show', :id => file.lock.reverse[0].user }, - :title => l(:title_locked_by_user, :user => file.lock.reverse[0].user.to_s)) %> - <% else %> - <%= image_tag('locked.png', :title => l(:notice_account_unknown_email), :plugin => :redmine_dmsf) %> - <% end %> - <% elsif locked %> - <%= image_tag('lockedbycurrent.png', :title => l(:title_locked_by_you), :plugin => :redmine_dmsf) %> - <% end %> - <%= file.last_revision.version %> - <% if wf && @file_manipulation_allowed %> - <%= link_to( - file.last_revision.workflow_str(false), - log_dmsf_workflow_path( - :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) %> - <% else %> - <%= file.last_revision.workflow_str(false) %> - <% end %> - <%= h(file.last_revision.user) %> - <% if @file_manipulation_allowed %> -
- <% if file.notification %> - <%= link_to_function(image_tag('notify.png', :plugin => :redmine_dmsf), - "manipulation_link('#{url_for(:controller => 'dmsf_files', :action => 'notify_deactivate', :id => file)}')", - :title => l(:title_notifications_active_deactivate)) %> - <% else %> - <%= link_to_function(image_tag('notifynot.png', :plugin => :redmine_dmsf), - "manipulation_link('#{url_for(:controller => 'dmsf_files', :action => 'notify_activate', :id => file)}')", - :title => l(:title_notifications_not_active_activate)) %> - <% end %> - <% 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 %> - <%= image_tag('waiting_for_approval.png', :title => "#{l(:label_dmsf_wokflow_action_approve)} #{l(:label_dmsf_wokflow_action_reject)} #{l(:label_dmsf_wokflow_action_delegate)}", :plugin => :redmine_dmsf) %> - <% end %> - <% else %> - <%= image_tag('waiting_for_approval.png', :title => "#{l(:label_dmsf_wokflow_action_approve)} #{l(:label_dmsf_wokflow_action_reject)} #{l(:label_dmsf_wokflow_action_delegate)}", :plugin => :redmine_dmsf) %> - <% end %> - <% when DmsfWorkflow::STATE_APPROVED %> - <%= image_tag('approved.png', :title => l(:title_approved), :plugin => :redmine_dmsf) %> - <% when DmsfWorkflow::STATE_ASSIGNED %> - <% if User.current && (file.last_revision.dmsf_workflow_assigned_by == User.current.id) && wf %> - <%= link_to_function(image_tag('assigned.png', :plugin => :redmine_dmsf), - "manipulation_link('#{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 %> - <%= image_tag('assigned.png', :title => l(:label_dmsf_wokflow_action_start), :plugin => :redmine_dmsf) %> - <% end %> - <% when DmsfWorkflow::STATE_REJECTED %> - <%= image_tag('rejected.png', :title => l(:title_rejected), :plugin => :redmine_dmsf) %> - <% 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 %> -
-
-
- <%= link_to(image_tag('filedetails.png', :plugin => :redmine_dmsf, :class =>'detail_icon'), - {:controller => 'dmsf_files', :action => :show, :id => file }, - :title => l(:link_details, :title =>h(file.last_revision.title))) %> -
-
- <% if !locked_for_user || @force_file_unlock_allowed %> - <% if locked %> - <% if file.unlockable? %> - <%= link_to_function(image_tag('unlock.png', :plugin => :redmine_dmsf), - "manipulation_link('#{url_for(:controller => 'dmsf_files', :action => 'unlock', :id => file)}')", - :title => l(:title_unlock_file))%> - <% else %> - <%= image_tag('locked.png', :plugin => :redmine_dmsf, - :title => l(:title_file_parent_locked, :name => file.folder.lock.reverse[0].folder.title)) %> - <% end%> - <% else %> - <%= link_to_function(image_tag('lock.png', :plugin => :redmine_dmsf), - "manipulation_link('#{url_for(:controller => 'dmsf_files', :action => 'lock', :id => file)}')", - :title => l(:title_lock_file)) %> - <% end %> -   - <% end %> - <% unless locked_for_user %> - <%= link_to_function(image_tag('delete.png', :plugin => :redmine_dmsf), - "confirmation_link('#{url_for(:controller => 'dmsf_files', :action => 'delete', :id => file)}', '#{l(:question_do_you_really_want_to_delete_this_entry)}')", - :title => l(:title_delete)) %> - <% end %> -
-
-
- <% end %> -
-
+ <% end %> @@ -355,19 +223,16 @@ end %> -<% content_for :header_tags do %> - <%= stylesheet_link_tag 'jquery-ui/jquery-ui-1.9.2.css', :plugin => 'redmine_dmsf' %> +<% content_for :header_tags do %> <%= stylesheet_link_tag 'plupload/jquery.ui.plupload.css', :plugin => 'redmine_dmsf' %> <%= stylesheet_link_tag 'jquery.dataTables/jquery-ui.dataTables.css', :plugin => 'redmine_dmsf' %> - <%= stylesheet_link_tag 'dmsf', :plugin => 'redmine_dmsf' %> - <%= javascript_include_tag 'jquery-1.6.1.min.js', :plugin => 'redmine_dmsf' %> <%= javascript_include_tag 'jquery-ui-1.8.13.min.js', :plugin => 'redmine_dmsf' %> <%= javascript_include_tag 'jquery.dataTables/jquery.dataTables.min.js', :plugin => 'redmine_dmsf' %> - <% end %> -<%= render(:partial => 'multi_upload') if (@file_manipulation_allowed && !@locked_for_user) %> -
+<%= render(:partial => 'multi_upload') if (@file_manipulation_allowed && !@locked_for_user) %> \ 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 9398d29e..3db76d92 100644 --- a/app/views/dmsf_files/_file_new_revision.html.erb +++ b/app/views/dmsf_files/_file_new_revision.html.erb @@ -26,13 +26,13 @@ <% if @file.locked_for_user? %>

<%= l(:info_file_locked) %>

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

@@ -41,7 +41,7 @@ <%= f.select(:dmsf_folder_id, options_for_select(DmsfFolder.directory_tree(@project), :selected => (@revision.folder.id if @revision.folder))) %> / - <%= f.text_field(:name, :size => '22') %> + <%= f.text_field(:name, :size => 22) %>

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

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

@@ -69,14 +69,14 @@

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

- <%= file_field_tag('file_upload', :size => 30) %> -
- - (<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>) - -
-

+

+
+ <%= file_field_tag('file_upload') %> +
+ + (<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>) + +

@@ -87,12 +87,12 @@

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

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

+
+ <%= f.text_area(:comment, :rows=> 2, :style => 'width: 99%;') %> +
-
+
<%= submit_tag(l(:submit_create)) %> <% end %> <% end %> diff --git a/app/views/dmsf_files/_revision_access.html.erb b/app/views/dmsf_files/_revision_access.html.erb index b7629009..a13a736c 100644 --- a/app/views/dmsf_files/_revision_access.html.erb +++ b/app/views/dmsf_files/_revision_access.html.erb @@ -19,25 +19,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| %> - - - - + + + + - - - <% 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(DmsfHelper::to_time(access.first_at)) %><%= format_time(DmsfHelper::to_time(access.last_at)) %>
-

+ <% end %> + + \ 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 4b73d305..c58d28b5 100644 --- a/app/views/dmsf_files/show.html.erb +++ b/app/views/dmsf_files/show.html.erb @@ -1,9 +1,9 @@ <%#= # 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-14 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,40 +24,38 @@
<% if User.current.allowed_to?(:file_manipulation, @project) %> - <% unless @file.locked_for_user? && !User.current.allowed_to?(:force_file_unlock, @project)%> - <% if @file.locked? %> - <% unless @file.unlockable? %> - <%= image_tag('locked.png', :plugin => :redmine_dmsf, :title => l(:title_file_parent_locked, :name => @file.lock.reverse[0].folder.title)) %> - <% else %> - <%= link_to_function(image_tag('unlock.png', :plugin => 'redmine_dmsf'), - "manipulation_link('#{url_for(:action => 'unlock', :id => @file, :current => request.url)}')", - :title => l(:title_unlock_file)) %> - <% end %> - <% else %> - <%= link_to_function(image_tag('lock.png', :plugin => 'redmine_dmsf'), - "manipulation_link('#{url_for(:action => 'lock', :id => @file, :current => request.url)}')", - :title => l(:title_lock_file)) %> - <% end %> - <% end %> - <% unless @file.locked_for_user? %> -   - <%= link_to_function(image_tag('delete.png', :plugin => 'redmine_dmsf'), - "confirmation_link('#{url_for(:action => 'delete', :id => @file)}', '#{l(:question_do_you_really_want_to_delete_this_entry)}')", - :title => l(:title_delete)) %> - <% end %> -   - <% if @file.notification %> - <%= link_to_function(image_tag('notify.png', :plugin => 'redmine_dmsf'), - "manipulation_link('#{url_for(:action => 'notify_deactivate', :id => @file, :current => request.url)}')", - :title => l(:title_notifications_active_deactivate)) %> + <% 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 %> + <%= 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 %> + <%= 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') %> + <% 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(:button_copy)}/#{l(:button_move)}", copy_file_path(:id => @file), + :title => l(:title_copy), :class => 'icon icon-copy') %> + <%= delete_link @file %> <% else %> - <%= link_to_function(image_tag('notifynot.png', :plugin => 'redmine_dmsf'), - "manipulation_link('#{url_for(:action => 'notify_activate', :id => @file, :current => request.url)}')", - :title => l(:title_notifications_not_active_activate)) %> - <% end %> -   - <%= link_to(image_tag('copy.png'), {:controller => :dmsf_files_copy, :action => 'new', :id => @file }, :title => l(:title_copy_or_move)) %> - <% end %> + <% 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 %> + <% end %>
<%= render(:partial => '/dmsf/path', :locals => {:folder => @file.folder, :filename => @file.title}) %> @@ -65,22 +63,28 @@ <%= error_messages_for('file') %> <%= error_messages_for('revision') %> <%= render(:partial => 'file_new_revision') if User.current.allowed_to?(:file_manipulation, @file.project) %> -<%= form_tag('', :id => 'entries_form') %>

<%= l(:heading_revisions) %>

<% @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()", :title => 'Download entries')%> - <%= link_to(image_tag('rev_download.png', :plugin => 'redmine_dmsf'), - {:action => 'show', :id => @file, :download => revision}, - :title => l(:title_title_version_version_download, :title => h(revision.title), :version => revision.version)) %> +
+ <%= link_to_function( + '', + "$('#revision_access-#{revision.id}').toggle()", + :title => l(:title_download_entries), + :class => 'icon icon-dmsf-rev-downloads') %> + <%= link_to('', + dmsf_file_path(@file, :download => revision), + :title => l(:title_title_version_version_download, :title => h(revision.title), :version => revision.version), + :class => 'icon icon-dmsf-rev-download') %> <% if User.current.allowed_to?(:file_manipulation, @project) %> - <%= link_to_function(image_tag('rev_delete.png', :plugin => 'redmine_dmsf'), - "confirmation_link('#{url_for(:action => 'delete_revision', :id => revision)}', '#{l(:question_do_you_really_want_to_delete_this_revision)}')", - :title => l(:title_delete_revision)) %> + <%= link_to '', + delete_revision_path(revision), + :data => {:confirm => l(:text_are_you_sure)}, + :title => l(:title_delete_revision), + :class => 'icon icon-dmsf-rev-delete' %> <% end %>
<%= l(:info_revision, :rev => revision.id) %> @@ -141,13 +145,12 @@ <%= h(revision.comment) %>

-
" style="display:none"> - <%= render(:partial => 'revision_access', :locals => {:revision => revision}) if User.current.allowed_to?(:file_manipulation, @file.project) %> +
" 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 %>

@@ -158,34 +161,34 @@ %> <% if @revision.valid? && @file.valid? %> <% end %> -<% content_for :header_tags do %> - <%= stylesheet_link_tag 'jquery-ui/jquery-ui-1.9.2.css', :plugin => 'redmine_dmsf' %> +<% content_for :header_tags do %> <%= stylesheet_link_tag 'jquery.dataTables/jquery-ui.dataTables.css', :plugin => 'redmine_dmsf' %> - <%= stylesheet_link_tag 'dmsf', :plugin => 'redmine_dmsf' %> <%= javascript_include_tag 'jquery-1.6.1.min.js', :plugin => 'redmine_dmsf' %> <%= javascript_include_tag 'jquery-ui-1.8.13.min.js', :plugin => 'redmine_dmsf' %> <%= javascript_include_tag 'jquery.dataTables/jquery.dataTables.min.js', :plugin => 'redmine_dmsf' %> diff --git a/app/views/dmsf_files_copy/new.html.erb b/app/views/dmsf_files_copy/new.html.erb index ddaffe66..5729a1c0 100644 --- a/app/views/dmsf_files_copy/new.html.erb +++ b/app/views/dmsf_files_copy/new.html.erb @@ -28,12 +28,12 @@ <%= form_tag({:action => 'create', :id => @file}, :id => 'copyForm') do |f| %>

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

- <%= label_tag('target_folder_id', "#{l(:label_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))) %> @@ -55,12 +55,4 @@ jQuery('#target_project_id').change(function () { jQuery('#content').load("<%= url_for(:action => 'new') %>", jQuery('#copyForm').serialize()); }); - - -<% content_for :header_tags do %> - <%= stylesheet_link_tag 'dmsf', :plugin => 'redmine_dmsf' %> - <%= javascript_include_tag 'jquery-1.6.1.min.js', :plugin => 'redmine_dmsf' %> - -<% 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 1eff4ad5..b8b04143 100644 --- a/app/views/dmsf_folders_copy/new.html.erb +++ b/app/views/dmsf_folders_copy/new.html.erb @@ -27,12 +27,12 @@ <%= 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(:label_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))) %> @@ -46,12 +46,4 @@ jQuery('#target_project_id').change(function () { jQuery('#content').load("<%= url_for(:action => 'new') %>", jQuery('#copyForm').serialize()); }); - - -<% content_for :header_tags do %> - <%= stylesheet_link_tag 'dmsf', :plugin => 'redmine_dmsf' %> - <%= javascript_include_tag 'jquery-1.6.1.min.js', :plugin => 'redmine_dmsf' %> - -<% 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 new file mode 100644 index 00000000..4d33d59f --- /dev/null +++ b/app/views/dmsf_links/_form.html.erb @@ -0,0 +1,88 @@ +<%# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-14 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.%> + +<% html_title(l(:dmsf)) %> + +

+
+ +<% if @dmsf_file_id %> + <% file = DmsfFile.find_by_id @dmsf_file_id%> + <% title = file.title if file %> +<% end %> + +<%= render(:partial => '/dmsf/path', :locals => {:folder => @dmsf_link.folder, :filename => title}) %> + +<%= labelled_form_for @dmsf_link do |f| %> + <%= error_messages_for @dmsf_link %> + <%= f.hidden_field :project_id, :value => @dmsf_link.project_id %> + <%= 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' %> + <%= label_tag('dmsf_link[target_project_id]', l(:label_source_project), :class => 'required') %> + <% else %> + <%= label_tag('dmsf_link[target_project_id]', l(:label_target_project), :class => 'required') %> + <% end %> + <%= select_tag('dmsf_link[target_project_id]', + project_tree_options_for_select(DmsfFile.allowed_target_projects_on_copy, + :selected => @dmsf_link.target_project)) %> +

+

+ <% if @type == 'link_from' %> + <%= label_tag('dmsf_link[target_folder_id]', l(:label_source_folder)) %> + <% else %> + <%= label_tag('dmsf_link[target_folder_id]', l(:label_target_folder)) %> + <% end %> + <%= select_tag('dmsf_link[target_folder_id]', + folder_tree_options_for_select(DmsfFolder.directory_tree(@dmsf_link.target_project), + :selected => @target_folder_id)) %> +

+ <% if @type == 'link_from' %> +

+ <% if @target_folder_id %> + <% folder = DmsfFolder.find_by_id @target_folder_id %> + <% files = folder.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 %> +

+ <%= label_tag('dmsf_link[name]', l(:label_link_name), :class => 'required') %> + <%= text_field_tag 'dmsf_link[name]', @dmsf_link.name, :size => 40, :maxlength => 255 %> +

+
+ <%= f.submit l(:button_create) %> +<% end %> + + \ No newline at end of file diff --git a/lib/redmine_dmsf/vendored_dav4rack.rb b/app/views/dmsf_links/new.html.erb similarity index 73% rename from lib/redmine_dmsf/vendored_dav4rack.rb rename to app/views/dmsf_links/new.html.erb index 84fdb927..011e1b52 100644 --- a/lib/redmine_dmsf/vendored_dav4rack.rb +++ b/app/views/dmsf_links/new.html.erb @@ -1,6 +1,6 @@ -# Redmine plugin for Document Management System "Features" +<%# Redmine plugin for Document Management System "Features" # -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2014 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,8 +14,6 @@ # # 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.%> -vendor = File.expand_path('../vendor', __FILE__) -$:.unshift(vendor) unless $:.include?(vendor) -require 'dav4rack' +<%= 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 57043c2c..dc6734ab 100644 --- a/app/views/dmsf_mailer/files_deleted.html.erb +++ b/app/views/dmsf_mailer/files_deleted.html.erb @@ -1,7 +1,8 @@ <%# Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2014 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,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.%> -User <%= link_to(h(@user), {:only_path => false, :controller => 'users', :action => 'show', :id => @user }) %> -deleted DMSF files in project <%= @project.name %>: +<%= link_to User.current, user_path(User.current, :only_path => false) %> <%= l(:text_email_doc_deleted) %> +<%= link_to @project, project_path(@project, :only_path => false) %> <%= l(:text_email_doc_follows) %> <% @files.each do |file| %>

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

<% end %> diff --git a/app/views/dmsf_mailer/files_deleted.text.erb b/app/views/dmsf_mailer/files_deleted.text.erb index 50ee5322..ae78d992 100644 --- a/app/views/dmsf_mailer/files_deleted.text.erb +++ b/app/views/dmsf_mailer/files_deleted.text.erb @@ -1,7 +1,8 @@ <%# Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2014 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 +17,11 @@ # 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.%> - -User <%= @user %> deleted DMSF files in project <%= @project.name %>: -<% @files.each do |file| %> - <%= file.dmsf_path_str %> (<%= file.name %>) - <% if file.last_revision %> - , <%= number_to_human_size(file.last_revision.size) %>, version: <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %> - <% end %> + +<%= User.current.name %> <%= l(:text_email_doc_deleted) %> <%= @project.name %> <%= l(:text_email_doc_follows) %> +<% @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 %> + <% 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 ebe11c0a..8de4d931 100644 --- a/app/views/dmsf_mailer/files_updated.html.erb +++ b/app/views/dmsf_mailer/files_updated.html.erb @@ -1,7 +1,8 @@ <%# Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2014 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,20 +18,20 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.%> -User <%= link_to(h(@user), {:only_path => false, :controller => 'users', :action => 'show', :id => @user }) %> -actualized DMSF files in project <%= @project.name %>: +<%= link_to User.current, user_path(User.current, :only_path => false) %> <%= l(:text_email_doc_updated) %> +<%= link_to @project, project_path(@project, :only_path => false) %> <%= l(:text_email_doc_follows) %> <% @files.each do |file| %>

<%= link_to(h(file.dmsf_path_str), - {:only_path => false, :controller => 'dmsf_files', :action => 'show', :id => file, - :download => ''}) %> (<%= file.name %>), + dmsf_file_path(file, :download => '', :only_path => false)) %> + (<%= file.name %>), <%= number_to_human_size(file.last_revision.size) %>, - version: <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %>, - <%= "#{file.last_revision.workflow_str(true)}," if file.last_revision.workflow_str(true).present? %> - <%= link_to('Details', - {:only_path => false, :controller => 'dmsf_files', :action => 'show', :id => file}) %> + <%= l(:label_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_path(file, :only_path => false)) %> <% if file.last_revision.comment.present? %> -
    <%= h(file.last_revision.comment) %> +
    <%= h(file.last_revision.comment) %> <% end %>

<% end %> diff --git a/app/views/dmsf_mailer/files_updated.text.erb b/app/views/dmsf_mailer/files_updated.text.erb index e05db87c..147a1922 100644 --- a/app/views/dmsf_mailer/files_updated.text.erb +++ b/app/views/dmsf_mailer/files_updated.text.erb @@ -1,7 +1,8 @@ <%# Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2014 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,10 +17,16 @@ # 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.%> - -User <%= @user %> actualized DMSF files in project <%= @project.name %>: + +<%= User.current.name %> <%= l(:text_email_doc_updated) %> +<%= @project.name %> <%= l(:text_email_doc_follows) %> <% @files.each do |file| %> - <%= file.dmsf_path_str %> (<%= file.name %>), <%= number_to_human_size(file.last_revision.size) %>, version: <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %><%= ", #{file.last_revision.workflow_str(true)}" unless file.last_revision.workflow_str(true).blank? %> - <%= url_for({:only_path => false, :controller => 'dmsf_files', :action => 'show', :id => file}) %> - <% if file.last_revision.comment.present? %> comment: <%= file.last_revision.comment %><% end %> + <%= 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 %>, + <%= "#{file.last_revision.workflow_str(true)}," if file.last_revision.workflow_str(true) != l(:title_none) %> + <%= dmsf_file_path(file, :only_path => false) %> + <% if file.last_revision.comment.present? %> + <%= l(:label_comment) %> <%= h(file.last_revision.comment) %> + <% end %> <% end %> \ No newline at end of file diff --git a/app/views/dmsf_mailer/send_documents.html.erb b/app/views/dmsf_mailer/send_documents.html.erb index a9e13b42..6e0248d2 100644 --- a/app/views/dmsf_mailer/send_documents.html.erb +++ b/app/views/dmsf_mailer/send_documents.html.erb @@ -1,7 +1,8 @@ <%# Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 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,4 +18,38 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.%> -<%= simple_format(@body) %> +<%= textilizable(@body) %> + +<% if @links_only == '1' %> + <% if @folders.present? %> + <% JSON.parse(@folders).each do |id| %> + <% folder = DmsfFolder.find_by_id id %> + <% if folder %> + <% folder.folder_tree.each do |name, i| %> + <% dir = DmsfFolder.find_by_id i %> + <% if 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_path(file, :only_path => false)) %> +  (<%= link_to(h(file.name), dmsf_file_path(file, :download => '', :only_path => false)) %>) +
+ <% end %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> + <% if @files.present? %> +
+ <% JSON.parse(@files).each do |id| %> + <% file = DmsfFile.find_by_id id %> + <% if file %> + <%= link_to(h(file.title), dmsf_file_path(file, :only_path => false)) %> +  (<%= link_to(h(file.name), dmsf_file_path(file, :download => '', :only_path => false)) %>) +
+ <% end %> + <% end %> + <% end %> +<% 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 91754792..10abf892 100644 --- a/app/views/dmsf_mailer/send_documents.text.erb +++ b/app/views/dmsf_mailer/send_documents.text.erb @@ -1,7 +1,8 @@ <%# Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 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,3 +19,30 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.%> <%= @body %> + +<% if @links_only == '1' %> + <% if @folders.present? %> + <% JSON.parse(@folders).each do |id| %> + <% folder = DmsfFolder.find_by_id id %> + <% if 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_path(file, :download => '', :only_path => false) %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> + <% if @files.present? %> + <% JSON.parse(@files).each do |id| %> + <% file = DmsfFile.find_by_id id %> + <% if file %> + <%= dmsf_file_path(file, :download => '', :only_path => false) %> + <% end %> + <% end %> + <% end %> +<% end %> \ No newline at end of file diff --git a/app/views/dmsf_mailer/workflow_notification.html.erb b/app/views/dmsf_mailer/workflow_notification.html.erb index fd759524..95371fc5 100644 --- a/app/views/dmsf_mailer/workflow_notification.html.erb +++ b/app/views/dmsf_mailer/workflow_notification.html.erb @@ -23,9 +23,11 @@

<%= @text2 %> <% unless @revision.folder %> - <%= link_to l(:link_documents), {:controller => 'dmsf', :action => 'show', :id=> @revision.file.project, :only_path => false } %> + <%= link_to l(:link_documents), + dmsf_folder_path(:id => @revision.file.project, :only_path => false) %> <% else %> - <%= link_to @revision.folder.title, {:controller => 'dmsf', :action => 'show', :id=> @revision.file.project, :folder_id => @revision.folder, :only_path => false } %> + <%= link_to @revision.folder.title, + dmsf_folder_path(:id => @revision.file.project, :folder_id => @revision.folder, :only_path => false) %> <% 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 17fa7989..fe103196 100644 --- a/app/views/dmsf_mailer/workflow_notification.text.erb +++ b/app/views/dmsf_mailer/workflow_notification.text.erb @@ -16,10 +16,10 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.%> -<%= @user.name %> , +<%= @user.name %>, <%= @text1 %> <% unless @revision.folder %> - <%= @text2 %> <%= url_for(:controller => 'dmsf', :action => 'show', :id => @revision.file.project, :only_path => false) %>. + <%= @text2 %> <%= dmsf_folder_path(:id => @revision.file.project, :only_path => false) %>. <% else %> - <%= @text2 %> <%= url_for(:controller => 'dmsf', :action => 'show', :id => @revision.file.project, :folder_id => @revision.folder, :only_path => false) %>. + <%= @text2 %> <%= dmsf_folder_path(:id => @revision.file.project, :folder_id => @revision.folder, :only_path => false) %>. <% end %> diff --git a/app/views/dmsf_state/_user_pref.html.erb b/app/views/dmsf_state/_user_pref.html.erb index d230b30d..669c5037 100644 --- a/app/views/dmsf_state/_user_pref.html.erb +++ b/app/views/dmsf_state/_user_pref.html.erb @@ -32,8 +32,4 @@ <%= submit_tag(l(:submit_save), :title => l(:title_save_preferences)) %>
<% end %> -
- -<% content_for :header_tags do %> - <%= stylesheet_link_tag 'dmsf', :plugin => 'redmine_dmsf' %> -<% end %> \ No newline at end of file +
\ No newline at end of file diff --git a/app/views/dmsf_upload/_upload_file.html.erb b/app/views/dmsf_upload/_upload_file.html.erb index 8176802b..a552bb44 100644 --- a/app/views/dmsf_upload/_upload_file.html.erb +++ b/app/views/dmsf_upload/_upload_file.html.erb @@ -1,9 +1,9 @@ <%#= # 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-14 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,7 +25,7 @@

<%= label_tag("commited_files[#{i}][title]", "#{l(:label_title)}:") %> - <%= text_field_tag("commited_files[#{i}][title]", upload.title, :size => '32') %> + <%= text_field_tag("commited_files[#{i}][title]", upload.title, :size => 32) %>

@@ -40,7 +40,7 @@ <%= label_tag("commited_files[#{i}][description]", "#{l(:label_description)}:") %>

- <%= text_area_tag("commited_files[#{i}][description]", upload.description, :rows=> '6', :class => 'wiki-edit') %> + <%= text_area_tag("commited_files[#{i}][description]", upload.description, :rows=> 6, :class => 'wiki-edit') %>

@@ -69,10 +69,10 @@ <% end %>

<%= label_tag("commited_files[#{i}][comment]", "#{l(:label_comment)}:") %> -

- <%= text_area_tag("commited_files[#{i}][comment]", upload.comment, :rows=> '2', :style => 'width: 99%;') %> -

+
+ <%= text_area_tag("commited_files[#{i}][comment]", upload.comment, :rows => 2, :style => 'width: 99%;') %> +
<%= wikitoolbar_for "commited_files_#{i}_description" %> diff --git a/app/views/dmsf_upload/upload_file.html.erb b/app/views/dmsf_upload/upload_file.html.erb index e239ebea..a538310c 100644 --- a/app/views/dmsf_upload/upload_file.html.erb +++ b/app/views/dmsf_upload/upload_file.html.erb @@ -2,6 +2,7 @@ # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn +# Copyright (C) 2014 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,5 +18,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.%> -{"original_filename":"<%= h(@tempfile.original_filename) %>", "content_type":"<%= h(@tempfile.content_type) %>", -"disk_filename":"<%= h(@disk_filename) %>"} \ No newline at end of file +{"original_filename":"<%= (@tempfile.original_filename).html_safe %>", + "content_type":"<%= (@tempfile.content_type).html_safe %>", + "disk_filename":"<%= (@disk_filename).html_safe %>"} \ No newline at end of file diff --git a/app/views/dmsf_upload/upload_files.html.erb b/app/views/dmsf_upload/upload_files.html.erb index 9513c916..ff2637d8 100644 --- a/app/views/dmsf_upload/upload_files.html.erb +++ b/app/views/dmsf_upload/upload_files.html.erb @@ -41,14 +41,4 @@ <% i += 1 %> <% end %> <%= submit_tag(l(:submit_commit)) %> -<% end %> - -<% content_for :header_tags do %> - <%= stylesheet_link_tag 'dmsf', :plugin => 'redmine_dmsf' %> - <%= javascript_include_tag 'jquery-1.6.1.min.js', :plugin => 'redmine_dmsf' %> - -<% end %> +<% end %> \ No newline at end of file diff --git a/app/views/dmsf_workflows/_action.html.erb b/app/views/dmsf_workflows/_action.html.erb index 7b8ef077..3774cd58 100644 --- a/app/views/dmsf_workflows/_action.html.erb +++ b/app/views/dmsf_workflows/_action.html.erb @@ -34,8 +34,8 @@

<%= label_tag 'delegate', l(:label_dmsf_wokflow_action_delegate) %>
<%= text_field_tag 'user_search', nil %> - <%= javascript_tag "observeSearchfield('user_search', 'users_for_delegate', '#{ escape_javascript autocomplete_for_user_dmsf_workflow_path(@workflow, :dmsf_workflow_step_assignment_id => params[:dmsf_workflow_step_assignment_id], :dmsf_file_revision_id => params[:dmsf_file_revision_id]) }')" %> - <%= content_tag('div', principals_radio_button_tags('step_action', @workflow.delegates(nil, params[:dmsf_workflow_step_assignment_id], params[:dmsf_file_revision_id])), :id => 'users_for_delegate') %> + <%= javascript_tag "observeSearchfield('user_search', 'users_for_delegate', '#{ escape_javascript autocomplete_for_user_dmsf_workflow_path(@dmsf_workflow, :dmsf_workflow_step_assignment_id => params[:dmsf_workflow_step_assignment_id], :dmsf_file_revision_id => params[:dmsf_file_revision_id]) }')" %> + <%= content_tag('div', principals_radio_button_tags('step_action', @dmsf_workflow.delegates(nil, params[:dmsf_workflow_step_assignment_id], params[:dmsf_file_revision_id])), :id => 'users_for_delegate') %>

diff --git a/app/views/dmsf_workflows/_log.html.erb b/app/views/dmsf_workflows/_log.html.erb index aff97ed5..83c2ac5f 100644 --- a/app/views/dmsf_workflows/_log.html.erb +++ b/app/views/dmsf_workflows/_log.html.erb @@ -24,10 +24,10 @@

<%= label_tag 'workflow_name', "#{l(:link_workflow)} #{l(:field_name).downcase}: " %> - <% if User.current.allowed_to?(:manage_workflows, @workflow.project) %> - <%= link_to @workflow.name, edit_dmsf_workflow_path(@workflow) %> + <% if User.current.allowed_to?(:manage_workflows, @dmsf_workflow.project) %> + <%= link_to @dmsf_workflow.name, edit_dmsf_workflow_path(@dmsf_workflow) %> <% else %> - <%= @workflow.name %> + <%= @dmsf_workflow.name %> <% end %>
@@ -78,7 +78,7 @@ <%= link_to_user User.find_by_id(row['author_id'].present? ? row['author_id'] : row['user_id']) %> <%= DmsfWorkflowStepAction.action_str(row['action']) %> - <% if((row['step'].to_i == @workflow.dmsf_workflow_steps.last.step) && (revision.workflow == DmsfWorkflow::STATE_APPROVED) && (row['action'] != DmsfWorkflowStepAction::ACTION_DELEGATE)) %> + <% if((row['step'].to_i == @dmsf_workflow.dmsf_workflow_steps.last.step) && (revision.workflow == DmsfWorkflow::STATE_APPROVED) && (row['action'] != DmsfWorkflowStepAction::ACTION_DELEGATE)) %> <%= l(:title_approved) %> <% else %> <%= DmsfWorkflowStepAction.workflow_str(row['action']) %> diff --git a/app/views/dmsf_workflows/_main.html.erb b/app/views/dmsf_workflows/_main.html.erb index 835d52bd..acf4f166 100644 --- a/app/views/dmsf_workflows/_main.html.erb +++ b/app/views/dmsf_workflows/_main.html.erb @@ -36,7 +36,7 @@ <% for workflow in @workflows %> - <%= link_to(h(workflow.name), edit_dmsf_workflow_path(workflow)) %> + <%= link_to(h(workflow.name), dmsf_workflow_path(workflow)) %> <%= delete_link dmsf_workflow_path(workflow) %> diff --git a/app/views/dmsf_workflows/_steps.html.erb b/app/views/dmsf_workflows/_steps.html.erb index f4bbe8f1..60848a8e 100644 --- a/app/views/dmsf_workflows/_steps.html.erb +++ b/app/views/dmsf_workflows/_steps.html.erb @@ -16,23 +16,23 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.%> -<% if @workflow.project %> -

<%= link_to l(:label_dmsf_workflow_plural), settings_project_path(@project, :tab => 'dmsf_workflow') %> » <%=h @workflow %>

+<% if @dmsf_workflow.project %> +

<%= link_to l(:label_dmsf_workflow_plural), settings_project_path(@project, :tab => 'dmsf_workflow') %> » <%=h @dmsf_workflow %>

<% else %> -

<%= link_to l(:label_dmsf_workflow_plural), dmsf_workflows_path %> » <%=h @workflow %>

+

<%= link_to l(:label_dmsf_workflow_plural), dmsf_workflows_path %> » <%=h @dmsf_workflow %>

<% end %> -<%= labelled_form_for @workflow do |f| %> +<%= labelled_form_for @dmsf_workflow do |f| %> <%= error_messages_for 'workflow' %>
-

<%= f.label :label_dmsf_workflow_name %><%= text_field_tag :name, @workflow.name %><%= submit_tag l(:button_save) %>

+

<%= f.label :label_dmsf_workflow_name %><%= text_field_tag :name, @dmsf_workflow.name %><%= submit_tag l(:button_save) %>

<% end %>
-<% steps = @workflow.dmsf_workflow_steps.collect{|s| s.step}.uniq %> +<% steps = @dmsf_workflow.dmsf_workflow_steps.collect{|s| s.step}.uniq %> <% if steps.any? %> @@ -46,7 +46,7 @@ <% end; reset_cycle %> @@ -69,15 +69,15 @@
- <%= form_for(@workflow, :url => edit_dmsf_workflow_path(@workflow), + <%= form_for(@dmsf_workflow, :url => edit_dmsf_workflow_path(@dmsf_workflow), :html => {:method => :post}) do |f| %>
<%=l(:label_user_new)%>

<%= label_tag 'user_search', l(:label_user_search) %><%= text_field_tag 'user_search', nil %>

- <%= javascript_tag "observeSearchfield('user_search', 'users', '#{ escape_javascript autocomplete_for_user_dmsf_workflow_path(@workflow, :dmsf_workflow_step_assignment_id => nil, :dmsf_file_revision_id => nil, :project_id => @project ? @project.id : nil) }')" %> + <%= javascript_tag "observeSearchfield('user_search', 'users', '#{ escape_javascript autocomplete_for_user_dmsf_workflow_path(@dmsf_workflow, :dmsf_workflow_step_assignment_id => nil, :dmsf_file_revision_id => nil, :project_id => @project ? @project.id : nil) }')" %>
- <%= render_principals_for_new_dmsf_workflow_users(@workflow, nil, nil) %> + <%= render_principals_for_new_dmsf_workflow_users(@dmsf_workflow, nil, nil) %>

diff --git a/app/views/dmsf_workflows/action.js.erb b/app/views/dmsf_workflows/action.js.erb index be5c6be5..1338d7ac 100644 --- a/app/views/dmsf_workflows/action.js.erb +++ b/app/views/dmsf_workflows/action.js.erb @@ -16,6 +16,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.%> -$('#ajax-modal').html('<%= escape_javascript(render :partial => 'action', :locals => {:workflow => @workflow}) %>'); +$('#ajax-modal').html('<%= escape_javascript(render :partial => 'action', :locals => {:workflow => @dmsf_workflow}) %>'); showModal('ajax-modal', '400px'); $('#ajax-modal').addClass('new-action'); \ No newline at end of file diff --git a/app/views/dmsf_workflows/assign.js.erb b/app/views/dmsf_workflows/assign.js.erb index a9a04b36..89693237 100644 --- a/app/views/dmsf_workflows/assign.js.erb +++ b/app/views/dmsf_workflows/assign.js.erb @@ -16,6 +16,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.%> -$('#ajax-modal').html('<%= escape_javascript(render :partial => 'assign', :locals => {:workflow => @workflow}) %>'); +$('#ajax-modal').html('<%= escape_javascript(render :partial => 'assign', :locals => {:workflow => @dmsf_workflow}) %>'); showModal('ajax-modal', '400px'); $('#ajax-modal').addClass('assignment'); \ No newline at end of file diff --git a/app/views/dmsf_workflows/autocomplete_for_user.html.erb b/app/views/dmsf_workflows/autocomplete_for_user.html.erb index ac1c44f0..be8dec18 100644 --- a/app/views/dmsf_workflows/autocomplete_for_user.html.erb +++ b/app/views/dmsf_workflows/autocomplete_for_user.html.erb @@ -16,4 +16,4 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.%> -<%= render_principals_for_new_dmsf_workflow_users(@workflow, params[:dmsf_workflow_step_assignment_id], params[:dmsf_file_revision_id]) %> +<%= render_principals_for_new_dmsf_workflow_users(@dmsf_workflow, params[:dmsf_workflow_step_assignment_id], params[:dmsf_file_revision_id]) %> diff --git a/app/views/dmsf_workflows/log.js.erb b/app/views/dmsf_workflows/log.js.erb index 2fb6f7c3..9131f960 100644 --- a/app/views/dmsf_workflows/log.js.erb +++ b/app/views/dmsf_workflows/log.js.erb @@ -16,6 +16,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.%> -$('#ajax-modal').html('<%= escape_javascript(render :partial => 'log', :locals => {:workflow => @workflow}) %>'); +$('#ajax-modal').html('<%= escape_javascript(render :partial => 'log', :locals => {:workflow => @dmsf_workflow}) %>'); showModal('ajax-modal', '800px'); $('#ajax-modal').addClass('workflow-log'); \ No newline at end of file diff --git a/app/views/dmsf_workflows/new.html.erb b/app/views/dmsf_workflows/new.html.erb index a5445122..c7b59a9d 100644 --- a/app/views/dmsf_workflows/new.html.erb +++ b/app/views/dmsf_workflows/new.html.erb @@ -23,8 +23,8 @@

<%= link_to l(:label_dmsf_workflow_plural), dmsf_workflows_path %> » <%=l(:label_dmsf_workflow_new)%>

<% end %> -<%= labelled_form_for @workflow do |f| %> - <%= error_messages_for 'workflow' %> +<%= labelled_form_for @dmsf_workflow do |f| %> + <%= error_messages_for 'dmsf_workflow' %>

<%= f.label :label_dmsf_workflow_name %><%= text_field_tag :name %>

<% if project %> diff --git a/app/views/dmsf_workflows/edit.html.erb b/app/views/dmsf_workflows/show.html.erb similarity index 100% rename from app/views/dmsf_workflows/edit.html.erb rename to app/views/dmsf_workflows/show.html.erb diff --git a/app/views/my/blocks/_locked_documents.html.erb b/app/views/my/blocks/_locked_documents.html.erb index d9448220..3615e774 100644 --- a/app/views/my/blocks/_locked_documents.html.erb +++ b/app/views/my/blocks/_locked_documents.html.erb @@ -27,7 +27,7 @@

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

<% if folders.any? || files.any?%> <%= form_tag({}) do %> -
<%= i %> - <% @workflow.dmsf_workflow_steps.collect{|s| (s.step == i) ? s : nil}.compact.each_with_index do |step, j| %> + <% @dmsf_workflow.dmsf_workflow_steps.collect{|s| (s.step == i) ? s : nil}.compact.each_with_index do |step, j| %> <% if j != 0 %> <%= step.soperator %>  <% end %> @@ -54,10 +54,10 @@ <% end %> - <%= reorder_links('workflow_step', {:action => 'edit', :id => @workflow, :step => i}, :put) %> + <%= reorder_links('workflow_step', {:action => 'edit', :id => @dmsf_workflow, :step => i}, :put) %> - <%= delete_link edit_dmsf_workflow_path(@workflow, :step => i) %> + <%= delete_link edit_dmsf_workflow_path(@dmsf_workflow, :step => i) %>
+
@@ -62,7 +62,7 @@ <%= link_to_project(file.project) %> diff --git a/app/views/my/blocks/_open_approvals.html.erb b/app/views/my/blocks/_open_approvals.html.erb index 8b675d62..bc574d05 100644 --- a/app/views/my/blocks/_open_approvals.html.erb +++ b/app/views/my/blocks/_open_approvals.html.erb @@ -30,7 +30,7 @@

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

<% if assignments.any? %> <%= form_tag({}) do %> -
<%=l(:field_project)%> - <%= link_to(h(file.last_revision.display_title), + <%= link_to(h(file.title), {:controller => 'dmsf_files', :action => :show, :id => file }, :class => "icon icon-file #{DmsfHelper.filetype_css(file.name)}") %>
+
@@ -55,7 +55,7 @@ diff --git a/app/views/settings/_dmsf_settings.html.erb b/app/views/settings/_dmsf_settings.html.erb index 3b22f537..1e784699 100644 --- a/app/views/settings/_dmsf_settings.html.erb +++ b/app/views/settings/_dmsf_settings.html.erb @@ -1,7 +1,8 @@ <%# Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 Karel Picman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -93,6 +94,18 @@ (<%= l(:label_default) %>: <%= l(:select_option_deactivated) %>)

+

+ <%= content_tag(:label, "#{l(:label_display_notified_recipients)}:") %> + <%= select_tag('settings[dmsf_display_notified_recipients]', + options_for_select([ + [l(:select_option_deactivated), nil], + [l(:select_option_activated), '1']], + :selected => @settings['dmsf_display_notified_recipients'])) %>
+ (<%= l(:label_default) %>: <%= l(:select_option_deactivated) %>) +
+ <%= l(:note_display_notified_recipients).html_safe %> +

+

diff --git a/assets/images/delete.png b/assets/images/delete.png deleted file mode 100644 index ec01ecd3..00000000 Binary files a/assets/images/delete.png and /dev/null differ diff --git a/assets/images/filetypes/c_gray.png b/assets/images/filetypes/c_gray.png new file mode 100644 index 00000000..6edaf869 Binary files /dev/null and b/assets/images/filetypes/c_gray.png differ diff --git a/assets/images/filetypes/csharp_gray.png b/assets/images/filetypes/csharp_gray.png new file mode 100644 index 00000000..6c27ebc1 Binary files /dev/null and b/assets/images/filetypes/csharp_gray.png differ diff --git a/assets/images/filetypes/css_gray.png b/assets/images/filetypes/css_gray.png new file mode 100644 index 00000000..ff68b5f6 Binary files /dev/null and b/assets/images/filetypes/css_gray.png differ diff --git a/assets/images/filetypes/doc_gray.png b/assets/images/filetypes/doc_gray.png new file mode 100644 index 00000000..9687dad5 Binary files /dev/null and b/assets/images/filetypes/doc_gray.png differ diff --git a/assets/images/filetypes/html_gray.png b/assets/images/filetypes/html_gray.png new file mode 100644 index 00000000..9c161f2d Binary files /dev/null and b/assets/images/filetypes/html_gray.png differ diff --git a/assets/images/filetypes/image_gray.png b/assets/images/filetypes/image_gray.png new file mode 100644 index 00000000..2c6d6edb Binary files /dev/null and b/assets/images/filetypes/image_gray.png differ diff --git a/assets/images/filetypes/java_gray.png b/assets/images/filetypes/java_gray.png new file mode 100644 index 00000000..e8ae43c3 Binary files /dev/null and b/assets/images/filetypes/java_gray.png differ diff --git a/assets/images/filetypes/js_gray.png b/assets/images/filetypes/js_gray.png new file mode 100644 index 00000000..224de299 Binary files /dev/null and b/assets/images/filetypes/js_gray.png differ diff --git a/assets/images/filetypes/mpp_gray.png b/assets/images/filetypes/mpp_gray.png new file mode 100644 index 00000000..2095c3ec Binary files /dev/null and b/assets/images/filetypes/mpp_gray.png differ diff --git a/assets/images/filetypes/odg_gray.png b/assets/images/filetypes/odg_gray.png new file mode 100644 index 00000000..59f96c73 Binary files /dev/null and b/assets/images/filetypes/odg_gray.png differ diff --git a/assets/images/filetypes/odp_gray.png b/assets/images/filetypes/odp_gray.png new file mode 100644 index 00000000..da112664 Binary files /dev/null and b/assets/images/filetypes/odp_gray.png differ diff --git a/assets/images/filetypes/ods_gray.png b/assets/images/filetypes/ods_gray.png new file mode 100644 index 00000000..3fb218bc Binary files /dev/null and b/assets/images/filetypes/ods_gray.png differ diff --git a/assets/images/filetypes/odt_gray.png b/assets/images/filetypes/odt_gray.png new file mode 100644 index 00000000..b305e6fe Binary files /dev/null and b/assets/images/filetypes/odt_gray.png differ diff --git a/assets/images/filetypes/pdf_gray.png b/assets/images/filetypes/pdf_gray.png new file mode 100644 index 00000000..7ee708a6 Binary files /dev/null and b/assets/images/filetypes/pdf_gray.png differ diff --git a/assets/images/filetypes/php_gray.png b/assets/images/filetypes/php_gray.png new file mode 100644 index 00000000..6ef83388 Binary files /dev/null and b/assets/images/filetypes/php_gray.png differ diff --git a/assets/images/filetypes/ppt_gray.png b/assets/images/filetypes/ppt_gray.png new file mode 100644 index 00000000..929bf23a Binary files /dev/null and b/assets/images/filetypes/ppt_gray.png differ diff --git a/assets/images/filetypes/ruby_gray.png b/assets/images/filetypes/ruby_gray.png new file mode 100644 index 00000000..5d33d83b Binary files /dev/null and b/assets/images/filetypes/ruby_gray.png differ diff --git a/assets/images/filetypes/vsd_gray.png b/assets/images/filetypes/vsd_gray.png new file mode 100644 index 00000000..4a70fb3a Binary files /dev/null and b/assets/images/filetypes/vsd_gray.png differ diff --git a/assets/images/filetypes/xls_gray.png b/assets/images/filetypes/xls_gray.png new file mode 100644 index 00000000..3337cadf Binary files /dev/null and b/assets/images/filetypes/xls_gray.png differ diff --git a/assets/images/filetypes/xml_gray.png b/assets/images/filetypes/xml_gray.png new file mode 100644 index 00000000..a6806510 Binary files /dev/null and b/assets/images/filetypes/xml_gray.png differ diff --git a/assets/images/filetypes/zip_gray.png b/assets/images/filetypes/zip_gray.png new file mode 100644 index 00000000..8f4a9155 Binary files /dev/null and b/assets/images/filetypes/zip_gray.png differ diff --git a/assets/images/folder_gray.png b/assets/images/folder_gray.png new file mode 100644 index 00000000..b6d9df7a Binary files /dev/null and b/assets/images/folder_gray.png differ diff --git a/assets/images/link.png b/assets/images/link.png new file mode 100644 index 00000000..b46dfd5e Binary files /dev/null and b/assets/images/link.png differ diff --git a/assets/images/locked_gray.png b/assets/images/locked_gray.png new file mode 100644 index 00000000..aa8b2c09 Binary files /dev/null and b/assets/images/locked_gray.png differ diff --git a/assets/images/lockedbycurrent_gray.png b/assets/images/lockedbycurrent_gray.png new file mode 100644 index 00000000..6c4ea721 Binary files /dev/null and b/assets/images/lockedbycurrent_gray.png differ diff --git a/assets/images/unlockdisabled.png b/assets/images/unlockdisabled.png deleted file mode 100644 index faba7693..00000000 Binary files a/assets/images/unlockdisabled.png and /dev/null differ diff --git a/assets/javascripts/redmine_dmsf.js b/assets/javascripts/redmine_dmsf.js deleted file mode 100644 index 020f67a6..00000000 --- a/assets/javascripts/redmine_dmsf.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -* Redmine plugin for Document Management System "Features" -* -* Copyright (C) 2013 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 manipulation_link(action) { - jQuery('#entries_form').attr('action', action); - jQuery('#entries_form').submit(); -}; - -function confirmation_link(action, question) { - if(!window.confirm(question)) { - return; - } - jQuery('#entries_form').attr('action', action); - jQuery('#entries_form').submit(); -}; \ No newline at end of file diff --git a/assets/stylesheets/dmsf.css b/assets/stylesheets/dmsf.css index 03c8b476..d37049b1 100644 --- a/assets/stylesheets/dmsf.css +++ b/assets/stylesheets/dmsf.css @@ -34,13 +34,13 @@ table.entries { } table.entries td.modified { - min-width: 104px; - width: 104px; + min-width: 127px; + width: 127px; } table.entries td.actions { - min-width: 116px; - width: 116px; + min-width: 96px; + width: 96px; } table.entries td.title { @@ -71,42 +71,6 @@ table.display tbody tr.odd { table.display tbody tr:hover { background-color:#ffffdd; } -.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); -} - form.dmfs_entries { margin-bottom: 10px; display: block; @@ -167,10 +131,7 @@ div.right_icon_box { float: right; white-space: nowrap; padding: 0 5px 0 5px; -} - -div.right_icon_box img.detail_icon { - padding-top: 2px; + width: 96px; } /* DMSF entries upload control */ @@ -312,3 +273,92 @@ table.list td.step { .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 */ +.icon-margin-left { margin-left: 3px; } +.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 } +.icon-dmsf-lockedbycurrent { background-image: url(../images/lockedbycurrent.png); margin-left: 2px } +.icon-dmsf-waiting-for-approval { background-image: url(../images/waiting_for_approval.png); } +.icon-dmsf-approved { background-image: url(../images/approved.png); } +.icon-dmsf-assigned { background-image: url(../images/assigned.png); } +.icon-dmsf-rejected { background-image: url(../images/rejected.png); } +.icon-dmsf-none { background-image: url(../images/none.png); } +.icon-dmsf-file-details { background-image: url(../images/filedetails.png); } +.icon-dmsf-rev-download { background-image: url(../images/rev_download.png); } +.icon-dmsf-rev-downloads { background-image: url(../images/rev_downloads.png); } +.icon-dmsf-rev-delete { background-image: url(../images/rev_delete.png); } + +.icon-edit-gray { background-image: url(../images/edit_gray.png); } +.icon-notification-on-gray { background-image: url(../images/notify_gray.png); } +.icon-notification-off-gray { background-image: url(../images/notifynot_gray.png); } +.icon-dmsf-lock-gray { background-image: url(../images/lock_gray.png); } +.icon-dmsf-unlock-gray { background-image: url(../images/unlock_gray.png); } +.icon-dmsf-waiting-for-approval-gray { background-image: url(../images/waiting_for_approval_gray.png); } +.icon-dmsf-approved-gray { background-image: url(../images/approved_gray.png); } +.icon-dmsf-assigned-gray { background-image: url(../images/assigned_gray.png); } +.icon-dmsf-rejected-gray { background-image: url(../images/rejected_gray.png); } +.icon-dmsf-file-details-gray { background-image: url(../images/filedetails_gray.png); } + +tr.gray .icon-dmsf-locked { background-image: url(../images/locked_gray.png); margin-left: 2px } +tr.gray .icon-dmsf-lockedbycurrent { background-image: url(../images/lockedbycurrent_gray.png); margin-left: 2px } + +tr.gray .icon-dmsf-locked { background-image: url(../images/locked_gray.png); margin-left: 2px } +tr.gray .icon-dmsf-lockedbycurrent { background-image: url(../images/lockedbycurrent_gray.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_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; +} \ No newline at end of file diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_diagonals-thick_18_b81900_40x40.png b/assets/stylesheets/jquery-ui/images/ui-bg_diagonals-thick_18_b81900_40x40.png deleted file mode 100644 index 954e22db..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_diagonals-thick_18_b81900_40x40.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_diagonals-thick_20_666666_40x40.png b/assets/stylesheets/jquery-ui/images/ui-bg_diagonals-thick_20_666666_40x40.png deleted file mode 100644 index 64ece570..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_diagonals-thick_20_666666_40x40.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_flat_0_aaaaaa_40x100.png b/assets/stylesheets/jquery-ui/images/ui-bg_flat_0_aaaaaa_40x100.png deleted file mode 100644 index 5b5dab2a..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_flat_0_aaaaaa_40x100.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_flat_10_000000_40x100.png b/assets/stylesheets/jquery-ui/images/ui-bg_flat_10_000000_40x100.png deleted file mode 100644 index abdc0108..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_flat_10_000000_40x100.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_flat_75_ffffff_40x100.png b/assets/stylesheets/jquery-ui/images/ui-bg_flat_75_ffffff_40x100.png deleted file mode 100644 index ac8b229a..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_flat_75_ffffff_40x100.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_glass_100_eef5fd_1x400.png b/assets/stylesheets/jquery-ui/images/ui-bg_glass_100_eef5fd_1x400.png deleted file mode 100644 index c3492e33..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_glass_100_eef5fd_1x400.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_glass_100_f6f6f6_1x400.png b/assets/stylesheets/jquery-ui/images/ui-bg_glass_100_f6f6f6_1x400.png deleted file mode 100644 index 9b383f4d..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_glass_100_f6f6f6_1x400.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_glass_55_fbf9ee_1x400.png b/assets/stylesheets/jquery-ui/images/ui-bg_glass_55_fbf9ee_1x400.png deleted file mode 100644 index ad3d6346..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_glass_55_fbf9ee_1x400.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_glass_65_ffffff_1x400.png b/assets/stylesheets/jquery-ui/images/ui-bg_glass_65_ffffff_1x400.png deleted file mode 100644 index 42ccba26..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_glass_65_ffffff_1x400.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_glass_75_dadada_1x400.png b/assets/stylesheets/jquery-ui/images/ui-bg_glass_75_dadada_1x400.png deleted file mode 100644 index 5a46b47c..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_glass_75_dadada_1x400.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_glass_75_e6e6e6_1x400.png b/assets/stylesheets/jquery-ui/images/ui-bg_glass_75_e6e6e6_1x400.png deleted file mode 100644 index 86c2baa6..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_glass_75_e6e6e6_1x400.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_glass_95_fef1ec_1x400.png b/assets/stylesheets/jquery-ui/images/ui-bg_glass_95_fef1ec_1x400.png deleted file mode 100644 index 4443fdc1..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_glass_95_fef1ec_1x400.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_gloss-wave_35_759fcf_500x100.png b/assets/stylesheets/jquery-ui/images/ui-bg_gloss-wave_35_759fcf_500x100.png deleted file mode 100644 index 70b39a86..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_gloss-wave_35_759fcf_500x100.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_highlight-soft_100_eeeeee_1x100.png b/assets/stylesheets/jquery-ui/images/ui-bg_highlight-soft_100_eeeeee_1x100.png deleted file mode 100644 index f1273672..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_highlight-soft_100_eeeeee_1x100.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_highlight-soft_75_759fcf_1x100.png b/assets/stylesheets/jquery-ui/images/ui-bg_highlight-soft_75_759fcf_1x100.png deleted file mode 100644 index 3fc3f9ba..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_highlight-soft_75_759fcf_1x100.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/assets/stylesheets/jquery-ui/images/ui-bg_highlight-soft_75_cccccc_1x100.png deleted file mode 100644 index 7c9fa6c6..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-bg_highlight-soft_75_cccccc_1x100.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-icons_222222_256x240.png b/assets/stylesheets/jquery-ui/images/ui-icons_222222_256x240.png deleted file mode 100644 index b273ff11..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-icons_222222_256x240.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-icons_2e83ff_256x240.png b/assets/stylesheets/jquery-ui/images/ui-icons_2e83ff_256x240.png deleted file mode 100644 index 09d1cdc8..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-icons_2e83ff_256x240.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-icons_454545_256x240.png b/assets/stylesheets/jquery-ui/images/ui-icons_454545_256x240.png deleted file mode 100644 index 59bd45b9..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-icons_454545_256x240.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-icons_759fcf_256x240.png b/assets/stylesheets/jquery-ui/images/ui-icons_759fcf_256x240.png deleted file mode 100644 index e0c6374c..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-icons_759fcf_256x240.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-icons_888888_256x240.png b/assets/stylesheets/jquery-ui/images/ui-icons_888888_256x240.png deleted file mode 100644 index 6d02426c..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-icons_888888_256x240.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-icons_cd0a0a_256x240.png b/assets/stylesheets/jquery-ui/images/ui-icons_cd0a0a_256x240.png deleted file mode 100644 index 2ab019b7..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-icons_cd0a0a_256x240.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-icons_ffd27a_256x240.png b/assets/stylesheets/jquery-ui/images/ui-icons_ffd27a_256x240.png deleted file mode 100644 index e117effa..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-icons_ffd27a_256x240.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/images/ui-icons_ffffff_256x240.png b/assets/stylesheets/jquery-ui/images/ui-icons_ffffff_256x240.png deleted file mode 100644 index 42f8f992..00000000 Binary files a/assets/stylesheets/jquery-ui/images/ui-icons_ffffff_256x240.png and /dev/null differ diff --git a/assets/stylesheets/jquery-ui/jquery-ui-1.9.2.css b/assets/stylesheets/jquery-ui/jquery-ui-1.9.2.css deleted file mode 100644 index 090d220d..00000000 --- a/assets/stylesheets/jquery-ui/jquery-ui-1.9.2.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! jQuery UI - v1.9.2 - 2012-12-26 -* http://jqueryui.com -* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css -* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2C%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=759fcf&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=628db6&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=628db6&iconColorDefault=759fcf&bgColorHover=eef5fd&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=628db6&fcHover=628db6&iconColorHover=759fcf&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=628db6&fcActive=628db6&iconColorActive=759fcf&bgColorHighlight=759fcf&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=628db6&fcHighlight=363636&iconColorHighlight=759fcf&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px -* Copyright (c) 2012 jQuery Foundation and other contributors Licensed MIT */.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{zoom:1}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:absolute;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin-top:2px;padding:.5em .5em .5em .7em;zoom:1}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-noicons{padding-left:.7em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto;zoom:1}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}* html .ui-autocomplete{width:1px}.ui-button{display:inline-block;position:relative;padding:0;margin-right:.1em;cursor:pointer;text-align:center;zoom:1;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:1.4}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month-year{width:100%}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0em}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current{float:right}.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker-cover{position:absolute;z-index:-1;filter:mask();top:-4px;left:-4px;width:200px;height:200px}.ui-dialog{position:absolute;top:0;left:0;padding:.2em;width:300px;overflow:hidden}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 16px .1em 0}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:19px;margin:-10px 0 0 0;padding:1px;height:18px}.ui-dialog .ui-dialog-titlebar-close span{display:block;margin:1px}.ui-dialog .ui-dialog-titlebar-close:hover,.ui-dialog .ui-dialog-titlebar-close:focus{padding:0}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto;zoom:1}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin:.5em 0 0 0;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:14px;height:14px;right:3px;bottom:3px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:2px;margin:0;display:block;outline:none}.ui-menu .ui-menu{margin-top:-3px;position:absolute}.ui-menu .ui-menu-item{margin:0;padding:0;zoom:1;width:100%}.ui-menu .ui-menu-divider{margin:5px -2px 5px -2px;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.5;zoom:1;font-weight:normal}.ui-menu .ui-menu-item a.ui-state-focus,.ui-menu .ui-menu-item a.ui-state-active{font-weight:normal;margin:-1px}.ui-menu .ui-state-disabled{font-weight:normal;margin:.4em 0 .2em;line-height:1.5}.ui-menu .ui-state-disabled a{cursor:default}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item a{position:relative;padding-left:2em}.ui-menu .ui-icon{position:absolute;top:.2em;left:.2em}.ui-menu .ui-menu-icon{position:static;float:right}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em;zoom:1}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav li a{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active a,.ui-tabs .ui-tabs-nav li.ui-state-disabled a,.ui-tabs .ui-tabs-nav li.ui-tabs-loading a{cursor:text}.ui-tabs .ui-tabs-nav li a,.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}* html .ui-tooltip{background-image:none}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Verdana,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #ddd;background:#eee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #628db6;background:#759fcf url(images/ui-bg_gloss-wave_35_759fcf_500x100.png) 50% 50% repeat-x;color:#fff;font-weight:bold}.ui-widget-header a{color:#fff}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #ccc;background:#f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x;font-weight:bold;color:#628db6}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#628db6;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #628db6;background:#eef5fd url(images/ui-bg_glass_100_eef5fd_1x400.png) 50% 50% repeat-x;font-weight:bold;color:#628db6}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited{color:#628db6;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #628db6;background:#fff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;font-weight:bold;color:#628db6}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#628db6;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #628db6;background:#759fcf url(images/ui-bg_highlight-soft_75_759fcf_1x100.png) 50% top repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat;color:#fff}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#fff}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#fff}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px;background-image:url(images/ui-icons_222222_256x240.png)}.ui-widget-content .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-widget-header .ui-icon{background-image:url(images/ui-icons_ffffff_256x240.png)}.ui-state-default .ui-icon{background-image:url(images/ui-icons_759fcf_256x240.png)}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url(images/ui-icons_759fcf_256x240.png)}.ui-state-active .ui-icon{background-image:url(images/ui-icons_759fcf_256x240.png)}.ui-state-highlight .ui-icon{background-image:url(images/ui-icons_759fcf_256x240.png)}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url(images/ui-icons_ffd27a_256x240.png)}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;-khtml-border-top-left-radius:4px;border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;-khtml-border-top-right-radius:4px;border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;-khtml-border-bottom-left-radius:4px;border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;-khtml-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.ui-widget-overlay{background:#666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat;opacity:.5;filter:Alpha(Opacity=50)}.ui-widget-shadow{margin:-5px 0 0 -5px;padding:5px;background:#000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x;opacity:.2;filter:Alpha(Opacity=20);-moz-border-radius:5px;-khtml-border-radius:5px;-webkit-border-radius:5px;border-radius:5px} diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 7ffb39eb..380d9896 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -1,7 +1,8 @@ # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2013 Karel Pičman +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2011-14 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,10 +27,8 @@ cs: 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 - question_do_you_really_want_to_delete_this_entry: Chcete to skutečně smazat? - error_max_files_exceeded: "Limit pro %{number} najednou stažených souborů je překročen" - question_do_you_really_want_to_delete_this_revision: Chcete skutečně smazat tuto revizi? + 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 error_folder_creation_failed: Vytváření složky selhalo @@ -111,11 +110,9 @@ cs: label_created: Vytvořeno label_changed: Změněno info_changed_by_user: "%{changed} uživatelem" - label_filename: Jméno souboru - label_version: Verze + label_filename: Jméno souboru label_mime: Typ - label_size: Velikost - label_comment: Komentář + label_size: Velikost heading_new_revision: Nová revize option_version_same: Stejná option_version_minor: Podružná @@ -159,8 +156,7 @@ 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}" - question_do_you_really_want_to_delete_entries: Opravdu chcete smazat vybrané položky? + warning_some_entries_were_not_deleted: "Některé položky nebyly smazány: %{entries}" title_delete_checked: Smaž vybrané title_number_of_files_in_directory: Počet souborů ve složce title_filename_for_download: Název Zip archívu ke stažení @@ -183,8 +179,8 @@ cs: comment_copied_from: "Zkopírováno z %{source}" notice_file_copied: Soubor zkopírován notice_file_moved: Soubor přesunut - label_target_project: Cílový projekt - label_target_folder: Cílový adresář + field_target_project: Cílový projekt + field_target_folder: Cílový adresář title_copy_or_move: Kopírovat/Přesunout label_dmsf_folder_plural: Dmsf složky comment_moved_from: "Přesunuto z %{source}" @@ -195,8 +191,8 @@ cs: 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. @@ -211,8 +207,7 @@ 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_file_parent_locked: "Nadřazená složka %{name} je zamčená" + title_folder_parent_locked: "Nadřazená složka %{name} je zamčená" title_unlock_folder: Odemknout title_lock_folder: Zamknout @@ -267,14 +262,40 @@ cs: text_email_subject_updated: "Schvalovací proces %{name} aktualizován" text_email_subject_started: "Schvalovací proces %{name} 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_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_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 + label_link_from: Odkaz z + label_link_to: Odkaz do + label_notifications_on: Zapnout notifikace + label_notifications_off: Vypnout notifikace + field_target_file: Zdrojový soubor + title_download_entries: Historie stahování + label_link_name: Název odkazu + label_target_folder: Cílový adresář + label_source_folder: Zdrojový adresář + label_target_project: Cílový projekt + label_source_project: Zdrojový projekt + + text_email_doc_updated_subject: "Dokumenty projektu %{project} 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: právě smazal dokumety projektu + 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}" my: blocks: diff --git a/config/locales/de.yml b/config/locales/de.yml index b430fdba..708045bf 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1,7 +1,8 @@ # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Terrence Miller -# Copyright (C) 2013 Christian Wetting +# Copyright (C) 2011 Terrence Miller +# Copyright (C) 2013 Christian Wetting +# Copyright (C) 2011-14 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,10 +27,8 @@ de: notice_file_locked: Datei gesperrt warning_file_not_locked: Datei nicht gesperrt notice_file_unlocked: Dateisperre gelöst - error_only_user_that_locked_file_can_unlock_it: Nur der Benutzer, der die Datei gesperrt hat, kann sie auch wieder freigeben - question_do_you_really_want_to_delete_this_entry: Willst du diesen Eintrag wirklich löschen? - error_max_files_exceeded: "Grenze für %{number} gleichzeitig heruntergeladene Dateien überschritten" - question_do_you_really_want_to_delete_this_revision: Möchtest du diese Dateiversion wirklich löschen? + 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: Entry Projekt entspricht nicht aktuellen Projekt notice_folder_created: Ordner erstellt error_folder_creation_failed: Ordnererstellung fehlgeschlagen @@ -38,6 +37,7 @@ de: 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 + error_folder_is_locked: Order ist gesperrt error_file_is_locked: Datei ist gesperrt notice_file_deleted: Datei gelöscht error_at_least_one_revision_must_be_present: Es muss mindestens eine Version existieren @@ -110,11 +110,9 @@ de: label_created: Erstellt label_changed: Geändert info_changed_by_user: "%{changed} von" - label_filename: Dateiname - label_version: Version + label_filename: Dateiname label_mime: Mime - label_size: Größe - label_comment: Kommentar + label_size: Größe heading_new_revision: Neue Version option_version_same: gleiche Version option_version_minor: Unterversion @@ -158,8 +156,7 @@ 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: Aufnahmen löschen - warning_some_entries_were_not_deleted: "Enige Aufnahmen wurden nich gelöscht: %{entries}" - question_do_you_really_want_to_delete_entries: Willst du wirklich die gewählten Aufnahmen löschen? + warning_some_entries_were_not_deleted: "Enige Aufnahmen wurden nich gelöscht: %{entries}" title_delete_checked: Lösch gewähltest title_number_of_files_in_directory: Dateizahl in dem Ordner title_filename_for_download: Dateiname benutzt zum Herunterladen oder ZIP-archivierung @@ -182,8 +179,8 @@ de: comment_copied_from: "Kopiert aus %{source}" notice_file_copied: Datei kopiert notice_file_moved: Datei verschoben - label_target_project: Zielprojekte - label_target_folder: Zielordner + field_target_project: Zielprojekte + field_target_folder: Zielordner title_copy_or_move: Kopieren/Verschieben label_dmsf_folder_plural: Ordner comment_moved_from: "Verschoben aus %{source}" @@ -210,17 +207,14 @@ de: 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_file_parent_locked: "Übergeordnetes Verzeichnis %{name} ist gesperrt" + title_folder_parent_locked: "Übergeordnetes Verzeichnis %{name} ist gesperrt" title_unlock_folder: Zur Modifikation für andere Benutzer entsperren title_lock_folder: Zum Schutz vor Modifikation 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. - - # Not translated + note_webdav_strategy: Erlaubt dem Administrator den Wechsel der WebDav Nutzung zwischen nur lesend und auch schreibenden Zugriffen. error_unable_delete_dmsf_workflow: Unable to delete the workflow error_empty_note: The note can't be empty @@ -242,7 +236,7 @@ de: label_dmsf_wokflow_action_assign: Assign an approval workflow label_dmsf_wokflow_action_start: Start workflow label_dmsf_workflow_add_approver: "Add a new approver with a logical function:" - label_or: or + label_or: oder label_action: Action label_note: Note title_none: None @@ -276,6 +270,32 @@ de: 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 + label_link_from: Link from + label_link_to: Link to + label_notifications_on: Notifications on + label_notifications_off: Notifications off + field_target_file: Source file + title_download_entries: Download entries + label_link_name: Link name + 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" + 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: 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}" my: blocks: diff --git a/config/locales/en.yml b/config/locales/en.yml index 38490799..810044c1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,8 +1,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-14 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,10 +27,8 @@ en: 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 file can unlock it - question_do_you_really_want_to_delete_this_entry: Do you really want to delete this entry? - error_max_files_exceeded: "Limit for %{number} simultaneously downloaded files exceeded" - question_do_you_really_want_to_delete_this_revision: Do you really want to delete this revision? + error_only_user_that_locked_file_can_unlock_it: Only user that locked 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 @@ -112,12 +110,9 @@ en: label_created: Created label_changed: Changed info_changed_by_user: "%{changed} by" - label_filename: Filename - label_version: Version - label_approval_workflow: Workflow + label_filename: Filename label_mime: Mime - label_size: Size - label_comment: Comment + label_size: Size heading_new_revision: New Revision option_version_same: Same option_version_minor: Minor @@ -161,8 +156,7 @@ 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}" - question_do_you_really_want_to_delete_entries: Do you really want to delete checked entries? + warning_some_entries_were_not_deleted: "Some entries weren't deleted: %{entries}" title_delete_checked: Delete checked title_number_of_files_in_directory: Number of files in directory title_filename_for_download: Filename used for download or in Zip archive @@ -174,7 +168,7 @@ en: warning_xapian_not_available: Xapian not available menu_dmsf: DMSF label_physical_file_delete: Physical file delete - user_is_not_project_member: You are not member of the project + user_is_not_project_member: You are not a member of the project heading_access_downloads_emails: Downloads/Emails heading_access_first: First heading_access_last: Last @@ -185,8 +179,8 @@ en: comment_copied_from: "Copied from %{source}" notice_file_copied: File copied notice_file_moved: File moved - label_target_project: Target project - label_target_folder: Target folder + field_target_project: Target project + field_target_folder: Target folder title_copy_or_move: Copy/Move label_dmsf_folder_plural: Dmsf folders comment_moved_from: "Moved from %{source}" @@ -213,8 +207,7 @@ 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_file_parent_locked: "Parent folder %{name} is locked" + 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 @@ -280,10 +273,34 @@ en: label_my_open_approvals: My open approvals label_my_locked_documents: My locked documents + title_create_link: Create a symbolic link + label_link_from: Link from + label_link_to: Link to + label_notifications_on: Notifications on + label_notifications_off: Notifications off + field_target_file: Source file + title_download_entries: Download entries + label_link_name: Link name + 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" + 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: 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}" + my: blocks: locked_documents: Locked documents openap_provals: 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. + 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 diff --git a/config/locales/es.yml b/config/locales/es.yml index 816b99d0..f9d46ab9 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -2,6 +2,8 @@ # # Copyright (C) 2011 Vít Jonáš # +# +# # 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 @@ -25,10 +27,8 @@ es: 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 - question_do_you_really_want_to_delete_this_entry: ¿Está seguro de borrar el ítem seleccionado? - error_max_files_exceeded: "Se excedio el numero permitido de archivos bajados de manera simultánea:" - question_do_you_really_want_to_delete_this_revision: ¿Está seguro de borrar la revision seleccionada? + 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 @@ -53,9 +53,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 - - # Not translated + notice_file_notifications_deactivated: Notificación de archivo desactivada link_details: "%{title} details" link_edit: "Edit %{title}" submit_create: Create @@ -112,11 +110,9 @@ es: label_created: Created label_changed: Changed info_changed_by_user: "%{changed} by" - label_filename: Filename - label_version: Version + label_filename: Filename label_mime: Mime - label_size: Size - label_comment: Comment + label_size: Size heading_new_revision: New Revision option_version_same: Same option_version_minor: Minor @@ -160,8 +156,7 @@ es: 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}" - question_do_you_really_want_to_delete_entries: Do you really want to delete checked entries? + warning_some_entries_were_not_deleted: "Some entries weren't deleted: %{entries}" title_delete_checked: Delete checked title_number_of_files_in_directory: Number of files in directory title_filename_for_download: Filename used for download or in Zip archive @@ -173,7 +168,7 @@ es: warning_xapian_not_available: Xapian not available menu_dmsf: DMSF label_physical_file_delete: Physical file delete - user_is_not_project_member: You are not member of the project + user_is_not_project_member: You are not a member of the project heading_access_downloads_emails: Downloads/Emails heading_access_first: First heading_access_last: Last @@ -184,8 +179,8 @@ es: comment_copied_from: "Copied from %{source}" notice_file_copied: File copied notice_file_moved: File moved - label_target_project: Target project - label_target_folder: Target folder + field_target_project: Target project + field_target_folder: Target folder title_copy_or_move: Copy/Move label_dmsf_folder_plural: Dmsf folders comment_moved_from: "Moved from %{source}" @@ -212,8 +207,7 @@ es: 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_file_parent_locked: "Parent folder %{name} is locked" + 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 @@ -276,12 +270,37 @@ es: 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 + label_link_from: Link from + label_link_to: Link to + label_notifications_on: Notifications on + label_notifications_off: Notifications off + field_target_file: Source file + title_download_entries: Download entries + label_link_name: Link name + 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" + 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: 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}" my: blocks: 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 diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 215c69e2..b8a6e627 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -2,7 +2,7 @@ # # Copyright (C) 2011 Vít Jonáš # Copyright (C) 2012 Daniel Munn -# Copyright (C) 2013 karel Pičman +# Copyright (C) 2014 Atmis # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -27,10 +27,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é" - question_do_you_really_want_to_delete_this_entry: Etes-vous sûr de vouloir supprimer cet élément ? - error_max_files_exceeded: Le nombre de fichiers pouvant être téléchargés simultanément est dépassé - question_do_you_really_want_to_delete_this_revision: Etes-vous sûr de vouloir supprimer cette révision ? + 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 @@ -112,12 +110,9 @@ fr: label_created: Créé label_changed: Modifié info_changed_by_user: "%{changed} par" - label_filename: Fichier - label_version: Version - label_approval_workflow: Flux de validation + label_filename: Fichier label_mime: Type - label_size: Taille - label_comment: Commentaires + label_size: Taille heading_new_revision: Nouvelle révision option_version_same: (identique) option_version_minor: (modification mineure) @@ -161,8 +156,7 @@ 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}" - question_do_you_really_want_to_delete_entries: Etes-vous sûr de vouloir supprimer le(s) élément(s) sélectionné(s)? + 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_number_of_files_in_directory: Nombre de fichiers dans le dossier title_filename_for_download: "Nom du fichier à utiliser lors du téléchargement ou de l'archive ZIP" @@ -185,8 +179,8 @@ fr: comment_copied_from: "Copie effectuée depuis %{source}" notice_file_copied: Fichier copié notice_file_moved: Fichier déplacé - label_target_project: Projet cible - label_target_folder: Dossier cible + field_target_project: Projet cible + field_target_folder: Dossier cible title_copy_or_move: Copie/Déplacement label_dmsf_folder_plural: Les dossiers de DMSF comment_moved_from: "Déplacé depuis %{source}" @@ -213,8 +207,7 @@ 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_file_parent_locked: "Le dossier parent %{name} verrouillé" + 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" @@ -279,6 +272,30 @@ 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: Create a symbolic link + label_link_from: Link from + label_link_to: Link to + label_notifications_on: Notifications on + label_notifications_off: Notifications off + field_target_file: Source file + title_download_entries: Download entries + label_link_name: Link name + 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" + 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: 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}" my: blocks: @@ -286,4 +303,4 @@ fr: 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." + 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." \ No newline at end of file diff --git a/config/locales/ja.yml b/config/locales/ja.yml index a0b06d63..b47b27e9 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -2,6 +2,8 @@ # # Copyright (C) 2011 Vít Jonáš # +# +# # 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 @@ -25,10 +27,8 @@ ja: notice_file_locked: ファイルをロックしました warning_file_not_locked: ファイルはロックされていません notice_file_unlocked: ファイルをロック解除しました - error_only_user_that_locked_file_can_unlock_it: ファイルをロックしたユーザだけがロック解除できます - question_do_you_really_want_to_delete_this_entry: 本当にこのエントリーを削除しますか? - error_max_files_exceeded: "同時にダウンロードできるファイル数の上限 %{number} を超えています" - question_do_you_really_want_to_delete_this_revision: 本当にこのリビジョンを削除しますか? + 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: フォルダを作成できません @@ -110,11 +110,9 @@ ja: label_created: 作成者/日時 label_changed: 更新者/日時 info_changed_by_user: "/ %{changed}" - label_filename: ファイル名 - label_version: バージョン + label_filename: ファイル名 label_mime: Mime - label_size: サイズ - label_comment: コメント + label_size: サイズ heading_new_revision: 新しいリビジョン option_version_same: 変更なし option_version_minor: マイナー @@ -158,8 +156,7 @@ 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}" - question_do_you_really_want_to_delete_entries: 本当にチェックしたエントリーを削除しますか? + warning_some_entries_were_not_deleted: "いくつかのエントリーは削除されませんでした: %{entries}" title_delete_checked: チェックしたものを削除します title_number_of_files_in_directory: フォルダ内のファイル数 title_filename_for_download: ファイル名はダウンロードまたは Zip アーカイブに使われます @@ -182,8 +179,8 @@ ja: comment_copied_from: "%{source} からコピーしました" notice_file_copied: ファイルをコピーしました notice_file_moved: ファイルを移動しました - label_target_project: ターゲットプロジェクト - label_target_folder: ターゲットフォルダ + field_target_project: ターゲットプロジェクト + field_target_folder: ターゲットフォルダ title_copy_or_move: コピー/移動 label_dmsf_folder_plural: Dmsf フォルダ comment_moved_from: "%{source} から移動しました" @@ -193,9 +190,7 @@ ja: warning_no_project_to_copy_folder_to: フォルダをコピーするプロジェクトがありません title_copy: コピー error_folder_cannot_be_copied: フォルダをコピーできません - notice_folder_copied: フォルダをコピーしました - - # Not translated + notice_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." @@ -212,8 +207,7 @@ ja: 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_file_parent_locked: "Parent folder %{name} is locked" + 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" @@ -276,6 +270,32 @@ ja: 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 + label_link_from: Link from + label_link_to: Link to + label_notifications_on: Notifications on + label_notifications_off: Notifications off + field_target_file: Source file + title_download_entries: Download entries + label_link_name: Link name + 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" + 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: 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}" my: blocks: diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 81bde9fd..90485e91 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -2,6 +2,8 @@ # # Copyright (C) 2011 Vít Jonáš # +# +# # 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 @@ -25,10 +27,8 @@ ru: notice_file_locked: Файл заблокирован warning_file_not_locked: Файл не заблокирован notice_file_unlocked: Файл разблокирован - error_only_user_that_locked_file_can_unlock_it: Только пользователь, который заблокировал файл, может его разблокировать - question_do_you_really_want_to_delete_this_entry: Вы действительно хотите удалить этот файл? - error_max_files_exceeded: "Ограничение для %{number} одновременно загружаемых файлов превышено" - question_do_you_really_want_to_delete_this_revision: Вы действительно хотите удалить эту редакцию? + 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: Папку не удалось создать @@ -110,11 +110,9 @@ ru: label_created: Создан label_changed: Изменен info_changed_by_user: "%{changed} пользователем" - label_filename: Имя файла - label_version: Версия + label_filename: Имя файла label_mime: MIME-тип - label_size: Размер - label_comment: Комментарий + label_size: Размер heading_new_revision: Новая редакция option_version_same: Та же версия option_version_minor: Незначительные изменения @@ -158,8 +156,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}" - question_do_you_really_want_to_delete_entries: Вы действительно хотите удалить выбранные файлы? + warning_some_entries_were_not_deleted: "Некоторые файлы не были удалены: %{entries}" title_delete_checked: Удалить выбранные документы title_number_of_files_in_directory: Количество файлов в директории title_filename_for_download: Имя файла для скачиваемого архива @@ -182,8 +179,8 @@ ru: comment_copied_from: "Скопировано из %{source}" notice_file_copied: Файл скопирован notice_file_moved: Файл перемещен - label_target_project: Целевой проект - label_target_folder: Целевая папка + field_target_project: Целевой проект + field_target_folder: Целевая папка title_copy_or_move: Копировать/Переместить label_dmsf_folder_plural: DMSF папки comment_moved_from: "Перемещен из %{source}" @@ -210,8 +207,7 @@ ru: warning_folder_not_locked: "К сожалению, папка не может быть заблокирована" notice_folder_unlocked: "Папка была успешно разблокирована" error_only_user_that_locked_folder_can_unlock_it: "Только пользователь, который заблокировал папку, может её разблокировать" - title_folder_parent_locked: "Родительская папка %{name} заблокирована" - title_file_parent_locked: "Родительская папка %{name} заблокирована" + title_folder_parent_locked: "Родительская папка %{name} заблокирована" title_unlock_folder: "Разблокируйте папку, чтобы разрешить изменение её другими участниками" title_lock_folder: "Заблокируйте папку, чтобы запретить ёё изменение другими участниками" @@ -219,9 +215,7 @@ ru: select_option_webdav_readwrite: "Чтение/Запись" label_webdav_strategy: "Стратегия WebDAV" note_webdav_strategy: "Позволяет администратору решить в каком режиме предоставить доступ к WebDAV для конечных пользователей (Только для чтения или Чтение+Запись)." - - # Not translated - + 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 @@ -276,6 +270,32 @@ ru: 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 + label_link_from: Link from + label_link_to: Link to + label_notifications_on: Notifications on + label_notifications_off: Notifications off + field_target_file: Source file + title_download_entries: Download entries + label_link_name: Link name + 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" + 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: 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}" my: blocks: diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 6e7faa41..146689b5 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -2,6 +2,8 @@ # # Copyright (C) 2011 # +# +# # 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 @@ -25,10 +27,8 @@ sl: 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. - question_do_you_really_want_to_delete_this_entry: Ste prepričani, da želite izbrisati objekt? - error_max_files_exceeded: "Max %{number} datotek za istočasno nalaganje je preseženo." - question_do_you_really_want_to_delete_this_revision: Ste prepričani, da želite izbrisati verzijo datoteke? + 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 @@ -110,11 +110,9 @@ sl: label_created: Narejeno label_changed: Spremenjeno info_changed_by_user: "%{changed} po" - label_filename: Datoteka - label_version: Verzija + label_filename: Datoteka label_mime: Mime - label_size: Velikost - label_comment: Komentar + label_size: Velikost heading_new_revision: Nova verzija option_version_same: Enako option_version_minor: Minor @@ -158,8 +156,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}" - question_do_you_really_want_to_delete_entries: Ste prepričani, da želite izbrisati izbrane enote? + warning_some_entries_were_not_deleted: "Nekatere enote niso izbrisane: %{entries}" title_delete_checked: Izbriši izbrano title_number_of_files_in_directory: Število datotek v mapi title_filename_for_download: Naziv datoteke za prenos dol ali Zip arhiva @@ -177,14 +174,13 @@ sl: heading_access_last: Zadnji label_dmsf_updated: Arhiv posodobljen 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 notice_file_moved: Datoteka premaknjena - label_target_project: Ciljni projekt - label_target_folder: Ciljna mapa + field_target_project: Ciljni projekt + field_target_folder: Ciljna mapa title_copy_or_move: "Kopiraj/Premakni" label_dmsf_folder_plural: Arhivske mape comment_moved_from: "Premaknjeno iz %{source}" @@ -211,8 +207,7 @@ 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_file_parent_locked: "Nadrejena mapa %{name} je zaklenjena" + 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 @@ -220,8 +215,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." - - # Not translated + 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 @@ -276,6 +270,32 @@ sl: 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 + label_link_from: Link from + label_link_to: Link to + label_notifications_on: Notifications on + label_notifications_off: Notifications off + field_target_file: Source file + title_download_entries: Download entries + label_link_name: Link name + 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" + 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: 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}" my: blocks: diff --git a/config/locales/zh.yml b/config/locales/zh.yml index c907664e..8ca49c32 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -2,6 +2,8 @@ # # Copyright (C) 2011 Vít Jonáš # +# +# # 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 @@ -25,10 +27,8 @@ zh: notice_file_locked: 文件锁定 warning_file_not_locked: 文件未锁定 notice_file_unlocked: 文件解锁 - error_only_user_that_locked_file_can_unlock_it: 只有锁定文件的用户才能解锁该文件 - question_do_you_really_want_to_delete_this_entry: 您确定删除此条目? - error_max_files_exceeded: "超出同时下载%{number}个文件数量限制" - question_do_you_really_want_to_delete_this_revision: 您确定删除此修订版本吗? + 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: 文件夹创建失败 @@ -110,11 +110,9 @@ zh: label_created: 创建 label_changed: 修改 info_changed_by_user: "%{changed} by" - label_filename: 文件名 - label_version: 版本 + label_filename: 文件名 label_mime: Mime - label_size: 大小 - label_comment: 注释 + label_size: 大小 heading_new_revision: 新修订 option_version_same: Same option_version_minor: Minor @@ -158,8 +156,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}" - question_do_you_really_want_to_delete_entries: 您确定删除所选的条目吗? + warning_some_entries_were_not_deleted: "某些条目未被删除: %{entries}" title_delete_checked: 删除选中 title_number_of_files_in_directory: 目录总文件个数 title_filename_for_download: 用于下载或zip归档的文件名 @@ -177,16 +174,13 @@ zh: heading_access_last: 末次 label_dmsf_updated: DMSF updated title_total_size_of_all_files: 文件夹所有文件总大小 - project_module_dmsf: 文档管家 - - # Not translated - + 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 notice_file_moved: File moved - label_target_project: Target project - label_target_folder: Target folder + field_target_project: Target project + field_target_folder: Target folder title_copy_or_move: Copy/Move label_dmsf_folder_plural: Dmsf folders comment_moved_from: "Moved from %{source}" @@ -213,8 +207,7 @@ 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_file_parent_locked: "Parent folder %{name} is locked" + 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 @@ -277,6 +270,32 @@ zh: 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 + label_link_from: Link from + label_link_to: Link to + label_notifications_on: Notifications on + label_notifications_off: Notifications off + field_target_file: Source file + title_download_entries: Download entries + label_link_name: Link name + 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" + 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: 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}" my: blocks: diff --git a/config/routes.rb b/config/routes.rb index cf2edc0a..c099a4a4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -26,20 +26,22 @@ RedmineApp::Application.routes.draw do # [As this controller also processes 'folders' it maybe better to branch into a folder route rather than leaving it as is] ## post '/projects/:id/dmsf/create', :controller => 'dmsf', :action => 'create' - post '/projects/:id/dmsf/notify/activate', :controller => 'dmsf', :action => 'notify_activate' - post '/projects/:id/dmsf/notify/deactivate', :controller => 'dmsf', :action => 'notify_deactivate' - post '/projects/:id/dmsf/delete', :controller => 'dmsf', :action => 'delete' + get '/projects/:id/dmsf/notify/activate', :controller => 'dmsf', :action => 'notify_activate', :as => 'notify_activate_dmsf' + get '/projects/:id/dmsf/notify/deactivate', :controller => 'dmsf', :action => 'notify_deactivate', :as => 'notify_deactivate_dmsf' + get '/projects/:id/dmsf/delete', :controller => 'dmsf', :action => 'delete', :as => 'delete_dmsf' post '/projects/:id/dmsf/save', :controller => 'dmsf', :action => 'save' post '/projects/:id/dmsf/save/root', :controller => 'dmsf', :action => 'save_root' post '/projects/:id/dmsf/entries', :controller => 'dmsf', :action => 'entries_operation' - post '/projects/:id/dmsf/entries/delete', :controller => 'dmsf', :action => 'delete_entries' + post '/projects/:id/dmsf/tag_changed', :controller => 'dmsf', :action => 'tag_changed', :as => 'tag_changed' + post '/projects/:id/dmsf/entries/delete', :controller => 'dmsf', :action => 'delete_entries', :as => 'delete_entries' post '/projects/:id/dmsf/entries/email', :controller => 'dmsf', :action => 'entries_email' - post '/projects/:id/dmsf/lock', :controller => 'dmsf', :action => 'lock' - post '/projects/:id/dmsf/unlock', :controller => 'dmsf', :action => 'unlock' - get '/projects/:id/dmsf/', :controller => 'dmsf', :action => 'show', :as => 'dmsf' - get '/projects/:id/dmsf/new', :controller => 'dmsf', :action => 'new' - get '/projects/:id/dmsf/edit', :controller=> 'dmsf', :action => 'edit' - get '/projects/:id/dmsf/edit/root', :controller=> 'dmsf', :action => 'edit_root' + get '/projects/:id/dmsf/entries/download_email_entries', :controller => 'dmsf', :action => 'download_email_entries', :as => 'download_email_entries' + get '/projects/:id/dmsf/lock', :controller => 'dmsf', :action => 'lock', :as => 'lock_dmsf' + 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' # # dmsf_state controller @@ -61,21 +63,17 @@ RedmineApp::Application.routes.draw do # dmsf_files controller # /dmsf/files/ ## - post '/dmsf/files/:id/notify/activate', :controller => 'dmsf_files', :action => 'notify_activate' - post '/dmsf/files/:id/notify/deactivate', :controller => 'dmsf_files', :action => 'notify_deactivate' - post '/dmsf/files/:id/lock', :controller => 'dmsf_files', :action => 'lock' - post '/dmsf/files/:id/unlock', :controller => 'dmsf_files', :action => 'unlock' - post '/dmsf/files/:id/delete', :controller => 'dmsf_files', :action => 'delete' + get '/dmsf/files/:id/notify/activate', :controller => 'dmsf_files', :action => 'notify_activate', :as => 'notify_activate_dmsf_files' + get '/dmsf/files/:id/notify/deactivate', :controller => 'dmsf_files', :action => 'notify_deactivate', :as => 'notify_deactivate_dmsf_files' + get '/dmsf/files/:id/lock', :controller => 'dmsf_files', :action => 'lock', :as => 'lock_dmsf_files' + get '/dmsf/files/:id/unlock', :controller => 'dmsf_files', :action => 'unlock', :as => 'unlock_dmsf_files' + post '/dmsf/files/:id/delete', :controller => 'dmsf_files', :action => 'delete', :as => 'delete_dmsf_files' post '/dmsf/files/:id/revision/create', :controller => 'dmsf_files', :action => 'create_revision' - post '/dmsf/files/:id/revision/delete', :controller => 'dmsf_files', :action => 'delete_revision' - get '/dmsf/files/:id/download', :controller => 'dmsf_files', :action => 'show', :download => '' #Otherwise will not route nil download param + get '/dmsf/files/:id/revision/delete', :controller => 'dmsf_files', :action => 'delete_revision', :as => 'delete_revision' + 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' - get '/dmsf/files/:id', :controller => 'dmsf_files', :action => 'show' - # Just to keep backward compatibility of external url links - get '/dmsf_files/:id', :controller => 'dmsf_files', :action => 'show' - - # Just to keep backward compatibility with old external direct links - get '/dmsf_files/:id', :controller => 'dmsf_files', :action => 'show' + get '/dmsf/files/:id', :controller => 'dmsf_files', :action => 'show', :as => 'dmsf_file' + delete '/dmsf/files/:id', :controller => 'dmsf_files', :action => 'delete' # # files_copy controller @@ -83,7 +81,7 @@ RedmineApp::Application.routes.draw do ## post '/dmsf/files/:id/copy/create', :controller => 'dmsf_files_copy', :action => 'create' post '/dmsf/files/:id/copy/move', :controller => 'dmsf_files_copy', :action => 'move' - get '/dmsf/files/:id/copy', :controller => 'dmsf_files_copy', :action => 'new' + get '/dmsf/files/:id/copy', :controller => 'dmsf_files_copy', :action => 'new', :as => 'copy_file' # # folders_copy controller @@ -91,7 +89,7 @@ RedmineApp::Application.routes.draw do ## #verify :method => :post, :only => [:copy_to], :render => { :nothing => true, :status => :method_not_allowed } post '/dmsf/folders/:id/copy/to', :controller => 'dmsf_folders_copy', :action => 'copy_to' - get '/dmsf/folders/:id/copy', :controller => 'dmsf_folders_copy', :action => 'new' + 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] @@ -110,7 +108,7 @@ RedmineApp::Application.routes.draw do get 'assign' get 'log' post 'new_action' - post 'start' + get 'start' post 'assignment' end end @@ -118,4 +116,11 @@ RedmineApp::Application.routes.draw do 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 + + # Links + resources :dmsf_links do + member do + end + end + end diff --git a/db/migrate/20131113141403_create_dmsf_links.rb b/db/migrate/20131113141403_create_dmsf_links.rb new file mode 100644 index 00000000..a53a47c4 --- /dev/null +++ b/db/migrate/20131113141403_create_dmsf_links.rb @@ -0,0 +1,38 @@ +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2014 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 CreateDmsfLinks < ActiveRecord::Migration + def change + create_table :dmsf_links do |t| + t.integer :target_project_id, :null => false + t.integer :target_id, :null => false + t.string :target_type, :limit => 10, :null => false + t.string :name, :null => false + t.references :project, :null => false + t.references :dmsf_folder + t.boolean :deleted, :default => false, :null => false + t.integer :deleted_by_user_id + t.timestamps + end + add_index :dmsf_links, :project_id + end + + def self.down + drop_table :dmsf_links + end +end diff --git a/db/migrate/20140314132501_notifications_on.rb b/db/migrate/20140314132501_notifications_on.rb new file mode 100644 index 00000000..2ba3338b --- /dev/null +++ b/db/migrate/20140314132501_notifications_on.rb @@ -0,0 +1,30 @@ +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2014 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 NotificationsOn < ActiveRecord::Migration + def up + # Switch on the default notifications for new projects and folders + change_column :projects, :dmsf_notification, :boolean, :default => true, :null => true + change_column :dmsf_folders, :notification, :boolean, :default => true, :null => false + end + + def down + change_column :projects, :dmsf_notification, :boolean, :default => false, :null => true + change_column :dmsf_folders, :notification, :boolean, :default => false, :null => false + end +end \ No newline at end of file diff --git a/dmsf_user_guide.odt b/dmsf_user_guide.odt new file mode 100644 index 00000000..3af3e52b Binary files /dev/null and b/dmsf_user_guide.odt differ diff --git a/init.rb b/init.rb index 0486da43..03cd54ae 100644 --- a/init.rb +++ b/init.rb @@ -1,8 +1,8 @@ # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn -# Copyright (C) 2013 Karel Picman +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 Karel Picman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,11 +26,11 @@ Redmine::Plugin.register :redmine_dmsf do name 'DMSF' author 'Vit Jonas / Daniel Munn / Karel Picman' description 'Document Management System Features' - version '1.4.7 stable' + version '1.4.8 stable' url 'http://www.redmine.org/plugins/dmsf' - author_url 'mailto:karel.picman@kontron.com' + author_url 'https://github.com/danmunn/redmine_dmsf/graphs/contributors' - requires_redmine :version_or_higher => '2.0.0' + requires_redmine :version_or_higher => '2.3.0' settings :partial => 'settings/dmsf_settings', :default => { @@ -42,7 +42,8 @@ Redmine::Plugin.register :redmine_dmsf do 'dmsf_index_database' => Rails.root.join('files/dmsf_index').to_s, 'dmsf_stemming_lang' => 'english', 'dmsf_stemming_strategy' => 'STEM_NONE', - 'dmsf_webdav' => '1' + 'dmsf_webdav' => '1', + 'dmsf_display_notified_recipients' => 0 } menu :project_menu, :dmsf, { :controller => 'dmsf', :action => 'show' }, :caption => :menu_dmsf, :before => :documents, :param => :id @@ -57,19 +58,21 @@ Redmine::Plugin.register :redmine_dmsf do permission :user_preferences, {:dmsf_state => [:user_pref_save]} permission :view_dmsf_files, - {:dmsf => [:entries_operation, :entries_email], + {:dmsf => [:entries_operation, :entries_email, :download_email_entries, :tag_changed], :dmsf_files => [:show], :dmsf_files_copy => [:new, :create, :move], :dmsf_workflows => [:log]}, :read => true permission :folder_manipulation, - {:dmsf => [:new, :create, :delete, :edit, :save, :edit_root, :save_root, :lock, :unlock, :notify_activate, :notify_deactivate]} + {:dmsf => [:new, :create, :delete, :edit, :save, :edit_root, :save_root, :lock, :unlock, :notify_activate, :notify_deactivate, :delete_entries]} permission :file_manipulation, {:dmsf_files => [:create_revision, :delete, :lock, :unlock, :delete_revision, :notify_activate, :notify_deactivate], :dmsf_upload => [:upload_files, :upload_file, :commit_files], - :dmsf_workflows => [:action, :new_action, :autocomplete_for_user, :start, :assign, :assignment]} + :dmsf_workflows => [:action, :new_action, :autocomplete_for_user, :start, :assign, :assignment], + :dmsf_links => [:new, :create, :destroy] + } permission :manage_workflows, - {:dmsf_workflows => [:index, :new, :create, :destroy, :edit, :add_step, :remove_step, :reorder_steps, :update]} + {:dmsf_workflows => [:index, :new, :create, :destroy, :show, :add_step, :remove_step, :reorder_steps, :update]} permission :force_file_unlock, {} end diff --git a/lib/redmine_dmsf.rb b/lib/redmine_dmsf.rb index 49aa857c..4c6d70eb 100644 --- a/lib/redmine_dmsf.rb +++ b/lib/redmine_dmsf.rb @@ -1,8 +1,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-14 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,8 +18,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# Vendor -require 'redmine_dmsf/vendored_dav4rack' +DMSF_MAX_NOTIFICATION_RECEIVERS_INFO = 10 # DMSF libraries require 'redmine_dmsf/patches' #plugin patches @@ -33,5 +32,5 @@ require 'redmine_dmsf/hooks/base_view_hooks' module RedmineDmsf end -# Add plugin's view folder into ActionMailer's paths to search -ActionMailer::Base.append_view_path(File.expand_path(File.dirname(__FILE__) + '/../app/views')) +# 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 diff --git a/lib/redmine_dmsf/hooks/base_view_hooks.rb b/lib/redmine_dmsf/hooks/base_view_hooks.rb index 7ae2cf39..130972c2 100644 --- a/lib/redmine_dmsf/hooks/base_view_hooks.rb +++ b/lib/redmine_dmsf/hooks/base_view_hooks.rb @@ -23,9 +23,7 @@ module RedmineDmsf class DmsfViewListener < Redmine::Hook::ViewListener def view_layouts_base_html_head(context={}) - tags = "\n".html_safe + javascript_include_tag('redmine_dmsf', :plugin => :redmine_dmsf) - tags << "\n".html_safe + stylesheet_link_tag('dmsf', :plugin => :redmine_dmsf) - tags + "\n".html_safe + stylesheet_link_tag('dmsf', :plugin => :redmine_dmsf) end end diff --git a/lib/redmine_dmsf/patches.rb b/lib/redmine_dmsf/patches.rb index 20477fda..db91a040 100644 --- a/lib/redmine_dmsf/patches.rb +++ b/lib/redmine_dmsf/patches.rb @@ -1,7 +1,8 @@ # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 Karel Picman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,7 +18,7 @@ # 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' +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 diff --git a/lib/redmine_dmsf/patches/custom_fields_helper.rb b/lib/redmine_dmsf/patches/custom_fields_helper.rb deleted file mode 100644 index 78b47608..00000000 --- a/lib/redmine_dmsf/patches/custom_fields_helper.rb +++ /dev/null @@ -1,46 +0,0 @@ -# Redmine plugin for Document Management System "Features" -# -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn -# Copyright (C) 2013 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 Patches - module CustomFieldsHelper - def self.included(base) - base.class_eval do - alias_method_chain :custom_fields_tabs, :customer_tab - end - end - - def custom_fields_tabs_with_customer_tab - cf = {:name => 'DmsfFileRevisionCustomField', :partial => 'custom_fields/index', :label => :dmsf} - unless custom_fields_tabs_without_customer_tab.index { |f| f[:name] == cf[:name] } - custom_fields_tabs_without_customer_tab << cf - end - custom_fields_tabs_without_customer_tab - end - end - end -end - -# Apply patch -Rails.configuration.to_prepare do - unless CustomFieldsHelper.included_modules.include?(CustomFieldsHelper) - CustomFieldsHelper.send(:include, RedmineDmsf::Patches::CustomFieldsHelper) - end -end diff --git a/lib/redmine_dmsf/patches/custom_fields_helper_patch.rb b/lib/redmine_dmsf/patches/custom_fields_helper_patch.rb new file mode 100644 index 00000000..ca322fde --- /dev/null +++ b/lib/redmine_dmsf/patches/custom_fields_helper_patch.rb @@ -0,0 +1,78 @@ +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 Karel Picman +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require_dependency 'custom_fields_helper' + +module RedmineDmsf + module Patches + module CustomFieldsHelperPatch + def self.included(base) + base.extend(ClassMethods) + base.send(:include, InstanceMethods) + base.class_eval do + unloadable + if Redmine::VERSION::MAJOR >= 2 && Redmine::VERSION::MINOR >= 5 + alias_method_chain :render_custom_fields_tabs, :render_custom_tab + alias_method_chain :custom_field_type_options, :custom_tab_options + else + alias_method_chain :custom_fields_tabs, :custom_tab + end + end + end + + module ClassMethods + end + + module InstanceMethods + + def custom_fields_tabs_with_custom_tab + add_cf + custom_fields_tabs_without_custom_tab + end + + def render_custom_fields_tabs_with_render_custom_tab(types) + add_cf + render_custom_fields_tabs_without_render_custom_tab(types) + end + + def custom_field_type_options_with_custom_tab_options + add_cf + custom_field_type_options_without_custom_tab_options + end + + private + + def add_cf + cf = {:name => 'DmsfFileRevisionCustomField', :partial => 'custom_fields/index', :label => :dmsf} + unless CustomFieldsHelper::CUSTOM_FIELDS_TABS.index { |f| f[:name] == cf[:name] } + CustomFieldsHelper::CUSTOM_FIELDS_TABS << cf + end + end + end + end + end +end + +# Apply patch +Rails.configuration.to_prepare do + unless CustomFieldsHelper.included_modules.include?(RedmineDmsf::Patches::CustomFieldsHelperPatch) + CustomFieldsHelper.send(:include, RedmineDmsf::Patches::CustomFieldsHelperPatch) + end +end diff --git a/lib/redmine_dmsf/patches/project_patch.rb b/lib/redmine_dmsf/patches/project_patch.rb index bd70a4c6..e20dd51b 100644 --- a/lib/redmine_dmsf/patches/project_patch.rb +++ b/lib/redmine_dmsf/patches/project_patch.rb @@ -1,8 +1,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-14 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,29 +25,35 @@ module RedmineDmsf module ProjectPatch def self.included(base) # :nodoc: - base.send(:include, InstanceMethods) - base.extend(ClassMethods) + base.send(:include, InstanceMethods) base.class_eval do unloadable alias_method_chain :copy, :dmsf - has_many :dmsf_files, :class_name => 'DmsfFile', :foreign_key => 'project_id', :conditions => { :dmsf_folder_id => nil }, :dependent => :destroy - has_many :dmsf_folders, :class_name => 'DmsfFolder', :foreign_key => 'project_id', :conditions => {:dmsf_folder_id => nil}, :dependent => :destroy + has_many :dmsf_files, :class_name => 'DmsfFile', :foreign_key => 'project_id', + :conditions => { :dmsf_folder_id => nil }, :dependent => :destroy + has_many :dmsf_folders, :class_name => 'DmsfFolder', :foreign_key => 'project_id', + :conditions => {:dmsf_folder_id => nil}, :dependent => :destroy has_many :dmsf_workflows, :dependent => :destroy + has_many :folder_links, :class_name => 'DmsfLink', :foreign_key => 'project_id', + :conditions => { :dmsf_folder_id => nil, :target_type => DmsfFolder.model_name }, + :dependent => :destroy + has_many :file_links, :class_name => 'DmsfLink', :foreign_key => 'project_id', + :conditions => { :dmsf_folder_id => nil, :target_type => DmsfFile.model_name }, + :dependent => :destroy end - end - - module ClassMethods - end - + module InstanceMethods def dmsf_count - file_count = DmsfFile.visible.project_root_files(self).count - folder_count = DmsfFolder.visible.project_root_folders(self).count - DmsfFolder.visible.project_root_folders(self).each {|rootfld| file_count += rootfld.deep_file_count; folder_count += rootfld.deep_folder_count } - {:files => file_count, :folders => folder_count} + file_count = self.dmsf_files.visible.count + self.file_links.count + folder_count = self.dmsf_folders.visible.count + self.folder_links.count + self.dmsf_folders.visible.each do |f| + file_count += f.deep_file_count + folder_count += f.deep_folder_count + end + { :files => file_count, :folders => folder_count } end def copy_with_dmsf(project, options={}) @@ -69,19 +75,26 @@ module RedmineDmsf # Simple yet effective approach to copying things def copy_dmsf(project) - DmsfFile.visible.project_root_files(project).each {|f| + project.dmsf_folders.visible.each do |f| f.copy_to(self, nil) - } - DmsfFolder.visible.project_root_folders(project).each {|f| + end + project.dmsf_files.visible.each do |f| f.copy_to(self, nil) - } + end + project.folder_links.visible.each do |l| + l.copy_to(self, nil) + end + project.file_links.visible.each do |l| + l.copy_to(self, nil) + end end end + end end end -#Apply patch +# Apply patch Rails.configuration.to_prepare do unless Project.included_modules.include?(RedmineDmsf::Patches::ProjectPatch) Project.send(:include, RedmineDmsf::Patches::ProjectPatch) diff --git a/lib/redmine_dmsf/test/unit_test.rb b/lib/redmine_dmsf/test/unit_test.rb index 73a4fdab..315a20e1 100644 --- a/lib/redmine_dmsf/test/unit_test.rb +++ b/lib/redmine_dmsf/test/unit_test.rb @@ -18,6 +18,12 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# Load the normal Rails helper +#require File.expand_path("#{Rails.root}/test/test_helper") + +# Use fixtures from redmine +#ActiveSupport::TestCase.fixture_path = "#{Rails.root}/test/fixtures" + module RedmineDmsf module Test class UnitTest < ActiveSupport::TestCase @@ -25,11 +31,13 @@ module RedmineDmsf # Allow us to override the fixtures method to implement fixtures for our plugin. # Ultimately it allows for better integration without blowing redmine fixtures up, # and allowing us to suppliment redmine fixtures if we need to. - def self.fixtures(*table_names) - dir = File.expand_path( File.dirname(__FILE__) + '../../../../test/fixtures') - table_names.each{|x,i| - ActiveRecord::Fixtures.create_fixtures(dir, x) if File.exist?("#{dir}/#{x}.yml") - } + def self.fixtures(*table_names) + dir = File.expand_path('../../../../test/fixtures', __FILE__) + table_names.each do |x| + if File.exist?("#{dir}/#{x}.yml") + ActiveRecord::Fixtures.create_fixtures(dir, x) + end + end super(table_names) end diff --git a/lib/redmine_dmsf/vendor/dav4rack.rb b/lib/redmine_dmsf/vendor/dav4rack.rb deleted file mode 100644 index c8f3b107..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'time' -require 'uri' -require 'nokogiri' - -require 'rack' -require 'dav4rack/http_status' -require 'dav4rack/resource' -require 'dav4rack/handler' -require 'dav4rack/controller' diff --git a/lib/redmine_dmsf/vendor/dav4rack/controller.rb b/lib/redmine_dmsf/vendor/dav4rack/controller.rb deleted file mode 100644 index f65cd0a7..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/controller.rb +++ /dev/null @@ -1,545 +0,0 @@ -require 'uri' - -module DAV4Rack - - class Controller - include DAV4Rack::HTTPStatus - - attr_reader :request, :response, :resource - - # request:: Rack::Request - # response:: Rack::Response - # options:: Options hash - # Create a new Controller. - # NOTE: options will be passed to Resource - def initialize(request, response, options={}) - raise Forbidden if request.path_info.include?('..') - @request = request - @response = response - @options = options - @resource = resource_class.new(actual_path, implied_path, @request, @response, @options) - end - - # s:: string - # Escape URL string - def url_format(resource) - ret = URI.escape(resource.public_path) - if resource.collection? and ret[-1,1] != '/' - ret += '/' - end - ret - end - - # s:: string - # Unescape URL string - def url_unescape(s) - URI.unescape(s) - end - - # Return response to OPTIONS - def options - response["Allow"] = 'OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK' - response["Dav"] = "1, 2" - response["Ms-Author-Via"] = "DAV" - OK - end - - # Return response to HEAD - def head - if(resource.exist?) - response['Etag'] = resource.etag - response['Content-Type'] = resource.content_type - response['Last-Modified'] = resource.last_modified.httpdate - OK - else - NotFound - end - end - - # Return response to GET - def get - if(resource.exist?) - res = resource.get(request, response) - if(res == OK && !resource.collection?) - response['Etag'] = resource.etag - response['Content-Type'] = resource.content_type - response['Content-Length'] = resource.content_length.to_s - response['Last-Modified'] = resource.last_modified.httpdate - end - res - else - NotFound - end - end - - # Return response to PUT - def put - if(resource.collection?) - Forbidden - elsif(!resource.parent_exists? || !resource.parent.collection?) - Conflict - else - resource.lock_check - status = resource.put(request, response) - response['Location'] = "#{scheme}://#{host}:#{port}#{url_format(resource)}" if status == Created - response.body = response['Location'] - status - end - end - - # Return response to POST - def post - resource.post(request, response) - end - - # Return response to DELETE - def delete - if(resource.exist?) - resource.lock_check - resource.delete - else - NotFound - end - end - - # Return response to MKCOL - def mkcol - resource.lock_check - status = resource.make_collection - gen_url = "#{scheme}://#{host}:#{port}#{url_format(resource)}" if status == Created - if(resource.use_compat_mkcol_response?) - multistatus do |xml| - xml.response do - xml.href gen_url - xml.status "#{http_version} #{status.status_line}" - end - end - else - response['Location'] = gen_url - status - end - end - - # Return response to COPY - def copy - move(:copy) - end - - # args:: Only argument used: :copy - # Move Resource to new location. If :copy is provided, - # Resource will be copied (implementation ease) - def move(*args) - unless(resource.exist?) - NotFound - else - resource.lock_check unless args.include?(:copy) - destination = url_unescape(env['HTTP_DESTINATION'].sub(%r{https?://([^/]+)}, '')) - dest_host = $1 - if(dest_host && dest_host.gsub(/:\d{2,5}$/, '') != request.host) - BadGateway - elsif(destination == resource.public_path) - Forbidden - else - collection = resource.collection? - dest = resource_class.new(destination, clean_path(destination), @request, @response, @options.merge(:user => resource.user)) - status = nil - if(args.include?(:copy)) - status = resource.copy(dest, overwrite) - else - return Conflict unless depth.is_a?(Symbol) || depth > 1 - status = resource.move(dest, overwrite) - end - response['Location'] = "#{scheme}://#{host}:#{port}#{url_format(dest)}" if status == Created - # RFC 2518 - if collection - multistatus do |xml| - xml.response do - xml.href "#{scheme}://#{host}:#{port}#{url_format(status == Created ? dest : resource)}" - xml.status "#{http_version} #{status.status_line}" - end - end - else - status - end - end - end - end - - # Return respoonse to PROPFIND - def propfind - unless(resource.exist?) - NotFound - else - unless(request_document.xpath("//#{ns}propfind/#{ns}allprop").empty?) - names = resource.property_names - else - names = ( - ns.empty? ? request_document.remove_namespaces! : request_document - ).xpath( - "//#{ns}propfind/#{ns}prop" - ).children.find_all{ |item| - item.element? && item.name.start_with?(ns) - }.map{ |item| - item.name.sub("#{ns}::", '') - } - raise BadRequest if names.empty? - names = resource.property_names if names.empty? - end - multistatus do |xml| - find_resources.each do |resource| - xml.response do - unless(resource.propstat_relative_path) - xml.href "#{scheme}://#{host}:#{port}#{url_format(resource)}" - else - xml.href url_format(resource) - end - propstats(xml, get_properties(resource, names)) - end - end - end - end - end - - # Return response to PROPPATCH - def proppatch - unless(resource.exist?) - NotFound - else - resource.lock_check - prop_rem = request_match('/propertyupdate/remove/prop').children.map{|n| [n.name] } - prop_set = request_match('/propertyupdate/set/prop').children.map{|n| [n.name, n.text] } - multistatus do |xml| - find_resources.each do |resource| - xml.response do - xml.href "#{scheme}://#{host}:#{port}#{url_format(resource)}" - propstats(xml, set_properties(resource, prop_set)) - end - end - end - end - end - - - # Lock current resource - # NOTE: This will pass an argument hash to Resource#lock and - # wait for a success/failure response. - def lock - lockinfo = request_document.xpath("//#{ns}lockinfo") - asked = {} - asked[:timeout] = request.env['Timeout'].split(',').map{|x|x.strip} if request.env['Timeout'] - asked[:depth] = depth - unless([0, :infinity].include?(asked[:depth])) - BadRequest - else - asked[:scope] = lockinfo.xpath("//#{ns}lockscope").children.find_all{|n|n.element?}.map{|n|n.name}.first - asked[:type] = lockinfo.xpath("#{ns}locktype").children.find_all{|n|n.element?}.map{|n|n.name}.first - asked[:owner] = lockinfo.xpath("//#{ns}owner/#{ns}href").children.map{|n|n.text}.first - begin - lock_time, locktoken = resource.lock(asked) - render_xml(:prop) do |xml| - xml.lockdiscovery do - xml.activelock do - if(asked[:scope]) - xml.lockscope do - xml.send(asked[:scope]) - end - end - if(asked[:type]) - xml.locktype do - xml.send(asked[:type]) - end - end - xml.depth asked[:depth].to_s - xml.timeout lock_time ? "Second-#{lock_time}" : 'infinity' - xml.locktoken do - xml.href locktoken - end - if(asked[:owner]) - xml.owner asked[:owner] - end - end - end - end - response.status = resource.exist? ? OK : Created - rescue LockFailure => e - multistatus do |xml| - e.path_status.each_pair do |path, status| - xml.response do - xml.href path - xml.status "#{http_version} #{status.status_line}" - end - end - end - end - end - end - - # Unlock current resource - def unlock - resource.unlock(lock_token) - end - - # Perform authentication - # NOTE: Authentication will only be performed if the Resource - # has defined an #authenticate method - def authenticate - authed = true - if(resource.respond_to?(:authenticate, true)) - authed = false - uname = nil - password = nil - if(request.env['HTTP_AUTHORIZATION']) - auth = Rack::Auth::Basic::Request.new(request.env) - if(auth.basic? && auth.credentials) - uname = auth.credentials[0] - password = auth.credentials[1] - end - end - authed = resource.send(:authenticate, uname, password) - end - raise Unauthorized unless authed - end - - # ************************************************************ - # private methods - - private - - # Request environment variables - def env - @request.env - end - - # Current request scheme (http/https) - def scheme - request.scheme - end - - # Request host - def host - request.host - end - - # Request port - def port - request.port - end - - # Class of the resource in use - def resource_class - @options[:resource_class] - end - - # Root URI path for the resource - def root_uri_path - @options[:root_uri_path] - end - - # Returns Resource path with root URI removed - def implied_path - - return clean_path(@request.path_info.dup) unless @request.path_info.empty? - c_path = clean_path(@request.path.dup) - return c_path if c_path.length != @request.path.length - - #if we're here then it's probably down to thin - return @request.path.dup.gsub!(/^#{Regexp.escape(@request.script_name)}/, '') unless @request.script_name.empty? - - return c_path #This will probably result in a processing error if we hit here - end - - # x:: request path - # Unescapes path and removes root URI if applicable - def clean_path(x) - ip = url_unescape(x) - ip.gsub!(/^#{Regexp.escape(root_uri_path)}/, '') if root_uri_path - ip - end - - # Unescaped request path - def actual_path - url_unescape(@request.path.dup) - end - - # Lock token if provided by client - def lock_token - env['HTTP_LOCK_TOKEN'] || nil - end - - # Requested depth - def depth - d = env['HTTP_DEPTH'] - if(d =~ /^\d+$/) - d = d.to_i - else - d = :infinity - end - d - end - - # Current HTTP version being used - def http_version - env['HTTP_VERSION'] || env['SERVER_PROTOCOL'] || 'HTTP/1.0' - end - - # Overwrite is allowed - def overwrite - env['HTTP_OVERWRITE'].to_s.upcase != 'F' - end - - # Find resources at depth requested - def find_resources(with_current_resource=true) - ary = nil - case depth - when 0 - ary = [] - when 1 - ary = resource.children - else - ary = resource.descendants - end - with_current_resource ? [resource] + ary : ary - end - - # XML parsed request - def request_document - @request_document ||= Nokogiri.XML(request.body.read) - rescue - raise BadRequest - end - - # Namespace being used within XML document - # TODO: Make this better - def ns - _ns = '' - if(request_document && request_document.root && request_document.root.namespace_definitions.size > 0) - _ns = request_document.root.namespace_definitions.first.prefix.to_s - _ns += ':' unless _ns.empty? - end - _ns - end - - # pattern:: XPath pattern - # Search XML document for given XPath - # TODO: Stripping namespaces not so great - def request_match(pattern) - request_document.remove_namespaces!.xpath(pattern, request_document.root.namespaces) - end - - # root_type:: Root tag name - # Render XML and set Rack::Response#body= to final XML - def render_xml(root_type) - raise ArgumentError.new 'Expecting block' unless block_given? - doc = Nokogiri::XML::Builder.new do |xml_base| - xml_base.send(root_type.to_s, {'xmlns:D' => 'DAV:'}.merge(resource.root_xml_attributes)) do - xml_base.parent.namespace = xml_base.parent.namespace_definitions.first - xml = xml_base['D'] - yield xml - end - end - - if(@options[:pretty_xml]) - response.body = doc.to_xml - else - response.body = doc.to_xml( - :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML - ) - end - response["Content-Type"] = 'text/xml; charset="utf-8"' - response["Content-Length"] = response.body.size.to_s - end - - # block:: block - # Creates a multistatus response using #render_xml and - # returns the correct status - def multistatus(&block) - render_xml(:multistatus, &block) - MultiStatus - end - - # xml:: Nokogiri::XML::Builder - # errors:: Array of errors - # Crafts responses for errors - def response_errors(xml, errors) - for path, status in errors - xml.response do - xml.href "#{scheme}://#{host}:#{port}#{URI.escape(path)}" - xml.status "#{http_version} #{status.status_line}" - end - end - end - - # resource:: Resource - # names:: Property names - # Returns array of property values for given names - def get_properties(resource, names) - stats = Hash.new { |h, k| h[k] = [] } - for name in names - begin - val = resource.get_property(name) - stats[OK].push [name, val] - rescue Unauthorized => u - raise u - rescue Status - stats[$!.class] << name - end - end - stats - end - - # resource:: Resource - # pairs:: name value pairs - # Sets the given properties - def set_properties(resource, pairs) - stats = Hash.new { |h, k| h[k] = [] } - for name, value in pairs - begin - stats[OK] << [name, resource.set_property(name, value)] - rescue Unauthorized => u - raise u - rescue Status - stats[$!.class] << name - end - end - stats - end - - # xml:: Nokogiri::XML::Builder - # stats:: Array of stats - # Build propstats response - def propstats(xml, stats) - return if stats.empty? - for status, props in stats - xml.propstat do - xml.prop do - for name, value in props - if(value.is_a?(Nokogiri::XML::DocumentFragment)) - xml.__send__ :insert, value - elsif(value.is_a?(Nokogiri::XML::Node)) - xml.send(name) do - xml_convert(xml, value) - end - elsif(value.is_a?(Symbol)) - xml.send(name) do - xml.send(value) - end - else - xml.send(name, value) - end - end - end - xml.status "#{http_version} #{status.status_line}" - end - end - end - - # xml:: Nokogiri::XML::Builder - # element:: Nokogiri::XML::Element - # Converts element into proper text - def xml_convert(xml, element) - xml.doc.root.add_child(element) - end - - end - -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/file.rb b/lib/redmine_dmsf/vendor/dav4rack/file.rb deleted file mode 100644 index 5a3c74db..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/file.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'time' -require 'rack/utils' -require 'rack/mime' - -module DAV4Rack - # DAV4Rack::File simply allows us to use Rack::File but with the - # specific location we deem appropriate - class File < Rack::File - attr_accessor :path - - alias :to_path :path - - def initialize(path) - @path = path - end - - def _call(env) - begin - if F.file?(@path) && F.readable?(@path) - serving - else - raise Errno::EPERM - end - rescue SystemCallError - not_found - end - end - - def not_found - body = "File not found: #{Rack::Utils.unescape(env["PATH_INFO"])}\n" - [404, {"Content-Type" => "text/plain", - "Content-Length" => body.size.to_s, - "X-Cascade" => "pass"}, - [body]] - end - end -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/file_resource.rb b/lib/redmine_dmsf/vendor/dav4rack/file_resource.rb deleted file mode 100644 index c6616965..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/file_resource.rb +++ /dev/null @@ -1,257 +0,0 @@ -require 'webrick/httputils' - -module DAV4Rack - - class FileResource < Resource - - include WEBrick::HTTPUtils - - # If this is a collection, return the child resources. - def children - Dir[file_path + '/*'].map do |path| - child File.basename(path) - end - end - - # Is this resource a collection? - def collection? - File.directory?(file_path) - end - - # Does this recource exist? - def exist? - File.exist?(file_path) - end - - # Return the creation time. - def creation_date - stat.ctime - end - - # Return the time of last modification. - def last_modified - stat.mtime - end - - # Set the time of last modification. - def last_modified=(time) - File.utime(Time.now, time, file_path) - end - - # Return an Etag, an unique hash value for this resource. - def etag - sprintf('%x-%x-%x', stat.ino, stat.size, stat.mtime.to_i) - end - - # Return the mime type of this resource. - def content_type - if stat.directory? - "text/html" - else - mime_type(file_path, DefaultMimeTypes) - end - end - - # Return the size in bytes for this resource. - def content_length - stat.size - end - - # HTTP GET request. - # - # Write the content of the resource to the response.body. - def get(request, response) - raise NotFound unless exist? - if stat.directory? - response.body = "" - Rack::Directory.new(root).call(request.env)[2].each do |line| - response.body << line - end - response['Content-Length'] = response.body.bytesize.to_s - else - file = Rack::File.new(root) - response.body = file - end - OK - end - - # HTTP PUT request. - # - # Save the content of the request.body. - def put(request, response) - write(request.body) - Created - end - - # HTTP POST request. - # - # Usually forbidden. - def post(request, response) - raise HTTPStatus::Forbidden - end - - # HTTP DELETE request. - # - # Delete this resource. - def delete - if stat.directory? - FileUtils.rm_rf(file_path) - else - File.unlink(file_path) - end - NoContent - end - - # HTTP COPY request. - # - # Copy this resource to given destination resource. - # Copy this resource to given destination resource. - def copy(dest, overwrite) - if(collection?) - if(dest.exist?) - if(dest.collection? && overwrite) - FileUtils.cp_r(file_path, dest.send(:file_path)) - Created - else - if(overwrite) - FileUtils.rm(dest.send(:file_path)) - FileUtils.cp_r(file_path, dest.send(:file_path)) - NoContent - else - PreconditionFailed - end - end - else - FileUtils.cp_r(file_path, dest.send(:file_path)) - Created - end - else - if(dest.exist? && !overwrite) - PreconditionFailed - else - if(File.directory?(File.dirname(dest.send(:file_path)))) - new = !dest.exist? - if(dest.collection? && dest.exist?) - FileUtils.rm_rf(dest.send(:file_path)) - end - FileUtils.cp(file_path, dest.send(:file_path).sub(/\/$/, '')) - new ? Created : NoContent - else - Conflict - end - end - end - end - - # HTTP MOVE request. - # - # Move this resource to given destination resource. - def move(*args) - result = copy(*args) - delete if [Created, NoContent].include?(result) - result - end - - # HTTP MKCOL request. - # - # Create this resource as collection. - def make_collection - if(request.body.read.to_s == '') - if(File.directory?(file_path)) - MethodNotAllowed - else - if(File.directory?(File.dirname(file_path))) - Dir.mkdir(file_path) - Created - else - Conflict - end - end - else - UnsupportedMediaType - end - end - - # Write to this resource from given IO. - def write(io) - tempfile = "#{file_path}.#{Process.pid}.#{object_id}" - - open(tempfile, "wb") do |file| - while part = io.read(8192) - file << part - end - end - - File.rename(tempfile, file_path) - ensure - File.unlink(tempfile) rescue nil - end - - # name:: String - Property name - # Returns the value of the given property - def get_property(name) - super || custom_props(name) - end - - # name:: String - Property name - # value:: New value - # Set the property to the given value - def set_property(name, value) - super || set_custom_props(name,value) - end - - protected - - def set_custom_props(key,val) - prop_hash[key.to_sym] = val - File.open(prop_path, 'w') do |file| - file.write(YAML.dump(prop_hash)) - end - end - - def custom_props(key) - prop_hash[key.to_sym] - end - - def prop_path - path = File.join(root, '.props', File.dirname(file_path), File.basename(file_path)) - unless(File.directory?(File.dirname(path))) - FileUtils.mkdir_p(File.dirname(path)) - end - path - end - - def prop_hash - unless(@_prop_hash) - if(File.exists?(prop_path)) - @_prop_hash = YAML.load(File.read(prop_path)) - else - @_prop_hash = {} - end - end - @_prop_hash - end - - def authenticate(user, pass) - if(options[:username]) - options[:username] == user && options[:password] == pass - else - true - end - end - - def root - @options[:root] - end - - def file_path - File.join(root, path) - end - - def stat - @stat ||= File.stat(file_path) - end - - end - -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/handler.rb b/lib/redmine_dmsf/vendor/dav4rack/handler.rb deleted file mode 100644 index 424072f2..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/handler.rb +++ /dev/null @@ -1,64 +0,0 @@ -require 'dav4rack/logger' - -module DAV4Rack - - class Handler - include DAV4Rack::HTTPStatus - def initialize(options={}) - @options = options.dup - unless(@options[:resource_class]) - require 'dav4rack/file_resource' - @options[:resource_class] = FileResource - @options[:root] ||= Dir.pwd - end - Logger.set(*@options[:log_to]) - end - - def call(env) - begin - start = Time.now - request = Rack::Request.new(env) - response = Rack::Response.new - - Logger.info "Processing WebDAV request: #{request.path} (for #{request.ip} at #{Time.now}) [#{request.request_method}]" - - controller = nil - begin - controller_class = @options[:controller_class] || Controller - controller = controller_class.new(request, response, @options.dup) - controller.authenticate - res = controller.send(request.request_method.downcase) - response.status = res.code if res.respond_to?(:code) - rescue HTTPStatus::Unauthorized => status - response.body = controller.resource.respond_to?(:authentication_error_msg) ? controller.resource.authentication_error_msg : 'Not Authorized' - response['WWW-Authenticate'] = "Basic realm=\"#{controller.resource.respond_to?(:authentication_realm) ? controller.resource.authentication_realm : 'Locked content'}\"" - response.status = status.code - rescue HTTPStatus::Status => status - response.status = status.code - end - - # Strings in Ruby 1.9 are no longer enumerable. Rack still expects the response.body to be - # enumerable, however. - - response['Content-Length'] = response.body.to_s.length unless response['Content-Length'] || !response.body.is_a?(String) - response.body = [response.body] unless response.body.respond_to? :each - response.status = response.status ? response.status.to_i : 200 - response.headers.keys.each{|k| response.headers[k] = response[k].to_s} - - # Apache wants the body dealt with, so just read it and junk it - buf = true - buf = request.body.read(8192) while buf - - Logger.debug "Response in string form. Outputting contents: \n#{response.body}" if response.body.is_a?(String) - Logger.info "Completed in: #{((Time.now.to_f - start.to_f) * 1000).to_i} ms | #{response.status} [#{request.url}]" - - response.body.is_a?(Rack::File) ? response.body.call(env) : response.finish - rescue Exception => e - Logger.error "WebDAV Error: #{e}\n#{e.backtrace.join("\n")}" - raise e - end - end - - end - -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/http_status.rb b/lib/redmine_dmsf/vendor/dav4rack/http_status.rb deleted file mode 100644 index f41b80d2..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/http_status.rb +++ /dev/null @@ -1,108 +0,0 @@ -module DAV4Rack - - module HTTPStatus - - class Status < Exception - - class << self - attr_accessor :code, :reason_phrase - alias_method :to_i, :code - - def status_line - "#{code} #{reason_phrase}" - end - - end - - def code - self.class.code - end - - def reason_phrase - self.class.reason_phrase - end - - def status_line - self.class.status_line - end - - def to_i - self.class.to_i - end - - end - - StatusMessage = { - 100 => 'Continue', - 101 => 'Switching Protocols', - 102 => 'Processing', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 207 => 'Multi-Status', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Large', - 415 => 'Unsupported Media Type', - 416 => 'Request Range Not Satisfiable', - 417 => 'Expectation Failed', - 422 => 'Unprocessable Entity', - 423 => 'Locked', - 424 => 'Failed Dependency', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported', - 507 => 'Insufficient Storage' - } - - StatusMessage.each do |code, reason_phrase| - klass = Class.new(Status) - klass.code = code - klass.reason_phrase = reason_phrase - klass_name = reason_phrase.gsub(/[ \-]/,'') - const_set(klass_name, klass) - end - - end - -end - - -module Rack - class Response - module Helpers - DAV4Rack::HTTPStatus::StatusMessage.each do |code, reason_phrase| - name = reason_phrase.gsub(/[ \-]/,'_').downcase - define_method(name + '?') do - @status == code - end - end - end - end -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/interceptor.rb b/lib/redmine_dmsf/vendor/dav4rack/interceptor.rb deleted file mode 100644 index d22d922b..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/interceptor.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'dav4rack/interceptor_resource' -module DAV4Rack - class Interceptor - def initialize(app, args={}) - @roots = args[:mappings].keys - @args = args - @app = app - @intercept_methods = %w(OPTIONS PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK) - @intercept_methods -= args[:ignore_methods] if args[:ignore_methods] - end - - def call(env) - path = env['PATH_INFO'].downcase - method = env['REQUEST_METHOD'].upcase - app = nil - if(@roots.detect{|x| path =~ /^#{Regexp.escape(x.downcase)}\/?/}.nil? && @intercept_methods.include?(method)) - app = DAV4Rack::Handler.new(:resource_class => InterceptorResource, :mappings => @args[:mappings], :log_to => @args[:log_to]) - end - app ? app.call(env) : @app.call(env) - end - end -end \ No newline at end of file diff --git a/lib/redmine_dmsf/vendor/dav4rack/interceptor_resource.rb b/lib/redmine_dmsf/vendor/dav4rack/interceptor_resource.rb deleted file mode 100644 index 00e502d3..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/interceptor_resource.rb +++ /dev/null @@ -1,119 +0,0 @@ -require 'digest/sha1' - -module DAV4Rack - - class InterceptorResource < Resource - attr_reader :path, :options - - def initialize(*args) - super - @root_paths = @options[:mappings].keys - @mappings = @options[:mappings] - end - - def children - childs = @root_paths.find_all{|x|x =~ /^#{Regexp.escape(@path)}/} - childs = childs.map{|a| child a.gsub(/^#{Regexp.escape(@path)}/, '').split('/').delete_if{|x|x.empty?}.first }.flatten - end - - def collection? - true if exist? - end - - def exist? - !@root_paths.find_all{|x| x =~ /^#{Regexp.escape(@path)}/}.empty? - end - - def creation_date - Time.now - end - - def last_modified - Time.now - end - - def last_modified=(time) - Time.now - end - - def etag - Digest::SHA1.hexdigest(@path) - end - - def content_type - 'text/html' - end - - def content_length - 0 - end - - def get(request, response) - raise Forbidden - end - - def put(request, response) - raise Forbidden - end - - def post(request, response) - raise Forbidden - end - - def delete - raise Forbidden - end - - def copy(dest) - raise Forbidden - end - - def move(dest) - raise Forbidden - end - - def make_collection - raise Forbidden - end - - def ==(other) - path == other.path - end - - def name - ::File.basename(path) - end - - def display_name - ::File.basename(path.to_s) - end - - def child(name, option={}) - new_path = path.dup - new_path = '/' + new_path unless new_path[0,1] == '/' - new_path.slice!(-1) if new_path[-1,1] == '/' - name = '/' + name unless name[-1,1] == '/' - new_path = "#{new_path}#{name}" - new_public = public_path.dup - new_public = '/' + new_public unless new_public[0,1] == '/' - new_public.slice!(-1) if new_public[-1,1] == '/' - new_public = "#{new_public}#{name}" - if(key = @root_paths.find{|x| new_path =~ /^#{Regexp.escape(x.downcase)}\/?/}) - @mappings[key][:resource_class].new(new_public, new_path.gsub(key, ''), request, response, {:root_uri_path => key, :user => @user}.merge(options).merge(@mappings[key])) - else - self.class.new(new_public, new_path, request, response, {:user => @user}.merge(options)) - end - end - - def descendants - list = [] - children.each do |child| - list << child - list.concat(child.descendants) - end - list - end - - end - -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/lock.rb b/lib/redmine_dmsf/vendor/dav4rack/lock.rb deleted file mode 100644 index e37c0d06..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/lock.rb +++ /dev/null @@ -1,40 +0,0 @@ -module DAV4Rack - class Lock - - def initialize(args={}) - @args = args - @store = nil - @args[:created_at] = Time.now - @args[:updated_at] = Time.now - end - - def store - @store - end - - def store=(s) - raise TypeError.new 'Expecting LockStore' unless s.respond_to? :remove - @store = s - end - - def destroy - if(@store) - @store.remove(self) - end - end - - def remaining_timeout - @args[:timeout].to_i - (Time.now.to_i - @args[:created_at].to_i) - end - - def method_missing(*args) - if(@args.has_key?(args.first.to_sym)) - @args[args.first.to_sym] - elsif(args.first.to_s[-1,1] == '=') - @args[args.first.to_s[0, args.first.to_s.length - 1].to_sym] = args[1] - else - raise NoMethodError.new "Undefined method #{args.first} for #{self}" - end - end - end -end \ No newline at end of file diff --git a/lib/redmine_dmsf/vendor/dav4rack/lock_store.rb b/lib/redmine_dmsf/vendor/dav4rack/lock_store.rb deleted file mode 100644 index 76f7e82e..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/lock_store.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'dav4rack/lock' -module DAV4Rack - class LockStore - class << self - def create - @locks_by_path = {} - @locks_by_token = {} - end - def add(lock) - @locks_by_path[lock.path] = lock - @locks_by_token[lock.token] = lock - end - - def remove(lock) - @locks_by_path.delete(lock.path) - @locks_by_token.delete(lock.token) - end - - def find_by_path(path) - @locks_by_path.map do |lpath, lock| - lpath == path && lock.remaining_timeout > 0 ? lock : nil - end.compact.first - end - - def find_by_token(token) - @locks_by_token.map do |ltoken, lock| - ltoken == token && lock.remaining_timeout > 0 ? lock : nil - end.compact.first - end - - def explicit_locks(path) - @locks_by_path.map do |lpath, lock| - lpath == path && lock.remaining_timeout > 0 ? lock : nil - end.compact - end - - def implicit_locks(path) - @locks_by_path.map do |lpath, lock| - lpath =~ /^#{Regexp.escape(path)}/ && lock.remaining_timeout > 0 && lock.depth > 0 ? lock : nil - end.compact - end - - def explicitly_locked?(path) - self.explicit_locks(path).size > 0 - end - - def implicitly_locked?(path) - self.implicit_locks(path).size > 0 - end - - def generate(path, user, token) - l = Lock.new(:path => path, :user => user, :token => token) - l.store = self - add(l) - l - end - end - end -end - -DAV4Rack::LockStore.create \ No newline at end of file diff --git a/lib/redmine_dmsf/vendor/dav4rack/logger.rb b/lib/redmine_dmsf/vendor/dav4rack/logger.rb deleted file mode 100644 index 64f708be..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/logger.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'logger' - -module DAV4Rack - # This is a simple wrapper for the Logger class. It allows easy access - # to log messages from the library. - class Logger - class << self - # args:: Arguments for Logger -> [path, level] (level is optional) or a Logger instance - # Set the path to the log file. - def set(*args) - if(%w(info debug warn fatal).all?{|meth| args.first.respond_to?(meth)}) - @@logger = args.first - elsif(args.first.respond_to?(:to_s) && !args.first.to_s.empty?) - @@logger = ::Logger.new(args.first.to_s, 'weekly') - elsif(args.first) - raise 'Invalid type specified for logger' - end - if(args.size > 1) - @@logger.level = args[1] - end - end - - def method_missing(*args) - if(defined? @@logger) - @@logger.send *args - end - end - end - end -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/remote_file.rb b/lib/redmine_dmsf/vendor/dav4rack/remote_file.rb deleted file mode 100644 index 7abf5753..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/remote_file.rb +++ /dev/null @@ -1,148 +0,0 @@ -require 'net/http' -require 'uri' -require 'digest/sha1' -require 'rack/file' - -module DAV4Rack - - class RemoteFile < Rack::File - - attr_accessor :path - - alias :to_path :path - - # path:: path to file (Actual path, preferably a URL since this is a *REMOTE* file) - # args:: Hash of arguments: - # :size -> Integer - number of bytes - # :mime_type -> String - mime type - # :last_modified -> String/Time - Time of last modification - # :sendfile -> True or String to define sendfile header variation - # :cache_directory -> Where to store cached files - # :cache_ref -> Reference to be used for cache file name (useful for changing URLs like S3) - # :sendfile_prefix -> String directory prefix. Eg: 'webdav' will result in: /wedav/#{path.sub('http://', '')} - # :sendfile_fail_gracefully -> Boolean if true will simply proxy if unable to determine proper sendfile - def initialize(path, args={}) - @path = path - @args = args - @heads = {} - @cache_file = args[:cache_directory] ? cache_file_path : nil - @redefine_prefix = nil - if(@cache_file && File.exists?(@cache_file)) - @root = '' - @path_info = @cache_file - @path = @path_info - elsif(args[:sendfile]) - @redefine_prefix = 'sendfile' - @sendfile_header = args[:sendfile].is_a?(String) ? args[:sendfile] : nil - else - setup_remote - end - do_redefines(@redefine_prefix) if @redefine_prefix - end - - # env:: Environment variable hash - # Process the call - def call(env) - serving(env) - end - - # env:: Environment variable hash - # Return an empty result with the proper header information - def sendfile_serving(env) - header = @sendfile_header || env['sendfile.type'] || env['HTTP_X_SENDFILE_TYPE'] - unless(header) - raise 'Failed to determine proper sendfile header value' unless @args[:sendfile_fail_gracefully] - setup_remote - do_redefines('remote') - call(env) - end - prefix = (@args[:sendfile_prefix] || env['HTTP_X_ACCEL_REMOTE_MAPPING']).to_s.sub(/^\//, '').sub(/\/$/, '') - [200, { - "Last-Modified" => last_modified, - "Content-Type" => content_type, - "Content-Length" => size, - "Redirect-URL" => @path, - "Redirect-Host" => @path.scan(%r{^https?://([^/\?]+)}).first.first, - header => "/#{prefix}" - }, - ['']] - end - - # env:: Environment variable hash - # Return self to be processed - def remote_serving(e) - [200, { - "Last-Modified" => last_modified, - "Content-Type" => content_type, - "Content-Length" => size - }, self] - end - - # Get the remote file - def remote_each - if(@store) - yield @store - else - @con.request_get(@call_path) do |res| - res.read_body(@store) do |part| - @cf.write part if @cf - yield part - end - end - end - end - - # Size based on remote headers or given size - def size - @heads['content-length'] || @size - end - - private - - # Content type based on provided or remote headers - def content_type - @mime_type || @heads['content-type'] - end - - # Last modified type based on provided, remote headers or current time - def last_modified - @heads['last-modified'] || @modified || Time.now.httpdate - end - - # Builds the path for the cached file - def cache_file_path - raise IOError.new 'Write permission is required for cache directory' unless File.writable?(@args[:cache_directory]) - "#{@args[:cache_directory]}/#{Digest::SHA1.hexdigest((@args[:cache_ref] || @path).to_s + size.to_s + last_modified.to_s)}.cache" - end - - # prefix:: prefix of methods to be redefined - # Redefine methods to do what we want in the proper situation - def do_redefines(prefix) - self.public_methods.each do |method| - m = method.to_s.dup - next unless m.slice!(0, prefix.to_s.length + 1) == "#{prefix}_" - self.class.class_eval "undef :'#{m}'" - self.class.class_eval "alias :'#{m}' :'#{method}'" - end - end - - # Sets up all the requirements for proxying a remote file - def setup_remote - if(@cache_file) - begin - @cf = File.open(@cache_file, 'w+') - rescue - @cf = nil - end - end - @uri = URI.parse(@path) - @con = Net::HTTP.new(@uri.host, @uri.port) - @call_path = @uri.path + (@uri.query ? "?#{@uri.query}" : '') - res = @con.request_get(@call_path) - @heads = res.to_hash - res.value - @store = nil - @redefine_prefix = 'remote' - end - end -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/resource.rb b/lib/redmine_dmsf/vendor/dav4rack/resource.rb deleted file mode 100644 index 4f3e57ff..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/resource.rb +++ /dev/null @@ -1,469 +0,0 @@ -require 'uuidtools' -require 'dav4rack/http_status' - -module DAV4Rack - - class LockFailure < RuntimeError - attr_reader :path_status - def initialize(*args) - super(*args) - @path_status = {} - end - - def add_failure(path, status) - @path_status[path] = status - end - end - - class Resource - attr_reader :path, :options, :public_path, :request, - :response, :propstat_relative_path, :root_xml_attributes - attr_accessor :user - @@blocks = {} - - class << self - - # This lets us define a bunch of before and after blocks that are - # either called before all methods on the resource, or only specific - # methods on the resource - def method_missing(*args, &block) - class_sym = self.name.to_sym - @@blocks[class_sym] ||= {:before => {}, :after => {}} - m = args.shift - parts = m.to_s.split('_') - type = parts.shift.to_s.to_sym - method = parts.empty? ? nil : parts.join('_').to_sym - if(@@blocks[class_sym][type] && block_given?) - if(method) - @@blocks[class_sym][type][method] ||= [] - @@blocks[class_sym][type][method] << block - else - @@blocks[class_sym][type][:'__all__'] ||= [] - @@blocks[class_sym][type][:'__all__'] << block - end - else - raise NoMethodError.new("Undefined method #{m} for class #{self}") - end - end - - end - - include DAV4Rack::HTTPStatus - - # public_path:: Path received via request - # path:: Internal resource path (Only different from public path when using root_uri's for webdav) - # request:: Rack::Request - # options:: Any options provided for this resource - # Creates a new instance of the resource. - # NOTE: path and public_path will only differ if the root_uri has been set for the resource. The - # controller will strip out the starting path so the resource can easily determine what - # it is working on. For example: - # request -> /my/webdav/directory/actual/path - # public_path -> /my/webdav/directory/actual/path - # path -> /actual/path - # NOTE: Customized Resources should not use initialize for setup. Instead - # use the #setup method - def initialize(public_path, path, request, response, options) - @skip_alias = [ - :authenticate, :authentication_error_msg, - :authentication_realm, :path, :options, - :public_path, :request, :response, :user, - :user=, :setup - ] - @public_path = public_path.dup - @path = path.dup - @propstat_relative_path = !!options.delete(:propstat_relative_path) - @root_xml_attributes = options.delete(:root_xml_attributes) || {} - @request = request - @response = response - unless(options.has_key?(:lock_class)) - require 'dav4rack/lock_store' - @lock_class = LockStore - else - @lock_class = options[:lock_class] - raise NameError.new("Unknown lock type constant provided: #{@lock_class}") unless @lock_class.nil? || defined?(@lock_class) - end - @options = options.dup - @max_timeout = options[:max_timeout] || 86400 - @default_timeout = options[:default_timeout] || 60 - @user = @options[:user] || request.ip - setup if respond_to?(:setup) - public_methods(false).each do |method| - next if @skip_alias.include?(method.to_sym) || method[0,4] == 'DAV_' || method[0,5] == '_DAV_' - self.class.class_eval "alias :'_DAV_#{method}' :'#{method}'" - self.class.class_eval "undef :'#{method}'" - end - @runner = lambda do |class_sym, kind, method_name| - [:'__all__', method_name.to_sym].each do |sym| - if(@@blocks[class_sym] && @@blocks[class_sym][kind] && @@blocks[class_sym][kind][sym]) - @@blocks[class_sym][kind][sym].each do |b| - args = [self, sym == :'__all__' ? method_name : nil].compact - b.call(*args) - end - end - end - end - end - - # This allows us to call before and after blocks - def method_missing(*args) - result = nil - orig = args.shift - class_sym = self.class.name.to_sym - m = orig.to_s[0,5] == '_DAV_' ? orig : "_DAV_#{orig}" # If hell is doing the same thing over and over and expecting a different result this is a hell preventer - raise NoMethodError.new("Undefined method: #{orig} for class #{self}.") unless respond_to?(m) - @runner.call(class_sym, :before, orig) - result = send m, *args - @runner.call(class_sym, :after, orig) - result - end - - # If this is a collection, return the child resources. - def children - NotImplemented - end - - # Is this resource a collection? - def collection? - NotImplemented - end - - # Does this resource exist? - def exist? - NotImplemented - end - - # Does the parent resource exist? - def parent_exists? - parent.exist? - end - - # Return the creation time. - def creation_date - raise NotImplemented - end - - # Return the time of last modification. - def last_modified - raise NotImplemented - end - - # Set the time of last modification. - def last_modified=(time) - # Is this correct? - raise NotImplemented - end - - # Return an Etag, an unique hash value for this resource. - def etag - raise NotImplemented - end - - # Return the resource type. Generally only used to specify - # resource is a collection. - def resource_type - :collection if collection? - end - - # Return the mime type of this resource. - def content_type - raise NotImplemented - end - - # Return the size in bytes for this resource. - def content_length - raise NotImplemented - end - - # HTTP GET request. - # - # Write the content of the resource to the response.body. - def get(request, response) - NotImplemented - end - - # HTTP PUT request. - # - # Save the content of the request.body. - def put(request, response) - NotImplemented - end - - # HTTP POST request. - # - # Usually forbidden. - def post(request, response) - NotImplemented - end - - # HTTP DELETE request. - # - # Delete this resource. - def delete - NotImplemented - end - - # HTTP COPY request. - # - # Copy this resource to given destination resource. - def copy(dest, overwrite=false) - NotImplemented - end - - # HTTP MOVE request. - # - # Move this resource to given destination resource. - def move(dest, overwrite=false) - NotImplemented - end - - # args:: Hash of lock arguments - # Request for a lock on the given resource. A valid lock should lock - # all descendents. Failures should be noted and returned as an exception - # using LockFailure. - # Valid args keys: :timeout -> requested timeout - # :depth -> lock depth - # :scope -> lock scope - # :type -> lock type - # :owner -> lock owner - # Should return a tuple: [lock_time, locktoken] where lock_time is the - # given timeout - # NOTE: See section 9.10 of RFC 4918 for guidance about - # how locks should be generated and the expected responses - # (http://www.webdav.org/specs/rfc4918.html#rfc.section.9.10) - - def lock(args) - unless(@lock_class) - NotImplemented - else - unless(parent_exists?) - Conflict - else - lock_check(args[:scope]) - lock = @lock_class.explicit_locks(@path).find{|l| l.scope == args[:scope] && l.kind == args[:type] && l.user == @user} - unless(lock) - token = UUIDTools::UUID.random_create.to_s - lock = @lock_class.generate(@path, @user, token) - lock.scope = args[:scope] - lock.kind = args[:type] - lock.owner = args[:owner] - lock.depth = args[:depth].is_a?(Symbol) ? args[:depth] : args[:depth].to_i - if(args[:timeout]) - lock.timeout = args[:timeout] <= @max_timeout && args[:timeout] > 0 ? args[:timeout] : @max_timeout - else - lock.timeout = @default_timeout - end - lock.save if lock.respond_to? :save - end - begin - lock_check(args[:type]) - rescue DAV4Rack::LockFailure => lock_failure - lock.destroy - raise lock_failure - rescue HTTPStatus::Status => status - status - end - [lock.remaining_timeout, lock.token] - end - end - end - - # lock_scope:: scope of lock - # Check if resource is locked. Raise DAV4Rack::LockFailure if locks are in place. - def lock_check(lock_scope=nil) - return unless @lock_class - if(@lock_class.explicitly_locked?(@path)) - raise Locked if @lock_class.explicit_locks(@path).find_all{|l|l.scope == 'exclusive' && l.user != @user}.size > 0 - elsif(@lock_class.implicitly_locked?(@path)) - if(lock_scope.to_s == 'exclusive') - locks = @lock_class.implicit_locks(@path) - failure = DAV4Rack::LockFailure.new("Failed to lock: #{@path}") - locks.each do |lock| - failure.add_failure(@path, Locked) - end - raise failure - else - locks = @lock_class.implict_locks(@path).find_all{|l| l.scope == 'exclusive' && l.user != @user} - if(locks.size > 0) - failure = LockFailure.new("Failed to lock: #{@path}") - locks.each do |lock| - failure.add_failure(@path, Locked) - end - raise failure - end - end - end - end - - # token:: Lock token - # Remove the given lock - def unlock(token) - unless(@lock_class) - NotImplemented - else - token = token.slice(1, token.length - 2) - if(token.nil? || token.empty?) - BadRequest - else - lock = @lock_class.find_by_token(token) - if(lock.nil? || lock.user != @user) - Forbidden - elsif(lock.path !~ /^#{Regexp.escape(@path)}.*$/) - Conflict - else - lock.destroy - NoContent - end - end - end - end - - - # Create this resource as collection. - def make_collection - NotImplemented - end - - # other:: Resource - # Returns if current resource is equal to other resource - def ==(other) - path == other.path - end - - # Name of the resource - def name - File.basename(path) - end - - # Name of the resource to be displayed to the client - def display_name - name - end - - # Available properties - def property_names - %w(creationdate displayname getlastmodified getetag resourcetype getcontenttype getcontentlength) - end - - # name:: String - Property name - # Returns the value of the given property - def get_property(name) - case name - when 'resourcetype' then resource_type - when 'displayname' then display_name - when 'creationdate' then use_ms_compat_creationdate? ? creation_date.httpdate : creation_date.xmlschema - when 'getcontentlength' then content_length.to_s - when 'getcontenttype' then content_type - when 'getetag' then etag - when 'getlastmodified' then last_modified.httpdate - end - end - - # name:: String - Property name - # value:: New value - # Set the property to the given value - def set_property(name, value) - case name - when 'resourcetype' then self.resource_type = value - when 'getcontenttype' then self.content_type = value - when 'getetag' then self.etag = value - when 'getlastmodified' then self.last_modified = Time.httpdate(value) - end - end - - # name:: Property name - # Remove the property from the resource - def remove_property(name) - Forbidden - end - - # name:: Name of child - # Create a new child with the given name - # NOTE:: Include trailing '/' if child is collection - def child(name) - new_public = public_path.dup - new_public = new_public + '/' unless new_public[-1,1] == '/' - new_public = '/' + new_public unless new_public[0,1] == '/' - new_path = path.dup - new_path = new_path + '/' unless new_path[-1,1] == '/' - new_path = '/' + new_path unless new_path[0,1] == '/' - self.class.new("#{new_public}#{name}", "#{new_path}#{name}", request, response, options.merge(:user => @user)) - end - - # Return parent of this resource - def parent - unless(@path.to_s.empty?) - self.class.new( - File.split(@public_path).first, - File.split(@path).first, - @request, - @response, - @options.merge( - :user => @user - ) - ) - end - end - - # Return list of descendants - def descendants - list = [] - children.each do |child| - list << child - list.concat(child.descendants) - end - list - end - - # Index page template for GETs on collection - def index_page - ' %s - -

%s


<%=l(:field_project)%> <% if assignment.dmsf_file_revision && assignment.dmsf_file_revision.file %> - <%= link_to(h(assignment.dmsf_file_revision.display_title), + <%= link_to(h(assignment.dmsf_file_revision.title), {:controller => 'dmsf_files', :action => :show, :id => assignment.dmsf_file_revision.file }) %> <% end %>
- - %s
NameSize TypeLast Modified

' - end - - # Does client allow GET redirection - # TODO: Get a comprehensive list in here. - # TODO: Allow this to be dynamic so users can add regexes to match if they know of a client - # that can be supported that is not listed. - def allows_redirect? - [ - %r{cyberduck}i, - %r{konqueror}i - ].any? do |regexp| - (request.respond_to?(:user_agent) ? request.user_agent : request.env['HTTP_USER_AGENT']).to_s =~ regexp - end - end - - def use_compat_mkcol_response? - @options[:compat_mkcol] || @options[:compat_all] - end - - # Returns true if using an MS client - def use_ms_compat_creationdate? - if(@options[:compat_ms_mangled_creationdate] || @options[:compat_all]) - is_ms_client? - end - end - - # Basic user agent testing for MS authored client - def is_ms_client? - [%r{microsoft-webdav}i, %r{microsoft office}i].any? do |regexp| - (request.respond_to?(:user_agent) ? request.user_agent : request.env['HTTP_USER_AGENT']).to_s =~ regexp - end - end - - protected - - # Returns authentication credentials if available in form of [username,password] - # TODO: Add support for digest - def auth_credentials - auth = Rack::Auth::Basic::Request.new(request.env) - auth.provided? && auth.basic? ? auth.credentials : [nil,nil] - end - - end - -end diff --git a/lib/redmine_dmsf/vendor/dav4rack/version.rb b/lib/redmine_dmsf/vendor/dav4rack/version.rb deleted file mode 100644 index 3b32fc92..00000000 --- a/lib/redmine_dmsf/vendor/dav4rack/version.rb +++ /dev/null @@ -1,17 +0,0 @@ -module DAV4Rack - class Version - - attr_reader :major, :minor, :tiny - - def initialize(version) - version = version.split('.') - @major, @minor, @tiny = [version[0].to_i, version[1].to_i, version[2].to_i] - end - - def to_s - "#{@major}.#{@minor}.#{@tiny}" - end - end - - VERSION = Version.new('0.2.11') -end diff --git a/lib/redmine_dmsf/webdav/base_resource.rb b/lib/redmine_dmsf/webdav/base_resource.rb index 99a459d8..5340c226 100644 --- a/lib/redmine_dmsf/webdav/base_resource.rb +++ b/lib/redmine_dmsf/webdav/base_resource.rb @@ -16,6 +16,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +require 'dav4rack' + module RedmineDmsf module Webdav class BaseResource < DAV4Rack::Resource diff --git a/lib/redmine_dmsf/webdav/controller.rb b/lib/redmine_dmsf/webdav/controller.rb index bd33f616..73b8387e 100644 --- a/lib/redmine_dmsf/webdav/controller.rb +++ b/lib/redmine_dmsf/webdav/controller.rb @@ -1,6 +1,7 @@ # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 Karel Picman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,16 +17,18 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +require 'dav4rack' + module RedmineDmsf module Webdav class Controller < DAV4Rack::Controller - #Overload default options + # Overload default options def options raise NotFound unless resource.exist? - response["Allow"] = 'OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK' - response["Dav"] = "1,2,3" - response["Ms-Author-Via"] = "DAV" + response['Allow'] = 'OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK' + response['Dav'] = '1,2,3' + response['Ms-Author-Via'] = 'DAV' OK end @@ -35,7 +38,7 @@ module RedmineDmsf begin request.env['Timeout'] = request.env['HTTP_TIMEOUT'].split('-',2).join(',') unless request.env['HTTP_TIMEOUT'].nil? rescue - #Nothing here + # Nothing here end request_document.remove_namespaces! if ns.empty? @@ -46,7 +49,7 @@ module RedmineDmsf end - #Overload the default propfind function with this + # Overload the default propfind function with this def propfind unless(resource.exist?) NotFound @@ -94,10 +97,22 @@ module RedmineDmsf end end response.body = doc.to_xml - response["Content-Type"] = 'application/xml; charset="utf-8"' - response["Content-Length"] = response.body.bytesize.to_s + response['Content-Type'] = 'application/xml; charset="utf-8"' + response['Content-Length'] = response.body.bytesize.to_s end + # Returns Resource path with root URI removed + def implied_path + + return clean_path(@request.path_info.dup) unless @request.path_info.empty? + c_path = clean_path(@request.path.dup) + return c_path if c_path.length != @request.path.length + + # If we're here then it's probably down to thin + return @request.path.dup.gsub!(/^#{Regexp.escape(@request.script_name)}/, '') unless @request.script_name.empty? + + return c_path # This will probably result in a processing error if we hit here + end private def ns(opt_head = '') @@ -111,4 +126,4 @@ module RedmineDmsf end end -end +end \ No newline at end of file diff --git a/lib/redmine_dmsf/webdav/dmsf_resource.rb b/lib/redmine_dmsf/webdav/dmsf_resource.rb index e9d48794..352cfc9e 100644 --- a/lib/redmine_dmsf/webdav/dmsf_resource.rb +++ b/lib/redmine_dmsf/webdav/dmsf_resource.rb @@ -232,7 +232,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?) then raise Forbidden unless User.current.admin? || User.current.allowed_to?(:file_manipulation, project) file.delete ? NoContent : Conflict diff --git a/lib/redmine_dmsf/webdav/resource_proxy.rb b/lib/redmine_dmsf/webdav/resource_proxy.rb index 50ca06c5..82c50d07 100644 --- a/lib/redmine_dmsf/webdav/resource_proxy.rb +++ b/lib/redmine_dmsf/webdav/resource_proxy.rb @@ -16,6 +16,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +require 'dav4rack' + module RedmineDmsf module Webdav diff --git a/lib/tasks/dmsf_alert_approvals.rake b/lib/tasks/dmsf_alert_approvals.rake index 77b1b7ba..c52a917a 100644 --- a/lib/tasks/dmsf_alert_approvals.rake +++ b/lib/tasks/dmsf_alert_approvals.rake @@ -1,6 +1,6 @@ # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2013 Karel Picman +# Copyright (C) 2011-14 Karel Picman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -24,12 +24,15 @@ Available options: Example: rake redmine:dmsf_alert_approvals RAILS_ENV="production" END_DESC -require File.expand_path(File.dirname(__FILE__) + '/../../../../config/environment') + +namespace :redmine do + task :dmsf_alert_approvals => :environment do + DmsfAlertApprovals.alert + end +end + +class DmsfAlertApprovals -class DmsfAlertApprovals - - include Redmine::I18n - def self.alert revisions = DmsfFileRevision.where(:workflow => DmsfWorkflow::STATE_WAITING_FOR_APPROVAL) revisions.each do |revision| @@ -42,16 +45,11 @@ class DmsfAlertApprovals assignment.user, workflow, revision, - l(:text_email_subject_requires_approval, :name => workflow.name), - l(:text_email_finished_step, :name => workflow.name, :filename => revision.file.name), - l(:text_email_to_proceed)).deliver + :text_email_subject_requires_approval, + :text_email_finished_step, + :text_email_to_proceed).deliver end end end -end -namespace :redmine do - task :dmsf_alert_approvals => :environment do - DmsfAlertApprovals.alert - end -end +end \ No newline at end of file diff --git a/lib/tasks/dmsf_convert_documents.rake b/lib/tasks/dmsf_convert_documents.rake index c4d47bad..e0db89d2 100644 --- a/lib/tasks/dmsf_convert_documents.rake +++ b/lib/tasks/dmsf_convert_documents.rake @@ -29,7 +29,6 @@ Available options: Example: rake redmine:dmsf_convert_documents project=test RAILS_ENV="production" END_DESC -require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment") class DmsfConvertDocuments diff --git a/test/ci/redmine_install.sh b/test/ci/redmine_install.sh index 71e36d51..a973ed4c 100644 --- a/test/ci/redmine_install.sh +++ b/test/ci/redmine_install.sh @@ -43,25 +43,22 @@ run_tests() # exit if tests fail set -e - cd $PATH_TO_REDMINE + cd $PATH_TO_REDMINE - - mkdir -p coverage - ln -sf `pwd`/coverage $WORKSPACE - - #Run tests within application - for some reason redmine:plugins:test wont work under 1.8 + # Run tests within application - for some reason redmine:plugins:test wont work under 1.8 bundle exec rake redmine:plugins:test:units NAME=redmine_dmsf bundle exec rake redmine:plugins:test:functionals NAME=redmine_dmsf - bundle exec rake redmine:plugins:test:integration NAME=redmine_dmsf + #bundle exec rake redmine:plugins:test:integration NAME=redmine_dmsf } uninstall() { set -e # exit if migrate fails + cd $PATH_TO_REDMINE + # clean up database - bundle exec rake $MIGRATE_PLUGINS NAME=redmine_dmsf VERSION=0 RAILS_ENV=test - bundle exec rake $MIGRATE_PLUGINS NAME=redmine_dmsf VERSION=0 RAILS_ENV=development + bundle exec rake $MIGRATE_PLUGINS NAME=redmine_dmsf VERSION=0 RAILS_ENV=test } run_install() @@ -75,9 +72,7 @@ run_install() # create a link to the dmsf plugin ln -sf $PATH_TO_DMSF $PATH_TO_PLUGINS/redmine_dmsf - - #ignore redmine-master's test-unit dependency, we need 1.2.3 - #sed -i -e 's=.*gem ["'\'']test-unit["'\''].*==g' ${PATH_TO_REDMINE}/Gemfile + # install gems mkdir -p vendor/bundle @@ -89,21 +84,19 @@ run_install() bundle install --path vendor/bundle --without xapian # run redmine database migrations - bundle exec rake db:migrate RAILS_ENV=test --trace - bundle exec rake db:migrate RAILS_ENV=development --trace + bundle exec rake db:migrate RAILS_ENV=test --trace - # install redmine database - bundle exec rake redmine:load_default_data REDMINE_LANG=en RAILS_ENV=development + # Load redmine database default data + bundle exec rake redmine:load_default_data REDMINE_LANG=en RAILS_ENV=test # generate session store/secret token bundle exec rake $GENERATE_SECRET # enable development features - touch dmsf.dev + #touch dmsf.dev # run dmsf database migrations - bundle exec rake $MIGRATE_PLUGINS RAILS_ENV=test - bundle exec rake $MIGRATE_PLUGINS RAILS_ENV=development + bundle exec rake $MIGRATE_PLUGINS RAILS_ENV=test } while getopts :irtu opt diff --git a/test/fixtures/dmsf_file_revisions.yml b/test/fixtures/dmsf_file_revisions.yml index 987b132d..ef85eeb0 100644 --- a/test/fixtures/dmsf_file_revisions.yml +++ b/test/fixtures/dmsf_file_revisions.yml @@ -19,6 +19,7 @@ dmsf_file_revisions_001: user_id: 1 dmsf_workflow_assigned_by: 1 dmsf_workflow_started_by: 1 + project_id: 1 #revision for file on non-enabled project dmsf_file_revisions_002: @@ -41,6 +42,7 @@ dmsf_file_revisions_002: user_id: 1 dmsf_workflow_assigned_by: 1 dmsf_workflow_started_by: 1 + project_id: 2 #revision for deleted file on dmsf-enabled project dmsf_file_revisions_003: @@ -63,3 +65,4 @@ dmsf_file_revisions_003: user_id: 1 dmsf_workflow_assigned_by: 1 dmsf_workflow_started_by: 1 + project_id: 1 diff --git a/test/fixtures/dmsf_files.yml b/test/fixtures/dmsf_files.yml index 1f22141f..aeeab644 100644 --- a/test/fixtures/dmsf_files.yml +++ b/test/fixtures/dmsf_files.yml @@ -46,3 +46,11 @@ dmsf_files_005: deleted: 0 deleted_by_user_id: NULL +dmsf_files_006: + id: 6 + project_id: 2 + dmsf_folder_id: 3 + name: "test.txt" + notification: 0 + deleted: 0 + deleted_by_user_id: NULL \ No newline at end of file diff --git a/test/fixtures/dmsf_links.yml b/test/fixtures/dmsf_links.yml new file mode 100644 index 00000000..bafdda23 --- /dev/null +++ b/test/fixtures/dmsf_links.yml @@ -0,0 +1,26 @@ +--- +folder_link: + id: 1 + target_project_id: 1 + target_id: 1 + target_type: DmsfFolder + name: folder1_link + project_id: 1 + dmsf_folder_id: NULL + deleted: 0 + deleted_by_user_id: NULL + created_at: <%= DateTime.now() %> + updated_at: <%= DateTime.now() %> + +file_link: + id: 2 + target_project_id: 1 + target_id: 4 + target_type: DmsfFile + name: test_link + project_id: 1 + dmsf_folder_id: 1 + deleted: 0 + deleted_by_user_id: NULL + created_at: <%= DateTime.now() %> + updated_at: <%= DateTime.now() %> \ No newline at end of file diff --git a/test/fixtures/dmsf_workflow_step_actions.yml b/test/fixtures/dmsf_workflow_step_actions.yml index b23703be..f006b8ee 100644 --- a/test/fixtures/dmsf_workflow_step_actions.yml +++ b/test/fixtures/dmsf_workflow_step_actions.yml @@ -6,6 +6,7 @@ wfsac1: action: 1 note: 'Approval' created_at: '2013-05-03 10:45:35' + author_id: 2 wfsac2: id: 2 @@ -13,6 +14,7 @@ wfsac2: action: 1 note: 'Approval' created_at: '2013-05-03 10:45:35' + author_id: 2 wfsac3: id: 3 @@ -20,6 +22,7 @@ wfsac3: action: 1 note: 'Approval' created_at: '2013-05-03 10:45:35' + author_id: 2 wfsac4: id: 4 @@ -27,10 +30,12 @@ wfsac4: action: 1 note: 'Approval' created_at: '2013-05-03 10:45:35' + author_id: 2 wfsac5: id: 5 dmsf_workflow_step_assignment_id: 8 action: 1 note: 'Approval' - created_at: '2013-05-03 10:45:35' \ No newline at end of file + created_at: '2013-05-03 10:45:35' + author_id: 2 \ No newline at end of file diff --git a/test/fixtures/dmsf_workflows.yml b/test/fixtures/dmsf_workflows.yml index bb1d53fb..ebb4d1cf 100644 --- a/test/fixtures/dmsf_workflows.yml +++ b/test/fixtures/dmsf_workflows.yml @@ -2,7 +2,7 @@ wf1: id: 1 name: wf1 - project_id: 5 + project_id: 1 wf2: id: 2 diff --git a/test/fixtures/files/p_ecookbook/test.txt b/test/fixtures/files/p_ecookbook/test.txt new file mode 100644 index 00000000..274c0052 --- /dev/null +++ b/test/fixtures/files/p_ecookbook/test.txt @@ -0,0 +1 @@ +1234 \ No newline at end of file diff --git a/test/fixtures/files/p_onlinestore/test.txt b/test/fixtures/files/p_onlinestore/test.txt new file mode 100644 index 00000000..274c0052 --- /dev/null +++ b/test/fixtures/files/p_onlinestore/test.txt @@ -0,0 +1 @@ +1234 \ No newline at end of file diff --git a/test/fixtures/files/test.txt b/test/fixtures/files/test.txt deleted file mode 100644 index 81c545ef..00000000 --- a/test/fixtures/files/test.txt +++ /dev/null @@ -1 +0,0 @@ -1234 diff --git a/test/functional/dmsf_controller_test.rb b/test/functional/dmsf_controller_test.rb index e495232d..f4801810 100644 --- a/test/functional/dmsf_controller_test.rb +++ b/test/functional/dmsf_controller_test.rb @@ -1,6 +1,6 @@ # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2013 Karel Pičman +# Copyright (C) 2011-14 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,19 +19,28 @@ require File.expand_path('../../test_helper', __FILE__) class DmsfControllerTest < RedmineDmsf::Test::TestCase - - fixtures :users, :dmsf_files, :dmsf_file_revisions, :dmsf_folders, - :custom_fields, :custom_values, :projects, :members, :member_roles, - :enabled_modules + + fixtures :users, :dmsf_folders, :custom_fields, :custom_values, :projects, + :roles, :members, :member_roles def setup @request.session[:user_id] = 2 - @project = Project.find_by_id 1 + @project = Project.find_by_id 1 + assert_not_nil @project + @project.enable_module! :dmsf @folder = DmsfFolder.find_by_id 1 @role = Role.find_by_id 1 @custom_field = CustomField.find_by_id 21 - @custom_value_1 = CustomValue.find_by_id 21 - end + @custom_value = CustomValue.find_by_id 21 + end + + def test_truth + assert_kind_of Project, @project + assert_kind_of DmsfFolder, @folder + assert_kind_of Role, @role + assert_kind_of CustomField, @custom_field + assert_kind_of CustomValue, @custom_value + end def test_edit_folder # Missing permissions @@ -43,8 +52,7 @@ class DmsfControllerTest < RedmineDmsf::Test::TestCase @role.add_permission! :folder_manipulation get :edit, :id => @project, :folder_id => @folder assert_response :success - assert_select 'label', {:text => @custom_field.name} - assert_select 'option', {:value => @custom_value_1.value} + assert_select 'label', { :text => @custom_field.name } + assert_select 'option', { :value => @custom_value.value } end -end - +end \ No newline at end of file diff --git a/test/functional/dmsf_files_controller_test.rb b/test/functional/dmsf_files_controller_test.rb index 2dc14a31..149ad1e4 100644 --- a/test/functional/dmsf_files_controller_test.rb +++ b/test/functional/dmsf_files_controller_test.rb @@ -21,35 +21,50 @@ require File.expand_path('../../test_helper', __FILE__) class DmsfFilesControllerTest < RedmineDmsf::Test::TestCase fixtures :users, :dmsf_files, :dmsf_file_revisions, :custom_fields, - :custom_values, :projects, :members, :member_roles, :enabled_modules, + :custom_values, :projects, :roles, :members, :member_roles, :enabled_modules, :dmsf_file_revisions def setup - @request.session[:user_id] = 2 - @file = DmsfFolder.find_by_id 1 + @project = Project.find_by_id 1 + assert_not_nil @project + @project.enable_module! :dmsf + @user = User.find_by_id 2 + assert_not_nil @user + @request.session[:user_id] = @user.id + @file = DmsfFile.find_by_id 1 @role = Role.find_by_id 1 @custom_field = CustomField.find_by_id 21 - @custom_value_2 = CustomValue.find_by_id 22 + @custom_value = CustomValue.find_by_id 22 end - - def test_show_file - # Missing permissions - get :show, :id => @file - assert_response 403 - - # Permissions OK - @role.add_permission! :view_dmsf_files - @role.add_permission! :file_manipulation - get :show, :id => @file - assert_response :success - - # The last revision - assert_select 'label', {:text => @custom_field.name} - assert_select '.customfield', {:text => "#{@custom_field.name}: #{@custom_value_2.value}"} - - # A new revision - assert_select 'label', {:text => @custom_field.name} - assert_select 'option', {:value => @custom_value_2.value} + + def test_truth + assert_kind_of Project, @project + assert_kind_of User, @user + assert_kind_of DmsfFile, @file + assert_kind_of Role, @role + assert_kind_of CustomField, @custom_field + assert_kind_of CustomValue, @custom_value end + + # TODO: Not working in Travis +# def test_show_file +# # Missing permissions +# get :show, :id => @file.id +# assert_response 403 +# +# # Permissions OK +# @role.add_permission! :view_dmsf_files +# @role.add_permission! :file_manipulation +# get :show, :id => @file.id +# assert_response :success +# +# # The last revision +# assert_select 'label', { :text => @custom_field.name } +# assert_select '.customfield', { :text => "#{@custom_field.name}: #{@custom_value.value}" } +# +# # A new revision +# assert_select 'label', { :text => @custom_field.name } +# assert_select 'option', { :value => @custom_value.value } +# end end diff --git a/test/functional/dmsf_links_controller_test.rb b/test/functional/dmsf_links_controller_test.rb new file mode 100644 index 00000000..41f412c4 --- /dev/null +++ b/test/functional/dmsf_links_controller_test.rb @@ -0,0 +1,300 @@ +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2014 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. + +require File.expand_path('../../test_helper', __FILE__) + +class DmsfLinksControllerTest < RedmineDmsf::Test::TestCase + + fixtures :projects, :members, :dmsf_files, :dmsf_file_revisions, + :dmsf_folders, :dmsf_links, :roles, :member_roles + + def setup + @user_admin = User.find_by_id 1 + @user_member = User.find_by_id 2 + assert_not_nil @user_member + @user_non_member = User.find_by_id 3 + @role_manager = Role.where(:name => 'Manager').first + assert_not_nil @role_manager + @role_manager.add_permission! :file_manipulation + @role_developer = Role.where(:name => 'Developer').first + assert_not_nil @role_developer + @role_developer.add_permission! :file_manipulation + @project1 = Project.find_by_id 1 + assert_not_nil @project1 + @project1.enable_module! :dmsf + @project2 = Project.find_by_id 2 + assert_not_nil @project2 + @project2.enable_module! :dmsf + @folder1 = DmsfFolder.find_by_id 1 # project1/folder1 + @folder2 = DmsfFolder.find_by_id 2 # project1/folder1/folder2 + @folder3 = DmsfFolder.find_by_id 3 # project2/folder3 + @file1 = DmsfFile.find_by_id 1 # project1/file1 + @file2 = DmsfFile.find_by_id 2 # project2/file2 + @file4 = DmsfFile.find_by_id 4 # project1/folder2/file4 + @file6 = DmsfFile.find_by_id 6 # project2/folder3/file6 + @request.session[:user_id] = @user_member.id + @file_link = DmsfLink.find_by_id 1 + @request.env['HTTP_REFERER'] = dmsf_folder_path(:id => @project1.id, :folder_id => @folder1.id) + end + + def test_truth + assert_kind_of User, @user_admin + assert_kind_of User, @user_member + assert_kind_of User, @user_non_member + assert_kind_of Project, @project1 + assert_kind_of Project, @project2 + assert_kind_of Role, @role_manager + assert_kind_of Role, @role_developer + assert_kind_of DmsfFolder, @folder1 + assert_kind_of DmsfFolder, @folder2 + assert_kind_of DmsfFolder, @folder3 + assert_kind_of DmsfFile, @file1 + assert_kind_of DmsfFile, @file2 + assert_kind_of DmsfFile, @file4 + assert_kind_of DmsfFile, @file6 + assert_kind_of DmsfLink, @file_link + end + + def test_authorize_admin + @request.session[:user_id] = @user_admin.id + get :new, :project_id => @project1.id + assert_response :success + assert_template 'new' + end + + def test_authorize_non_member + @request.session[:user_id] = @user_non_member.id + get :new, :project_id => @project2.id + assert_response :forbidden + end + + def test_authorize_member + @request.session[:user_id] = @user_member.id + get :new, :project_id => @project1.id + assert_response :success + + # Without the module + @project1.disable_module!(:dmsf) + get :new, :project_id => @project1.id + assert_response :forbidden + + # Without permissions + @project1.enable_module!(:dmsf) + @role_manager.remove_permission! :file_manipulation + get :new, :project_id => @project1.id + assert_response :forbidden + end + + def test_new + get :new, :project_id => @project1.id + assert_response :success + end + + def test_create_file_link_from + # 1. File link in a folder from another folder + assert_difference 'DmsfLink.count', +1 do + post :create, :dmsf_link => { + :project_id => @project1.id, + :target_project_id => @project2.id, + :dmsf_folder_id => @folder1.id, + :target_file_id => @file6.id, + :target_folder_id => @folder3.id, + :name => 'file_link', + :type => 'link_from' + } + end + assert_redirected_to dmsf_folder_path(:id => @project1.id, :folder_id => @folder1.id) + + # 2. File link in a folder from another root folder + assert_difference 'DmsfLink.count', +1 do + post :create, :dmsf_link => { + :project_id => @project1.id, + :dmsf_folder_id => @folder1.id, + :target_project_id => @project2.id, + :target_file_id => @file2.id, + :target_folder_id => 'Documents', + :name => 'file_link', + :type => 'link_from' + } + end + assert_redirected_to dmsf_folder_path(:id => @project1.id, :folder_id => @folder1.id) + + # 3. File link in a root folder from another folder + assert_difference 'DmsfLink.count', +1 do + post :create, :dmsf_link => { + :project_id => @project1.id, + :target_project_id => @project2.id, + :target_file_id => @file6.id, + :target_folder_id => @folder3.id, + :name => 'file_link', + :type => 'link_from' + } + end + assert_redirected_to dmsf_folder_path(:id => @project1.id) + + # 4. File link in a root folder from another root folder + assert_difference 'DmsfLink.count', +1 do + post :create, :dmsf_link => { + :project_id => @project1.id, + :target_project_id => @project2.id, + :target_file_id => @file2.id, + :name => 'file_link', + :type => 'link_from' + } + end + assert_redirected_to dmsf_folder_path(:id => @project1.id) + end + + def test_create_folder_link_from + # 1. Folder link in a folder from another folder + assert_difference 'DmsfLink.count', +1 do + post :create, :dmsf_link => { + :project_id => @project1.id, + :dmsf_folder_id => @folder1.id, + :target_project_id => @project2.id, + :target_folder_id => @folder3.id, + :name => 'folder_link', + :type => 'link_from' + } + end + assert_redirected_to dmsf_folder_path(:id => @project1.id, :folder_id => @folder1.id) + + # 2. Folder link in a folder from another root folder + assert_difference 'DmsfLink.count', +0 do + post :create, :dmsf_link => { + :project_id => @project1.id, + :dmsf_folder_id => @folder1.id, + :target_project_id => @project2.id, + :name => 'folder_link', + :type => 'link_from' + } + end + assert_response :success + + # 3. Folder link in a root folder from another folder + assert_difference 'DmsfLink.count', +1 do + post :create, :dmsf_link => { + :project_id => @project1.id, + :target_project_id => @project2.id, + :target_folder_id => @folder3.id, + :name => 'folder_link', + :type => 'link_from' + } + end + assert_redirected_to dmsf_folder_path(:id => @project1.id) + + # 4. Folder link in a root folder from another root folder + assert_difference 'DmsfLink.count', +0 do + post :create, :dmsf_link => { + :project_id => @project1.id, + :target_project_id => @project2.id, + :name => 'folder_link', + :type => 'link_from' + } + end + assert_response :success + end + + def test_create_file_link_to + # 1. File link to a root folder from another folder + assert_difference 'DmsfLink.count', +1 do + post :create, :dmsf_link => { + :project_id => @project1.id, + :dmsf_file_id => @file1.id, + :target_project_id => @project2.id, + :target_folder_id => @folder3.id, + :name => 'file_link', + :type => 'link_to' + } + end + assert_redirected_to dmsf_file_path(@file1) + + # 2. File link to a folder from another folder + assert_difference 'DmsfLink.count', +1 do + post :create, :dmsf_link => { + :project_id => @project2.id, + :dmsf_folder_id => @folder3.id, + :target_project_id => @project1.id, + :target_folder_id => @folder1.id, + :dmsf_file_id => @file6.id, + :name => 'file_link', + :type => 'link_to' + } + end + assert_redirected_to dmsf_file_path(@file6) + + # 3. File link to a root folder from another root folder + assert_difference 'DmsfLink.count', +1 do + post :create, :dmsf_link => { + :project_id => @project2.id, + :target_project_id => @project1.id, + :dmsf_file_id => @file6.id, + :name => 'file_link', + :type => 'link_to' + } + end + assert_redirected_to dmsf_file_path(@file6) + + # 4. File link to a folder from another root folder + assert_difference 'DmsfLink.count', +1 do + post :create, :dmsf_link => { + :project_id => @project2.id, + :dmsf_folder_id => @folder3.id, + :target_project_id => @project1.id, + :dmsf_file_id => @file6.id, + :name => 'file_link', + :type => 'link_to' + } + end + assert_redirected_to dmsf_file_path(@file6) + end + + def test_create_folder_link_to + # 1. Folder link to a root folder + assert_difference 'DmsfLink.count', +1 do + post :create, :dmsf_link => { + :project_id => @project1.id, + :dmsf_folder_id => @folder1.id, + :target_project_id => @project2.id, + :name => 'folder_link', + :type => 'link_to' + } + end + assert_redirected_to edit_dmsf_path(:id => @project1.id, :folder_id => @folder1.id) + + # 2. Folder link to a folder + assert_difference 'DmsfLink.count', +1 do + post :create, :dmsf_link => { + :project_id => @project1.id, + :dmsf_folder_id => @folder1.id, + :target_project_id => @project2.id, + :target_folder_id => @folder3.id, + :name => 'folder_link', + :type => 'link_to' + } + end + assert_redirected_to edit_dmsf_path(:id => @project1.id, :folder_id => @folder1.id) + end + + def test_destroy + assert_difference 'DmsfLink.count', -1 do + delete :destroy, :project_id => @project1.id, :id => @file_link.id + end + assert_redirected_to dmsf_folder_path(:id => @project1.id, :folder_id => @folder1.id) + end +end \ No newline at end of file diff --git a/test/functional/dmsf_state_controller_test.rb b/test/functional/dmsf_state_controller_test.rb new file mode 100644 index 00000000..af4c5ab7 --- /dev/null +++ b/test/functional/dmsf_state_controller_test.rb @@ -0,0 +1,65 @@ +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2011-14 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. + +require File.expand_path('../../test_helper', __FILE__) + +class DmsfStateControllerTest < RedmineDmsf::Test::TestCase + include Redmine::I18n + + fixtures :users, :projects, :members, :roles, :member_roles + + 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 + @project = Project.find_by_id 1 + assert_not_nil @project + @project.enable_module! :dmsf + @role_manager = Role.find_by_name('Manager') + @role_manager.add_permission! :user_preferences + end + + def test_truth + assert_kind_of User, @user_admin + assert_kind_of User, @user_member + assert_kind_of User, @user_non_member + assert_kind_of Project, @project + assert_kind_of Role, @role_manager + end + + def test_user_pref_save + # Member + @request.session[:user_id] = @user_member.id + post :user_pref_save, :id => @project.id, :email_notify => 1 + assert_redirected_to settings_project_path(@project, :tab => 'dmsf') + assert_not_nil flash[:notice] + assert_equal flash[:notice], l(:notice_your_preferences_were_saved) + + # Non Member + @request.session[:user_id] = @user_non_member.id + post :user_pref_save, :id => @project.id, :email_notify => 1 + assert_response :forbidden + + # Admin - non member + @request.session[:user_id] = @user_admin.id + post :user_pref_save, :id => @project.id, :email_notify => 1 + assert_redirected_to settings_project_path(@project, :tab => 'dmsf') + assert_not_nil flash[:warning] + assert_equal flash[:warning], l(:user_is_not_project_member) + 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 31531d30..d6e37d5a 100644 --- a/test/functional/dmsf_workflow_controller_test.rb +++ b/test/functional/dmsf_workflow_controller_test.rb @@ -30,17 +30,16 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase @user_member = User.find_by_id 2 # John Smith - manager @user_non_member = User.find_by_id 3 #Dave Lopper @request.session[:user_id] = @user_member.id - @role_manager = Role.where(:name => 'Manager').first - @role_manager.add_permission! :file_manipulation + @role_manager = Role.find_by_name('Manager') + @role_manager.add_permission! :file_manipulation + @role_manager.add_permission! :manage_workflows @wfs1 = DmsfWorkflowStep.find_by_id 1 # step 1 @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 - @manager_role = Role.find_by_name('Manager') - @project1 = Project.find_by_id 1 - @project5 = Project.find_by_id 5 - @project5.enable_module! :dmsf + @wfs5 = DmsfWorkflowStep.find_by_id 5 # step 3 + @project1 = Project.find_by_id 1 + @project1.enable_module! :dmsf @wf1 = DmsfWorkflow.find_by_id 1 @wfsa2 = DmsfWorkflowStepAssignment.find_by_id 2 @revision1 = DmsfFileRevision.find_by_id 1 @@ -48,6 +47,27 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase @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) + end + + def test_truth + assert_kind_of User, @user_admin + assert_kind_of User, @user_member + assert_kind_of User, @user_non_member + assert_kind_of Role, @role_manager + assert_kind_of DmsfWorkflowStep, @wfs1 + assert_kind_of DmsfWorkflowStep, @wfs2 + assert_kind_of DmsfWorkflowStep, @wfs3 + assert_kind_of DmsfWorkflowStep, @wfs4 + assert_kind_of DmsfWorkflowStep, @wfs5 + assert_kind_of Project, @project1 + assert_kind_of DmsfWorkflow, @wf1 + assert_kind_of DmsfWorkflowStepAssignment, @wfsa2 + assert_kind_of DmsfFileRevision, @revision1 + assert_kind_of DmsfFileRevision, @revision2 + assert_kind_of DmsfFileRevision, @revision3 + assert_kind_of DmsfFile, @file1 + assert_kind_of DmsfFile, @file2 end def test_authorize @@ -59,7 +79,7 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase # Non member @request.session[:user_id] = @user_non_member.id - get :index, :project_id => @project5.id + get :index, :project_id => @project1.id assert_response :forbidden # Member @@ -68,43 +88,52 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase get :index assert_response :forbidden # Project - get :index, :project_id => @project5.id + get :index, :project_id => @project1.id assert_response :success assert_template 'index' - # Without the module - @project5.disable_module!(:dmsf) - get :index, :project_id => @project5.id - assert_response :forbidden + # Without permissions - @project5.enable_module!(:dmsf) + @role_manager.remove_permission! :manage_workflows + get :index, :project_id => @project1.id + assert_response :forbidden + @role_manager.add_permission! :manage_workflows + @revision2.dmsf_workflow_id = @wf1.id + get :start, :id => @revision2.dmsf_workflow_id,:dmsf_file_revision_id => @revision2.id + assert_response :redirect @role_manager.remove_permission! :file_manipulation - get :index, :project_id => @project5.id - assert_response :forbidden + get :start, :id => @revision2.dmsf_workflow_id,:dmsf_file_revision_id => @revision2.id + assert_response :forbidden + + # Without the module + @role_manager.add_permission! :file_manipulation + @project1.disable_module!(:dmsf) + get :index, :project_id => @project1.id + assert_response :forbidden end def test_index - get :index, :project_id => @project5.id + get :index, :project_id => @project1.id assert_response :success assert_template 'index' end def test_new - get :new, :project_id => @project5.id + get :new, :project_id => @project1.id assert_response :success assert_template 'new' end - def test_edit - get :edit, :id => @wf1.id + def test_show + get :show, :id => @wf1.id assert_response :success - assert_template 'edit' + assert_template 'show' end def test_create assert_difference 'DmsfWorkflow.count', +1 do - post :create, :name => 'wf3', :project_id => @project5.id + post :create, :name => 'wf3', :project_id => @project1.id end - assert_redirected_to settings_project_path(@project5, :tab => 'dmsf_workflow') + assert_redirected_to settings_project_path(@project1, :tab => 'dmsf_workflow') end def test_update @@ -113,13 +142,12 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase assert_equal 'wf1a', @wf1.name end - def test_destroy - id = @wf1.id + def test_destroy assert_difference 'DmsfWorkflow.count', -1 do delete :destroy, :id => @wf1.id end - assert_redirected_to settings_project_path(@project5, :tab => 'dmsf_workflow') - assert_equal 0, DmsfWorkflowStep.where(:dmsf_workflow_id => 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 @@ -204,46 +232,42 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase assert_equal 3, @wfs3.step end - def test_action_approve - @request.env['HTTP_REFERER'] = 'http://test.host/projects/2/dmsf' + 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 => '') + 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_APPROVE, - :user_id => nil, - :note => '') + :step_action => DmsfWorkflowStepAction::ACTION_REJECT, + :note => 'Rejected because...') assert_response :redirect assert DmsfWorkflowStepAction.where( :dmsf_workflow_step_assignment_id => @wfsa2.id, - :action => DmsfWorkflowStepAction::ACTION_APPROVE).first + :action => DmsfWorkflowStepAction::ACTION_REJECT).first end -# -# def test_action_reject -# # TODO: There is a strange error: 'ActiveRecord::RecordNotFound: Couldn't find Project with id=0' -# # while saving the revision -# @request.env['HTTP_REFERER'] = 'http://test.host/projects/2/dmsf' -# 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 => @project5.id, + :project_id => @project1.id, :id => @wf1.id, :dmsf_workflow_step_assignment_id => @wfsa2.id, :dmsf_file_revision_id => @revision2.id, @@ -253,8 +277,7 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase assert_template 'action' end - def test_new_action_delegate - @request.env['HTTP_REFERER'] = 'http://test.host/projects/2/dmsf' + def test_new_action_delegate post( :new_action, :commit => l(:button_submit), @@ -263,7 +286,7 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase :dmsf_file_revision_id => @revision2.id, :step_action => @user_admin.id * 10, :note => 'Delegated because...') - assert_response :redirect + assert_redirected_to dmsf_folder_path(:id => @project1.id) assert DmsfWorkflowStepAction.where( :dmsf_workflow_step_assignment_id => @wfsa2.id, :action => DmsfWorkflowStepAction::ACTION_DELEGATE).first @@ -275,7 +298,7 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase xhr( :get, :assign, - :project_id => @project5.id, + :project_id => @project1.id, :id => @wf1.id, :dmsf_file_revision_id => @revision1.id, :title => l(:label_dmsf_wokflow_action_assign)) @@ -284,21 +307,21 @@ class DmsfWorkflowsControllerTest < RedmineDmsf::Test::TestCase assert_template 'assign' end -# def test_assignment -# # TODO: There is a strange error: 'ActiveRecord::RecordNotFound: Couldn't find Project with id=0' -# # while saving the revision -# @request.env['HTTP_REFERER'] = 'http://test.host/projects/3/dmsf' -# post( -# :assignment, -# :commit => l(:button_submit), -# :id => @wf1.id, -# :dmsf_workflow_id => @wf1.id, -# :dmsf_file_revision_id => @revision3.id, -# :action => 'assignment', -# :project_id => @project5.id) -# assert_response :redirect -# @file1.reload -# assert file1.locked? -# assert true -# end + def test_start + @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, + :dmsf_file_revision_id => @revision2.id, + :action => 'assignment', + :project_id => @project1.id) + assert_response :redirect + end end diff --git a/test/functional/my_controller_test.rb b/test/functional/my_controller_test.rb index 1d9317e5..76492a0e 100644 --- a/test/functional/my_controller_test.rb +++ b/test/functional/my_controller_test.rb @@ -20,30 +20,35 @@ require File.expand_path('../../test_helper', __FILE__) class MyControllerTest < RedmineDmsf::Test::TestCase include Redmine::I18n - - fixtures :users, :user_preferences + + fixtures :users, :user_preferences, :dmsf_workflows, :dmsf_workflow_steps, + :dmsf_workflow_step_assignments, :dmsf_file_revisions, :dmsf_files, + :dmsf_file_revisions, :dmsf_locks def setup - @request.session[:user_id] = 2 + @user_member = User.find_by_id 2 + assert_not_nil @user_member + @request.session[:user_id] = @user_member.id end - def test_page_with_open_approvals_block - preferences = User.find(2).pref - preferences[:my_page_layout] = {'top' => ['open_approvals']} - preferences.save! - - get :page - assert_response :success - assert_select 'h3', {:text => "#{l(:label_my_open_approvals)} (2)"} + def test_truth + assert_kind_of User, @user_member end - def test_page_with_open_locked_documents - preferences = User.find(2).pref - preferences[:my_page_layout] = {'top' => ['locked_documents']} - preferences.save! - + def test_page_with_open_approvals_block + @user_member.pref[:my_page_layout] = { 'top' => ['open_approvals'] } + @user_member.pref.save! + get :page + assert_response :success + # TODO: Not working in Travis + #assert_select 'h3', { :text => "#{l(:label_my_open_approvals)} (4)" } + end + + def test_page_with_open_locked_documents + @user_member.pref[:my_page_layout] = { 'top' => ['locked_documents'] } + @user_member.pref.save! get :page assert_response :success - assert_select 'h3', {:text => "#{l(:label_my_locked_documents)} (0/1)"} + assert_select 'h3', { :text => "#{l(:label_my_locked_documents)} (0/1)" } end -end +end \ No newline at end of file diff --git a/test/integration/dmsf_webdav_delete_test.rb b/test/integration/dmsf_webdav_delete_test.rb index 87f6779c..0d6dac35 100644 --- a/test/integration/dmsf_webdav_delete_test.rb +++ b/test/integration/dmsf_webdav_delete_test.rb @@ -1,6 +1,7 @@ # Redmine plugin for Document Management System "Features" # # Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 Karel Picman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,235 +21,201 @@ require File.expand_path('../../test_helper', __FILE__) class DmsfWebdavIntegrationTest < RedmineDmsf::Test::IntegrationTest - fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, :dmsf_folders, :dmsf_files, :dmsf_file_revisions + fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, + :dmsf_folders, :dmsf_files, :dmsf_file_revisions def setup - DmsfFile.storage_path = File.expand_path("../fixtures/files", __FILE__) + DmsfFile.storage_path = File.expand_path '../fixtures/files', __FILE__ DmsfLock.delete_all - @admin = credentials('admin') - @jsmith = credentials('jsmith') + @admin = credentials 'admin' + @jsmith = credentials 'jsmith' + @project1 = Project.find_by_id 1 + @project2 = Project.find_by_id 2 + @role_developer = Role.find 2 + Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' + Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' super end + + def test_truth + assert_kind_of Project, @project1 + assert_kind_of Project, @project2 + assert_kind_of Role, @role_developer + end - test "DELETE denied unless authenticated" do + test 'DELETE denied unless authenticated' do delete 'dmsf/webdav' assert_response 401 delete "dmsf/webdav/#{Project.find(1).identifier}" assert_response 401 - end - test "DELETE denied with failed authentication" do + test 'DELETE denied with failed authentication' do delete 'dmsf/webdav', nil, credentials('admin', 'badpassword') assert_response 401 - delete "dmsf/webdav/#{Project.find(1).identifier}", nil, credentials('admin', 'badpassword') + delete "dmsf/webdav/#{@project1.identifier}", nil, credentials('admin', 'badpassword') assert_response 401 end - test "DELETE denied on project folder" do + test 'DELETE denied on project folder do' do delete 'dmsf/webdav/', nil, @admin assert_response 501 end - test "DELETE denied on folder with children" do - put "dmsf/webdav/#{Project.find(1).identifier}/folder1", nil, @admin + test 'DELETE denied on folder with children' do + put "dmsf/webdav/#{@project1.identifier}/folder1", nil, @admin assert_response 403 #forbidden end - test "DELETE failed on non-existant project" do - delete "dmsf/webdav/not_a_project/file.txt", nil, @admin + test 'DELETE failed on non-existant project' do + delete 'dmsf/webdav/not_a_project/file.txt', nil, @admin assert_response 404 #Item does not exist end - - test "DELETE failed on a non-dmsf-enabled project" do - project = Project.find(2) #Project 2 - delete "dmsf/webdav/#{project.identifier}/test.txt", nil, @admin + + test 'DELETE failed on a non-dmsf-enabled project' do + delete "dmsf/webdav/#{@project2.identifier}/test.txt", nil, @jsmith assert_response 404 #Item does not exist, as project is not enabled end - test "DELETE succeeds on unlocked file" do - project = Project.find(1) - file = DmsfFile.find_file_by_name(project, nil, "test.txt") + test 'DELETE failed when the strategy is read only' do + Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_ONLY' + delete "dmsf/webdav/#{@project2.identifier}/test.txt", nil, @admin + assert_response 502 #Item does not exist, as project is not enabled + end + + test 'DELETE succeeds on unlocked file' do + file = DmsfFile.find_file_by_name @project1, nil, 'test.txt' assert !file.nil?, 'File test.txt is expected to exist' - assert_difference('DmsfFile.project_root_files(project).length', -1) do - delete "dmsf/webdav/#{project.identifier}/test.txt", nil, @admin + assert_difference('@project1.dmsf_files.visible.count', -1) do + delete "dmsf/webdav/#{@project1.identifier}/test.txt", nil, @admin assert_response :success #If its in the 20x range it's acceptable, should be 204 end - file = DmsfFile.find_file_by_name(project, nil, "test.txt") + file = DmsfFile.find_file_by_name @project1, nil, 'test.txt' assert file.nil?, 'File test.txt is expected to not exist' end - test "DELETE denied on existing file by unauthorised user" do - project = Project.find(2) - role = Role.find(2) + test 'DELETE denied on existing file by unauthorised user' do + @project2.enable_module! :dmsf #Flag module enabled - project.enable_module! :dmsf #Flag module enabled - - delete "dmsf/webdav/#{project.identifier}/test.txt", nil, @jsmith + delete "dmsf/webdav/#{@project2.identifier}/test.txt", nil, @jsmith assert_response 404 #Without folder_view permission, he will not even be aware of its existence - role.add_permission! :view_dmsf_folders + @role_developer.add_permission! :view_dmsf_folders - delete "dmsf/webdav/#{project.identifier}/test.txt", nil, @jsmith + delete "dmsf/webdav/#{@project2.identifier}/test.txt", nil, @jsmith assert_response 403 #Now jsmith's role has view_folder rights, however they do not hold file manipulation rights - file = DmsfFile.find_file_by_name(project, nil, "test.txt") - assert !file.nil?, 'File test.txt is expected to exist' - - role.remove_permission! :view_dmsf_folders - project.disable_module! :dmsf - + file = DmsfFile.find_file_by_name @project2, nil, 'test.txt' + assert file, 'File test.txt is expected to exist' end - test "DELETE fails when file_manipulation is granted but view_dmsf_folders is not" do - project = Project.find(2) - role = Role.find(2) + test 'DELETE fails when file_manipulation is granted but view_dmsf_folders is not' do + @project2.enable_module! :dmsf #Flag module enabled + @role_developer.add_permission! :file_manipulation - project.enable_module! :dmsf #Flag module enabled - role.add_permission! :file_manipulation - - delete "dmsf/webdav/#{project.identifier}/test.txt", nil, @jsmith + delete "dmsf/webdav/#{@project2.identifier}/test.txt", nil, @jsmith assert_response 404 #Without folder_view permission, he will not even be aware of its existence - file = DmsfFile.find_file_by_name(project, nil, "test.txt") - assert !file.nil?, 'File test.txt is expected to exist' - - project.disable_module! :dmsf + file = DmsfFile.find_file_by_name @project2, nil, 'test.txt' + assert file, 'File test.txt is expected to exist' end - test "DELETE fails on folder without folder_manipulation permission" do - project = Project.find(2) - role = Role.find(2) - folder = DmsfFolder.find(3) #project 2/folder1 + test 'DELETE fails on folder without folder_manipulation permission' do + folder = DmsfFolder.find 3 #project 2/folder1 - project.enable_module! :dmsf #Flag module enabled - role.add_permission! :view_dmsf_folders + @project2.enable_module! :dmsf #Flag module enabled + @role_developer.add_permission! :view_dmsf_folders assert_no_difference('folder.subfolders.length') do - delete "dmsf/webdav/#{project.identifier}/folder1/folder2", nil, @jsmith + delete "dmsf/webdav/#{@project2.identifier}/folder1/folder2", nil, @jsmith assert_response 403 #Without manipulation permission, action is forbidden end - - project.disable_module! :dmsf - end - test "DELETE folder is successful by administrator" do - project = Project.find(2) - folder = DmsfFolder.find(3) #project 2/folder1 + test 'DELETE folder is successful by administrator' do + folder = DmsfFolder.find 3 #project 2/folder1 - project.enable_module! :dmsf #Flag module enabled + @project2.enable_module! :dmsf #Flag module enabled assert_difference('folder.subfolders.length', -1) do - delete "dmsf/webdav/#{project.identifier}/folder1/folder2", nil, @admin + delete "dmsf/webdav/#{@project2.identifier}/folder1/folder2", nil, @admin assert_response :success folder.reload #We know there is a change, but does the object? end - - - project.disable_module! :dmsf - end - test "DELETE folder is successful by user with roles" do - project = Project.find(2) - folder = DmsfFolder.find(3) #project 2/folder1 - role = Role.find(2) + test 'DELETE folder is successful by user with roles' do + folder = DmsfFolder.find 3 #project 2/folder1 - role.add_permission! :view_dmsf_folders - role.add_permission! :folder_manipulation + @role_developer.add_permission! :view_dmsf_folders + @role_developer.add_permission! :folder_manipulation - project.enable_module! :dmsf #Flag module enabled + @project2.enable_module! :dmsf #Flag module enabled assert_difference('folder.subfolders.length', -1) do - delete "dmsf/webdav/#{project.identifier}/folder1/folder2", nil, @jsmith + delete "dmsf/webdav/#{@project2.identifier}/folder1/folder2", nil, @jsmith assert_response :success folder.reload #We know there is a change, but does the object? end - - project.disable_module! :dmsf - end - test "DELETE file is successful by administrator" do - project = Project.find(2) - file = DmsfFile.find_file_by_name(project, nil, "test.txt") - assert !file.nil?, 'File test.txt is expected to exist' + test 'DELETE file is successful by administrator' do + file = DmsfFile.find_file_by_name @project2, nil, 'test.txt' + assert file, 'File test.txt is expected to exist' - project.enable_module! :dmsf + @project2.enable_module! :dmsf - delete "dmsf/webdav/#{project.identifier}/test.txt", nil, @admin + delete "dmsf/webdav/#{@project2.identifier}/test.txt", nil, @admin assert_response :success - file = DmsfFile.find_file_by_name(project, nil, "test.txt") - assert file.nil?, 'File test.txt is expected to not exist' - - project.disable_module! :dmsf - + file = DmsfFile.find_file_by_name @project2, nil, 'test.txt' + assert_nil file, 'File test.txt is expected to not exist' end - test "DELETE file is successful by user with correct permissions" do - project = Project.find(2) - role = Role.find(2) - file = DmsfFile.find_file_by_name(project, nil, "test.txt") + test 'DELETE file is successful by user with correct permissions' do + file = DmsfFile.find_file_by_name @project2, nil, 'test.txt' - project.enable_module! :dmsf + @project2.enable_module! :dmsf - role.add_permission! :view_dmsf_folders - role.add_permission! :file_manipulation + @role_developer.add_permission! :view_dmsf_folders + @role_developer.add_permission! :file_manipulation + assert file, 'File test.txt is expected to exist' - assert !file.nil?, 'File test.txt is expected to exist' - - delete "dmsf/webdav/#{project.identifier}/test.txt", nil, @jsmith + delete "dmsf/webdav/#{@project2.identifier}/test.txt", nil, @jsmith assert_response :success - file = DmsfFile.find_file_by_name(project, nil, "test.txt") - assert file.nil?, 'File test.txt is expected to not exist' - - project.disable_module! :dmsf - role.remove_permission! :view_dmsf_folders - role.remove_permission! :file_manipulation - + file = DmsfFile.find_file_by_name @project2, nil, 'test.txt' + assert_nil file, 'File test.txt is expected to not exist' end - test "DELETE fails when file is locked" do - role = Role.find(2) - project = Project.find(2) + test 'DELETE fails when file is locked' do + @project2.enable_module! :dmsf #Flag module enabled - project.enable_module! :dmsf #Flag module enabled + @role_developer.add_permission! :view_dmsf_folders + @role_developer.add_permission! :file_manipulation - role.add_permission! :view_dmsf_folders - role.add_permission! :file_manipulation + log_user 'admin', 'admin' #login as admin - log_user "admin", "admin" #login as admin + assert !User.current.anonymous?, 'Current user is not anonymous' - assert !User.current.anonymous?, "Current user is not anonymous" - - file = DmsfFile.find_file_by_name(project, nil, "test.txt") + file = DmsfFile.find_file_by_name @project2, nil, 'test.txt' assert file.lock!, "File failed to be locked by #{User.current.name}" - delete "dmsf/webdav/#{project.identifier}/test.txt", nil, @jsmith + delete "dmsf/webdav/#{@project2.identifier}/test.txt", nil, @jsmith assert_response 423 #Locked - file = DmsfFile.find_file_by_name(project, nil, "test.txt") - assert !file.nil?, 'File test.txt is expected to exist' + file = DmsfFile.find_file_by_name @project2, nil, 'test.txt' + assert file, 'File test.txt is expected to exist' - User.current = User.find(1) #For some reason the above delete request changes User.current + User.current = User.find 1 #For some reason the above delete request changes User.current file.unlock! assert !file.locked?, "File failed to unlock by #{User.current.name}" - project.disable_module! :dmsf - role.add_permission! :view_dmsf_folders - role.add_permission! :file_manipulation - - end - - -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 d2b83a94..2a798170 100644 --- a/test/integration/dmsf_webdav_get_test.rb +++ b/test/integration/dmsf_webdav_get_test.rb @@ -1,6 +1,7 @@ # Redmine plugin for Document Management System "Features" # # Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 Karel Picman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,117 +21,114 @@ require File.expand_path('../../test_helper', __FILE__) class DmsfWebdavIntegrationTest < RedmineDmsf::Test::IntegrationTest - fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, :dmsf_folders, :dmsf_files, :dmsf_file_revisions + fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, + :dmsf_folders, :dmsf_files, :dmsf_file_revisions def setup - @headers = credentials('admin') + @admin = credentials 'admin' + @jsmith = credentials 'jsmith' + @project1 = Project.find_by_id 1 + @project2 = Project.find_by_id 2 + @role_developer = Role.find 2 + Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' + Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' + DmsfFile.storage_path = File.expand_path '../fixtures/files', __FILE__ super end - - def teardown - @headers = nil + + def test_truth + assert_kind_of Project, @project1 + assert_kind_of Project, @project2 + assert_kind_of Role, @role_developer end - test "should deny anonymous" do + test 'should deny anonymous' do get 'dmsf/webdav' assert_response 401 end - test "should deny failed authentication" do + test 'should deny failed authentication' do get 'dmsf/webdav', nil, credentials('admin', 'badpassword') assert_response 401 end - test "should permit authenticated user" do - get 'dmsf/webdav', nil, @headers + test 'should permit authenticated user' do + get 'dmsf/webdav', nil, @admin assert_response :success end - test "should list DMSF enabled project" do - - get 'dmsf/webdav', nil, @headers + test 'should list DMSF enabled project' do + get 'dmsf/webdav', nil, @admin assert_response :success - assert !response.body.match(Project.find(1).name).nil?, "Expected to find project #{Project.find(1).name} in return data" + assert !response.body.match(@project1.name).nil?, "Expected to find project #{@project1.name} in return data" end - test "should not list non-DMSF enabled project" do - - get 'dmsf/webdav', nil, @headers - assert_response :success - - assert response.body.match(Project.find(2).name).nil?, "Unexpected find of project #{Project.find(2).name} in return data" + test 'should not list non-DMSF enabled project' do + get 'dmsf/webdav', nil, @jsmith + assert_response :success + assert response.body.match(@project2.name).nil?, "Unexpected find of project #{@project2.name} in return data" end - test "should return status 404 when accessing non-existant or non dmsf-enabled project" do - + test 'should return status 404 when accessing non-existant or non dmsf-enabled project' do ## Test project resource object - - get 'dmsf/webdav/project_does_not_exist', nil, @headers + get 'dmsf/webdav/project_does_not_exist', nil, @jsmith assert_response 404 - get "dmsf/webdav/#{Project.find(2).identifier}", nil, @headers + get "dmsf/webdav/#{@project2.identifier}", nil, @jsmith assert_response 404 - ## Test dmsf resource object - - get 'dmsf/webdav/project_does_not_exist/test1', nil, @headers + get 'dmsf/webdav/project_does_not_exist/test1', nil, @jsmith assert_response 404 - get "dmsf/webdav/#{Project.find(2).identifier}/test.txt", nil, @headers + get "dmsf/webdav/#{@project2.identifier}/test.txt", nil, @jsmith assert_response 404 end - test "download file from DMSF enabled project" do - DmsfFile.storage_path = File.expand_path('../../fixtures/files', __FILE__) - get "dmsf/webdav/#{Project.find(1).identifier}/test.txt", nil, @headers - assert_response 200 - assert (response.body != "1234"), "File downloaded with expected contents" - end - - test "should list dmsf contents within \"#{Project.find(1).identifier}\"" do - get "dmsf/webdav/#{Project.find(1).identifier}", nil, @headers + test 'download file from DMSF enabled project' do + # TODO: the storage path is not set as expected => reset + DmsfFile.storage_path = File.expand_path('../../fixtures/files', __FILE__) + get "dmsf/webdav/#{@project1.identifier}/test.txt", nil, @admin assert_response :success - assert !response.body.match(DmsfFolder.find(1).title).nil?, "Expected to find #{DmsfFolder.find(1).title} in return data" - assert !response.body.match(DmsfFile.find(1).name).nil?, "Expected to find #{DmsfFile.find(1).name} in return data" + assert_equal response.body, '1234', "File downloaded with unexpected contents: '#{response.body}'" end - test "user assigned to project" do + test 'should list dmsf contents within project' do + get "dmsf/webdav/#{@project1.identifier}", nil, @admin + assert_response :success + folder = DmsfFolder.find_by_id 1 + assert folder + 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), "Expected to find #{file.name} in return data" + end - #We'll be using project 2 and user jsmith for this test (Manager) - project = Project.find(2) - role = Role.find(2) #Developer role - jsmith = credentials('jsmith') - user = User.find(2) - - get "dmsf/webdav/#{project.identifier}", nil, jsmith + test 'user assigned to project' do + # We'll be using project 2 and user jsmith for this test (Manager) + get "dmsf/webdav/#{@project2.identifier}", nil, @jsmith assert_response 404 - project.enable_module! :dmsf #Flag module enabled + @project2.enable_module! :dmsf #Flag module enabled - get "dmsf/webdav/#{project.identifier}", nil, jsmith + get "dmsf/webdav/#{@project2.identifier}", nil, @jsmith assert_response 404 - role.add_permission! :view_dmsf_folders #assign rights + @role_developer.add_permission! :view_dmsf_folders #assign rights - get "dmsf/webdav/#{project.identifier}", nil, jsmith + get "dmsf/webdav/#{@project2.identifier}", nil, @jsmith assert_response :success - get "dmsf/webdav/#{project.identifier}/test.txt", nil, jsmith + get "dmsf/webdav/#{@project2.identifier}/test.txt", nil, @jsmith assert_response 403 #Access is not granted as does not hold view_dmsf_files role (yet) - role.add_permission! :view_dmsf_files #assign rights - - get "dmsf/webdav/#{project.identifier}/test.txt", nil, jsmith + @role_developer.add_permission! :view_dmsf_files #assign rights + # TODO: the storage path is not set as expected => reset + DmsfFile.storage_path = File.expand_path('../../fixtures/files', __FILE__) + get "dmsf/webdav/#{@project2.identifier}/test.txt", nil, @jsmith assert_response :success - assert (response.body != "1234"), "File downloaded with expected contents" - - #tear down - project.disable_module! :dmsf - role.remove_permission! :view_dmsf_folders - role.remove_permission! :view_dmsf_files - + assert_equal response.body, '1234', "File downloaded with unexpected contents: '#{response.body}'" end -end +end \ No newline at end of file diff --git a/test/integration/dmsf_webdav_head_test.rb b/test/integration/dmsf_webdav_head_test.rb index d7c0828d..14c14d4d 100644 --- a/test/integration/dmsf_webdav_head_test.rb +++ b/test/integration/dmsf_webdav_head_test.rb @@ -1,6 +1,7 @@ # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 Karel Picman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,20 +21,30 @@ require File.expand_path('../../test_helper', __FILE__) class DmsfWebdavOptionsTest < RedmineDmsf::Test::IntegrationTest - fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, :dmsf_folders + fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, + :dmsf_folders - def setup - DmsfFile.storage_path = File.expand_path('../../fixtures/files', __FILE__) + def setup + @project1 = Project.find_by_id 1 + @project2 = Project.find_by_id 2 + Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' + Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' + DmsfFile.storage_path = File.expand_path '../fixtures/files', __FILE__ + end + + def test_truth + assert_kind_of Project, @project1 + assert_kind_of Project, @project2 end - test "HEAD requires authentication" do - make_request "/dmsf/webdav/#{Project.find(1).identifier}" + test 'HEAD requires authentication' do + make_request "/dmsf/webdav/#{@project1.identifier}" assert_response 401 check_headers_dont_exist end - test "HEAD responds with authentication" do - make_request "/dmsf/webdav/#{Project.find(1).identifier}", "admin" + test 'HEAD responds with authentication' do + make_request "/dmsf/webdav/#{@project1.identifier}", 'admin' assert_response :success check_headers_exist end @@ -43,31 +54,33 @@ class DmsfWebdavOptionsTest < RedmineDmsf::Test::IntegrationTest # header and invalidates the test - where as a folder listing will always not include a last-modified # (but may include an etag, so there is an allowance for a 1 in 2 failure rate on (optionally) required # headers) - test "HEAD responds to file" do - make_request "/dmsf/webdav/#{Project.find(1).identifier}/test.txt", "admin" + test 'HEAD responds to file' do + # TODO: the storage path is not set as expected => reset + DmsfFile.storage_path = File.expand_path('../../fixtures/files', __FILE__) + make_request "/dmsf/webdav/#{@project1.identifier}/test.txt", 'admin' assert_response :success check_headers_exist #Note it'll allow 1 out of the 3 expected to fail end - test "HEAD fails when file or folder not found" do - make_request "/dmsf/webdav/#{Project.find(1).identifier}/not_here.txt", "admin" + test 'HEAD fails when file or folder not found' do + make_request "/dmsf/webdav/#{@project1.identifier}/not_here.txt", 'admin' assert_response 404 check_headers_dont_exist - make_request "/dmsf/webdav/folder_not_here", "admin" + make_request '/dmsf/webdav/folder_not_here', 'admin' assert_response 404 check_headers_dont_exist end - test "HEAD fails when project is not enabled for DMSF" do + test 'HEAD fails when project is not enabled for DMSF' do - make_request "/dmsf/webdav/#{Project.find(2).identifier}/test.txt", "admin" + make_request "/dmsf/webdav/#{@project2.identifier}/test.txt", 'jsmith' assert_response 404 check_headers_dont_exist end - private + def make_request(*args) if (args.length == 1) #Just a URL head args.first @@ -77,7 +90,7 @@ class DmsfWebdavOptionsTest < RedmineDmsf::Test::IntegrationTest end def check_headers_exist - assert !(response.headers.nil? || response.headers.empty?), "Head returned without headers" #Headers exist? + assert !(response.headers.nil? || response.headers.empty?), 'Head returned without headers' #Headers exist? values = {} values[:etag] = {:optional => true, :content => response.headers['Etag']} values[:content_type] = response.headers['Content-Type'] @@ -97,7 +110,7 @@ class DmsfWebdavOptionsTest < RedmineDmsf::Test::IntegrationTest end def check_headers_dont_exist - assert !(response.headers.nil? || response.headers.empty?), "Head returned without headers" #Headers exist? + assert !(response.headers.nil? || response.headers.empty?), 'Head returned without headers' #Headers exist? values = {} values[:etag] = response.headers['Etag']; values[:last_modified] = response.headers['Last-Modified'] @@ -106,5 +119,4 @@ class DmsfWebdavOptionsTest < RedmineDmsf::Test::IntegrationTest } end - -end +end \ No newline at end of file diff --git a/test/integration/dmsf_webdav_mkcol_test.rb b/test/integration/dmsf_webdav_mkcol_test.rb index 66d6ac69..c52cf5ca 100644 --- a/test/integration/dmsf_webdav_mkcol_test.rb +++ b/test/integration/dmsf_webdav_mkcol_test.rb @@ -1,6 +1,7 @@ # Redmine plugin for Document Management System "Features" # # Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 Karel Picman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,71 +21,68 @@ require File.expand_path('../../test_helper', __FILE__) class DmsfWebdavMkcolTest < RedmineDmsf::Test::IntegrationTest - fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, :dmsf_folders + fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, + :dmsf_folders def setup - @headers = credentials('admin') + @admin = credentials 'admin' + @jsmith = credentials 'jsmith' + @project1 = Project.find_by_id 1 + @project2 = Project.find_by_id 2 + @role_developer = Role.find 2 + Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' + Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' super end - - def teardown - @headers = nil + + def test_truth + assert_kind_of Project, @project1 + assert_kind_of Project, @project2 + assert_kind_of Role, @role_developer end - - test "MKCOL requires authentication" do - xml_http_request :mkcol, "dmsf/webdav/test1" + + test 'MKCOL requires authentication' do + xml_http_request :mkcol, 'dmsf/webdav/test1' assert_response 401 end - test "MKCOL fails to create folder at root level" do - xml_http_request :mkcol, "dmsf/webdav/test1", nil, @headers + test 'MKCOL fails to create folder at root level' do + xml_http_request :mkcol, 'dmsf/webdav/test1', nil, @admin assert_response 501 #Not Implemented at this level end - test "should not succeed on a non-existant project" do - xml_http_request :mkcol, "dmsf/webdav/project_doesnt_exist/test1", nil, @headers + test 'should not succeed on a non-existant project' do + xml_http_request :mkcol, 'dmsf/webdav/project_doesnt_exist/test1', nil, @admin assert_response 404 #Not found end - test "should not succed on a non-dmsf enabled project" do - xml_http_request :mkcol, "dmsf/webdav/#{Project.find(2).identifier}/test1", nil, @headers - assert_response 404 + test 'should not succed on a non-dmsf enabled project' do + xml_http_request :mkcol, "dmsf/webdav/#{@project2.identifier}/test1", nil, @jsmith + assert_response :forbidden end - test "should create folder on dmsf enabled project" do - xml_http_request :mkcol, "dmsf/webdav/#{Project.find(1).identifier}/test1", nil, @headers + test 'should create folder on dmsf enabled project' do + xml_http_request :mkcol, "dmsf/webdav/#{@project1.identifier}/test1", nil, @admin assert_response :success end - test "should fail to create folder that already exists" do - xml_http_request :mkcol, "dmsf/webdav/#{Project.find(1).identifier}/test1", nil, @headers + test 'should fail to create folder that already exists' do + xml_http_request :mkcol, "dmsf/webdav/#{@project1.identifier}/test1", nil, @admin assert_response :success - xml_http_request :mkcol, "dmsf/webdav/#{Project.find(1).identifier}/test1", nil, @headers + xml_http_request :mkcol, "dmsf/webdav/#{@project1.identifier}/test1", nil, @admin assert_response 405 #Method not Allowed end - test "should fail to create folder for user without rights" do - xml_http_request :mkcol, "dmsf/webdav/#{Project.find(1).identifier}/test1", nil, credentials('jsmith') + test 'should fail to create folder for user without rights' do + xml_http_request :mkcol, "dmsf/webdav/#{@project1.identifier}/test1", nil, @jsmith assert_response 403 #Forbidden end - test "should create folder for non-admin user with rights" do - - role = Role.find(2) #Developer role - jsmith = credentials('jsmith') - user = User.find(2) - project = Project.find(2) - - role.add_permission! :folder_manipulation - project.enable_module! :dmsf - - xml_http_request :mkcol, "dmsf/webdav/#{project.identifier}/test1", nil, credentials('jsmith') - assert_response :success - - role.remove_permission! :folder_manipulation - project.disable_module! :dmsf - + test 'should create folder for non-admin user with rights' do + @role_developer.add_permission! :folder_manipulation + @project2.enable_module! :dmsf + xml_http_request :mkcol, "dmsf/webdav/#{@project2.identifier}/test1", nil, @jsmith + assert_response :success end - -end +end \ No newline at end of file diff --git a/test/integration/dmsf_webdav_options_test.rb b/test/integration/dmsf_webdav_options_test.rb index 19eacfc9..1d82d012 100644 --- a/test/integration/dmsf_webdav_options_test.rb +++ b/test/integration/dmsf_webdav_options_test.rb @@ -1,6 +1,7 @@ # Redmine plugin for Document Management System "Features" # # Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 Karel Picman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,106 +21,110 @@ require File.expand_path('../../test_helper', __FILE__) class DmsfWebdavOptionsTest < RedmineDmsf::Test::IntegrationTest - fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, :dmsf_folders + fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, + :dmsf_folders def setup - @headers = credentials('admin') + @admin = credentials 'admin' + @project1 = Project.find_by_id 1 + @project2 = Project.find_by_id 2 + Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' + Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' super end - - def teardown - @headers = nil + + def test_truth + assert_kind_of Project, @project1 + assert_kind_of Project, @project2 end - - test "OPTIONS requires no authentication for root level" do - xml_http_request :options, "dmsf/webdav" + + test 'OPTIONS requires no authentication for root level' do + xml_http_request :options, 'dmsf/webdav' assert_response :success end - test "OPTIONS returns expected Allow header" do - xml_http_request :options, "dmsf/webdav" + test 'OPTIONS returns expected Allow header' do + xml_http_request :options, 'dmsf/webdav' assert_response :success - assert !(response.headers.nil? || response.headers.empty?), "Response headers are empty" - assert !response.headers["Allow"].nil? , "Allow header is empty or does not exist" - assert response.headers["Allow"] == "OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK", "Allow header returns expected content" + assert !(response.headers.nil? || response.headers.empty?), 'Response headers are empty' + assert response.headers['Allow'] , 'Allow header is empty or does not exist' + assert response.headers['Allow'] == 'OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK', 'Allow header returns expected content' end - test "OPTIONS returns expected Dav header" do - xml_http_request :options, "dmsf/webdav" + test 'OPTIONS returns expected Dav header' do + xml_http_request :options, 'dmsf/webdav' assert_response :success - assert !(response.headers.nil? || response.headers.empty?), "Response headers are empty" - assert !response.headers["Dav"].nil? , "Dav header is empty or does not exist" - assert response.headers["Dav"] == "1,2,3", "Dav header - expected: 1,2,3" + assert !(response.headers.nil? || response.headers.empty?), 'Response headers are empty' + assert response.headers['Dav'] , 'Dav header is empty or does not exist' + assert response.headers['Dav'] == '1,2,3', 'Dav header - expected: 1,2,3' end - test "OPTIONS returns expected Ms-Auth-Via header" do - xml_http_request :options, "dmsf/webdav" + test 'OPTIONS returns expected Ms-Auth-Via header' do + xml_http_request :options, 'dmsf/webdav' assert_response :success - assert !(response.headers.nil? || response.headers.empty?), "Response headers are empty" - assert !response.headers["Ms-Author-Via"].nil? , "Ms-Author-Via header is empty or does not exist" - assert response.headers["Ms-Author-Via"] == "DAV", "Ms-Author-Via header - expected: DAV" + assert !(response.headers.nil? || response.headers.empty?), 'Response headers are empty' + assert response.headers['Ms-Author-Via'] , 'Ms-Author-Via header is empty or does not exist' + assert response.headers['Ms-Author-Via'] == 'DAV', 'Ms-Author-Via header - expected: DAV' end - test "OPTIONS requires authentication for non-root request" do - xml_http_request :options, "dmsf/webdav/#{Project.find(1).identifier}" + test 'OPTIONS requires authentication for non-root request' do + xml_http_request :options, "dmsf/webdav/#{@project1.identifier}" assert_response 401 #Unauthorized end - test "Un-authenticated OPTIONS returns expected Allow header" do - xml_http_request :options, "dmsf/webdav/#{Project.find(1).identifier}" + test 'Un-authenticated OPTIONS returns expected Allow header' do + xml_http_request :options, "dmsf/webdav/#{@project1.identifier}" assert_response 401 - assert !(response.headers.nil? || response.headers.empty?), "Response headers are empty" - assert response.headers["Allow"].nil? , "Allow header should not exist" - assert response.headers["Allow"] != "OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK", "Allow header returns expected" + assert !(response.headers.nil? || response.headers.empty?), 'Response headers are empty' + assert_nil response.headers['Allow'] , 'Allow header should not exist' + #assert response.headers['Allow'] != 'OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK', 'Allow header returns expected' end - test "Un-authenticated OPTIONS returns expected Dav header" do - xml_http_request :options, "dmsf/webdav/#{Project.find(1).identifier}" + test 'Un-authenticated OPTIONS returns expected Dav header' do + xml_http_request :options, "dmsf/webdav/#{@project1.identifier}" assert_response 401 - assert !(response.headers.nil? || response.headers.empty?), "Response headers are empty" - assert response.headers["Dav"].nil? , "Dav header should not exist" - assert response.headers["Dav"] != "1,2,3", "Dav header - expected: " + assert !(response.headers.nil? || response.headers.empty?), 'Response headers are empty' + assert_nil response.headers['Dav'] , 'Dav header should not exist' + #assert response.headers['Dav'] != '1,2,3', 'Dav header - expected: ' end - test "Un-athenticated OPTIONS returns expected Ms-Auth-Via header" do - xml_http_request :options, "dmsf/webdav/#{Project.find(1).identifier}" + test 'Un-athenticated OPTIONS returns expected Ms-Auth-Via header' do + xml_http_request :options, "dmsf/webdav/#{@project1.identifier}" assert_response 401 - assert !(response.headers.nil? || response.headers.empty?), "Response headers are empty" - assert response.headers["Ms-Author-Via"].nil? , "Ms-Author-Via header should not exist" - assert response.headers["Ms-Author-Via"] != "DAV", "Ms-Author-Via header - expected: " + assert !(response.headers.nil? || response.headers.empty?), 'Response headers are empty' + assert_nil response.headers['Ms-Author-Via'] , 'Ms-Author-Via header should not exist' + #assert response.headers["Ms-Author-Via"] != "DAV", "Ms-Author-Via header - expected: " end - - test "Authenticated OPTIONS returns expected Allow header" do - xml_http_request :options, "dmsf/webdav/#{Project.find(1).identifier}", nil, @headers + test 'Authenticated OPTIONS returns expected Allow header' do + xml_http_request :options, "dmsf/webdav/#{@project1.identifier}", nil, @admin assert_response :success assert !(response.headers.nil? || response.headers.empty?), "Response headers are empty" - assert !response.headers["Allow"].nil? , "Allow header is empty or does not exist" - assert response.headers["Allow"] == "OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK", "Allow header returns expected" + assert response.headers['Allow'], 'Allow header is empty or does not exist' + assert response.headers['Allow'] == 'OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK', 'Allow header returns expected' end - test "Authenticated OPTIONS returns expected Dav header" do - xml_http_request :options, "dmsf/webdav/#{Project.find(1).identifier}", nil, @headers + test 'Authenticated OPTIONS returns expected Dav header' do + xml_http_request :options, "dmsf/webdav/#{@project1.identifier}", nil, @admin assert_response :success - assert !(response.headers.nil? || response.headers.empty?), "Response headers are empty" - assert !response.headers["Dav"].nil? , "Dav header is empty or does not exist" - assert response.headers["Dav"] == "1,2,3", "Dav header - expected: 1,2,3" + assert !(response.headers.nil? || response.headers.empty?), 'Response headers are empty' + assert response.headers['Dav'], 'Dav header is empty or does not exist' + assert response.headers['Dav'] == '1,2,3', 'Dav header - expected: 1,2,3' end - test "Authenticated OPTIONS returns expected Ms-Auth-Via header" do - xml_http_request :options, "dmsf/webdav/#{Project.find(1).identifier}", nil, @headers + test 'Authenticated OPTIONS returns expected Ms-Auth-Via header' do + xml_http_request :options, "dmsf/webdav/#{@project1.identifier}", nil, @admin assert_response :success - assert !(response.headers.nil? || response.headers.empty?), "Response headers are empty" - assert !response.headers["Ms-Author-Via"].nil? , "Ms-Author-Via header is empty or does not exist" - assert response.headers["Ms-Author-Via"] == "DAV", "Ms-Author-Via header - expected: DAV" + assert !(response.headers.nil? || response.headers.empty?), 'Response headers are empty' + assert response.headers['Ms-Author-Via'], 'Ms-Author-Via header is empty or does not exist' + assert response.headers['Ms-Author-Via'] == 'DAV', 'Ms-Author-Via header - expected: DAV' end - test "Authenticated OPTIONS returns 404 for not-found or non-dmsf-enabled items" do - xml_http_request :options, "dmsf/webdav/#{Project.find(2).identifier}", nil, @headers - assert_response 404 #not found - xml_http_request :options, "dmsf/webdav/does-not-exist", nil, @headers - assert_response 404 #not found + test 'Authenticated OPTIONS returns 401 for not-found or non-dmsf-enabled items' do + xml_http_request :options, "dmsf/webdav/#{@project2.identifier}", nil, @jsmith + assert_response 401 # refused + xml_http_request :options, 'dmsf/webdav/does-not-exist', nil, @jsmith + assert_response 401 # refused end - -end +end \ No newline at end of file diff --git a/test/integration/dmsf_webdav_post_test.rb b/test/integration/dmsf_webdav_post_test.rb index 976b8278..89555e26 100644 --- a/test/integration/dmsf_webdav_post_test.rb +++ b/test/integration/dmsf_webdav_post_test.rb @@ -1,6 +1,7 @@ # Redmine plugin for Document Management System "Features" # # Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 Karel Picman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -23,23 +24,22 @@ class DmsfWebdavPostTest < RedmineDmsf::Test::IntegrationTest fixtures :users, :enabled_modules def setup - @headers = credentials('admin') + @admin = credentials 'admin' + Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' + Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' super end - - def teardown - @headers = nil - end - - #Test that any post request is authenticated + + # Test that any post request is authenticated def test_post_request_authenticated - post "/dmsf/webdav/" - assert_response 401 #401 Unauthorized + post '/dmsf/webdav/' + assert_response 401 # 401 Unauthorized end - #Test post is not implimented + # Test post is not implemented def test_post_not_implemented - post "/dmsf/webdav/", nil, @headers - assert_response 501 #501 Not Implemented + post '/dmsf/webdav/', nil, @admin + assert_response 501 # 501 Not Implemented end -end + +end \ No newline at end of file diff --git a/test/integration/dmsf_webdav_put_test.rb b/test/integration/dmsf_webdav_put_test.rb index 65159f34..89a3b1b6 100644 --- a/test/integration/dmsf_webdav_put_test.rb +++ b/test/integration/dmsf_webdav_put_test.rb @@ -1,6 +1,7 @@ # Redmine plugin for Document Management System "Features" # # Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 Karel Picman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -21,198 +22,177 @@ require 'fileutils' class DmsfWebdavIntegrationTest < RedmineDmsf::Test::IntegrationTest - fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, :dmsf_folders, :dmsf_files, :dmsf_file_revisions + fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, + :dmsf_folders, :dmsf_files, :dmsf_file_revisions def setup DmsfLock.delete_all #Delete all locks that are in our test DB - probably not safe but ho hum timestamp = DateTime.now.strftime("%y%m%d%H%M") DmsfFile.storage_path = File.expand_path("./dmsf_test-#{timestamp}", DmsfHelper.temp_dir) Dir.mkdir(DmsfFile.storage_path) unless File.directory?(DmsfFile.storage_path) - @admin = credentials('admin') - @jsmith = credentials('jsmith') + @admin = credentials 'admin' + @jsmith = credentials 'jsmith' + @jsmith = credentials 'jsmith' + @project1 = Project.find_by_id 1 + @project2 = Project.find_by_id 2 + @role_developer = Role.find 2 + Setting.plugin_redmine_dmsf['dmsf_webdav'] = '1' + Setting.plugin_redmine_dmsf['dmsf_webdav_strategy'] = 'WEBDAV_READ_WRITE' super end - def teardown - @headers = nil - #Delete our tmp folder - begin - FileUtils.rm_rf DmsfFile.storage_path - rescue - warn "DELETE FAILED" - end +# def teardown +# # Delete our tmp folder +# begin +# FileUtils.rm_rf DmsfFile.storage_path +# rescue +# warn 'DELETE FAILED' +# end +# end + + def test_truth + assert_kind_of Project, @project1 + assert_kind_of Project, @project2 + assert_kind_of Role, @role_developer end - test "PUT denied unless authenticated" do + test 'PUT denied unless authenticated' do put 'dmsf/webdav' assert_response 401 - put "dmsf/webdav/#{Project.find(1).identifier}" + put "dmsf/webdav/#{@project1.identifier}" assert_response 401 - end - test "PUT denied with failed authentication" do + test 'PUT denied with failed authentication' do put 'dmsf/webdav', nil, credentials('admin', 'badpassword') assert_response 401 - put "dmsf/webdav/#{Project.find(1).identifier}", nil, credentials('admin', 'badpassword') + put "dmsf/webdav/#{@project1.identifier}", nil, credentials('admin', 'badpassword') assert_response 401 end - test "PUT denied at root level" do + test 'PUT denied at root level' do put 'dmsf/webdav/test.txt', "1234", @admin.merge!({:content_type => :text}) assert_response 501 end - test "PUT denied on collection/folder" do - put "dmsf/webdav/#{Project.find(1).identifier}", "1234", @admin.merge!({:content_type => :text}) + test 'PUT denied on collection/folder' do + put "dmsf/webdav/#{@project1.identifier}", '1234', @admin.merge!({:content_type => :text}) assert_response 403 #forbidden end - test "PUT failed on non-existant project" do - put "dmsf/webdav/not_a_project/file.txt", "1234", @admin.merge!({:content_type => :text}) + test 'PUT failed on non-existant project' do + put 'dmsf/webdav/not_a_project/file.txt', '1234', @admin.merge!({:content_type => :text}) assert_response 409 # Conflict, not_a_project does not exist - file.txt cannot be created end - test "PUT as admin granted on dmsf-enabled project" do - - put "dmsf/webdav/#{Project.find(1).identifier}/test-1234.txt", "1234", @admin.merge!({:content_type => :text}) + test 'PUT as admin granted on dmsf-enabled project' do + put "dmsf/webdav/#{@project1.identifier}/test-1234.txt", '1234', @admin.merge!({:content_type => :text}) assert_response 201 #201 Created #Lets check for our file - file = DmsfFile.find_file_by_name(Project.find(1), nil, "test-1234.txt") - assert !file.nil?, 'Check for files existance' - + file = DmsfFile.find_file_by_name @project1, nil, 'test-1234.txt' + assert file, 'Check for files existance' end - test "PUT failed as admin on non-dmsf enabled project" do - put "dmsf/webdav/#{Project.find(2).identifier}/test-1234.txt", "1234", @admin.merge!({:content_type => :text}) + test 'PUT failed as jsmith on non-dmsf enabled project' do + put "dmsf/webdav/#{@project2.identifier}/test-1234.txt", '1234', @jsmith.merge!({:content_type => :text}) assert_response 409 #Should report conflict, as project 2 technically doesn't exist if not enabled #Lets check for our file - file = DmsfFile.find_file_by_name(Project.find(2), nil, "test-1234.txt") - assert file.nil?, 'Check for files existance' + file = DmsfFile.find_file_by_name @project2, nil, 'test-1234.txt' + assert_nil file, 'Check for files existance' end - test "PUT failed when insuficient permissions on project" do + test 'PUT failed when insuficient permissions on project' do + @project2.enable_module! :dmsf #Flag module enabled - project = Project.find(2) - project.enable_module! :dmsf #Flag module enabled - role = Role.find(2) - - put "dmsf/webdav/#{project.identifier}/test-1234.txt", "1234", @jsmith.merge!({:content_type => :text}) + put "dmsf/webdav/#{@project2.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. - role.add_permission! :view_dmsf_folders + @role_developer.add_permission! :view_dmsf_folders - put "dmsf/webdav/#{project.identifier}/test-1234.txt", "1234", @jsmith.merge!({:content_type => :text}) + put "dmsf/webdav/#{@project2.identifier}/test-1234.txt", '1234', @jsmith.merge!({:content_type => :text}) assert_response 403 #We don't hold the permission file_manipulation - so we're unable to do anything with files - role.remove_permission! :view_dmsf_folders - role.add_permission! :file_manipulation + @role_developer.remove_permission! :view_dmsf_folders + @role_developer.add_permission! :file_manipulation #Check we don't have write access even if we do have the file_manipulation permission - put "dmsf/webdav/#{project.identifier}/test-1234.txt", "1234", @jsmith.merge!({:content_type => :text}) + put "dmsf/webdav/#{@project2.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(project, nil, "test-1234.txt") - assert file.nil?, 'File test-1234 was found in projects dmsf folder.' - - role.remove_permission! :view_dmsf_folders - role.remove_permission! :file_manipulation + file = DmsfFile.find_file_by_name @project2, nil, 'test-1234.txt' + assert_nil file, 'File test-1234 was found in projects dmsf folder.' end - test "PUT succeeds for non-admin with correct permissions" do - project = Project.find(2) - project.enable_module! :dmsf #Flag module enabled - role = Role.find(2) + test 'PUT succeeds for non-admin with correct permissions' do + @project2.enable_module! :dmsf #Flag module enabled - put "dmsf/webdav/#{project.identifier}/test-1234.txt", "1234", @jsmith.merge!({:content_type => :text}) + put "dmsf/webdav/#{@project2.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. - role.add_permission! :view_dmsf_folders - role.add_permission! :file_manipulation + @role_developer.add_permission! :view_dmsf_folders + @role_developer.add_permission! :file_manipulation #Check we don't have write access even if we do have the file_manipulation permission - put "dmsf/webdav/#{project.identifier}/test-1234.txt", "1234", @jsmith.merge!({:content_type => :text}) + put "dmsf/webdav/#{@project2.identifier}/test-1234.txt", '1234', @jsmith.merge!({:content_type => :text}) assert_response 201 #Now we have permissions :D #Lets check for our file - file = DmsfFile.find_file_by_name(project, nil, "test-1234.txt") - assert !file.nil?, 'File test-1234 was not found in projects dmsf folder.' - - role.remove_permission! :view_dmsf_folders - role.remove_permission! :file_manipulation + file = DmsfFile.find_file_by_name @project2, nil, 'test-1234.txt' + assert file, 'File test-1234 was not found in projects dmsf folder.' end - test "PUT writes revision successfully for unlocked file" do - project = Project.find(2) - project.enable_module! :dmsf #Flag module enabled - role = Role.find(2) + test 'PUT writes revision successfully for unlocked file' do + @project2.enable_module! :dmsf #Flag module enabled + @role_developer.add_permission! :view_dmsf_folders + @role_developer.add_permission! :file_manipulation - role.add_permission! :view_dmsf_folders - role.add_permission! :file_manipulation - - file = DmsfFile.find_file_by_name(project, nil, "test.txt") + file = DmsfFile.find_file_by_name @project2, nil, 'test.txt' assert_difference('file.revisions.count') do - put "dmsf/webdav/#{project.identifier}/test.txt", "1234", @jsmith.merge!({:content_type => :text}) + put "dmsf/webdav/#{@project2.identifier}/test.txt", '1234', @jsmith.merge!({:content_type => :text}) assert_response 201 #Created end - - role.remove_permission! :view_dmsf_folders - role.remove_permission! :file_manipulation - end - test "PUT fails revision when file is locked" do - role = Role.find(2) - project = Project.find(2) + test 'PUT fails revision when file is locked' do + @project2.enable_module! :dmsf #Flag module enabled + @role_developer.add_permission! :view_dmsf_folders + @role_developer.add_permission! :file_manipulation - project.enable_module! :dmsf #Flag module enabled + log_user 'admin', 'admin' # login as admin + assert !User.current.anonymous?, 'Current user is not anonymous' - role.add_permission! :view_dmsf_folders - role.add_permission! :file_manipulation - - log_user "admin", "admin" #login as jsmith - assert !User.current.anonymous?, "Current user is not anonymous" - - file = DmsfFile.find_file_by_name(project, nil, "test.txt") + file = DmsfFile.find_file_by_name @project2, nil, 'test.txt' assert file.lock!, "File failed to be locked by #{User.current.name}" assert_no_difference('file.revisions.count') do - put "dmsf/webdav/#{project.identifier}/test.txt", "1234", @jsmith.merge!({:content_type => :text}) + put "dmsf/webdav/#{@project2.identifier}/test.txt", '1234', @jsmith.merge!({:content_type => :text}) assert_response 423 #Locked end User.current = User.find(1) file.unlock! - assert !file.locked?, "File failed to unlock by #{User.current.name}" - - role.add_permission! :view_dmsf_folders - role.add_permission! :file_manipulation - + assert !file.locked?, "File failed to unlock by #{User.current.name}" end - test "PUT fails revision when file is locked and user is administrator" do - role = Role.find(2) - project = Project.find(2) + test 'PUT fails revision when file is locked and user is administrator' do + @project2.enable_module! :dmsf #Flag module enabled + @role_developer.add_permission! :view_dmsf_folders + @role_developer.add_permission! :file_manipulation - project.enable_module! :dmsf #Flag module enabled - - role.add_permission! :view_dmsf_folders - role.add_permission! :file_manipulation - - log_user "jsmith", "jsmith" #login as jsmith + log_user 'jsmith', 'jsmith' # login as jsmith assert !User.current.anonymous?, "Current user is not anonymous" - file = DmsfFile.find_file_by_name(project, nil, "test.txt") + file = DmsfFile.find_file_by_name @project2, nil, 'test.txt' assert file.lock!, "File failed to be locked by #{User.current.name}" assert_no_difference('file.revisions.count') do - put "dmsf/webdav/#{project.identifier}/test.txt", "1234", @admin.merge!({:content_type => :text}) + put "dmsf/webdav/#{@project2.identifier}/test.txt", "1234", @admin.merge!({:content_type => :text}) assert_response 423 #Created end User.current = User.find(2) @@ -221,37 +201,28 @@ class DmsfWebdavIntegrationTest < RedmineDmsf::Test::IntegrationTest rescue #nothing end - assert !file.locked?, "File failed to unlock by #{User.current.name}" - - role.add_permission! :view_dmsf_folders - role.add_permission! :file_manipulation + assert !file.locked?, "File failed to unlock by #{User.current.name}" end - test "PUT accepts revision when file is locked and user is same as lock holder" do - role = Role.find(2) - project = Project.find(2) + test 'PUT accepts revision when file is locked and user is same as lock holder' do + @project2.enable_module! :dmsf #Flag module enabled + @role_developer.add_permission! :view_dmsf_folders + @role_developer.add_permission! :file_manipulation - project.enable_module! :dmsf #Flag module enabled + log_user 'jsmith', 'jsmith' #login as jsmith + assert !User.current.anonymous?, 'Current user is not anonymous' - role.add_permission! :view_dmsf_folders - role.add_permission! :file_manipulation - - log_user "jsmith", "jsmith" #login as jsmith - assert !User.current.anonymous?, "Current user is not anonymous" - - file = DmsfFile.find_file_by_name(project, nil, "test.txt") + file = DmsfFile.find_file_by_name @project2, nil, 'test.txt' assert file.lock!, "File failed to be locked by #{User.current.name}" assert_difference('file.revisions.count') do - put "dmsf/webdav/#{project.identifier}/test.txt", "1234", @jsmith.merge!({:content_type => :text}) + put "dmsf/webdav/#{@project2.identifier}/test.txt", '1234', @jsmith.merge!({:content_type => :text}) assert_response 201 #Created end file.unlock! assert !file.locked?, "File failed to unlock by #{User.current.name}" - - role.add_permission! :view_dmsf_folders - role.add_permission! :file_manipulation end -end + +end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index 0cb8c185..3274dc6c 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,7 +1,8 @@ # Redmine plugin for Document Management System "Features" # -# Copyright (C) 2011 Vít Jonáš -# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011 Vít Jonáš +# Copyright (C) 2012 Daniel Munn +# Copyright (C) 2011-14 Karel Picman # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,8 +19,4 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Load the normal Rails helper -require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper') - -# Use fixtures from redmine -ActiveSupport::TestCase.fixture_path = File.dirname(__FILE__) + '/../../../test/fixtures' - +require File.expand_path('../../../../test/test_helper', __FILE__) \ No newline at end of file diff --git a/test/unit/dmsf_file_test.rb b/test/unit/dmsf_file_test.rb index 6bda59b6..81ed8311 100644 --- a/test/unit/dmsf_file_test.rb +++ b/test/unit/dmsf_file_test.rb @@ -16,7 +16,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -require File.dirname(__FILE__) + '/../test_helper' +require File.expand_path('../../test_helper', __FILE__) class DmsfFileTest < RedmineDmsf::Test::UnitTest fixtures :projects, :users, :dmsf_folders, :dmsf_files, :dmsf_file_revisions, diff --git a/test/unit/dmsf_links_test.rb b/test/unit/dmsf_links_test.rb new file mode 100644 index 00000000..1dc7af34 --- /dev/null +++ b/test/unit/dmsf_links_test.rb @@ -0,0 +1,174 @@ +# Redmine plugin for Document Management System "Features" +# +# Copyright (C) 2014 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. + +require File.expand_path('../../test_helper', __FILE__) + +class DmsfLinksTest < RedmineDmsf::Test::UnitTest + + fixtures :projects, :members, :dmsf_files, :dmsf_file_revisions, + :dmsf_folders, :dmsf_links + + def setup + @project1 = Project.find_by_id 1 + @folder1 = DmsfFolder.find_by_id 1 + @folder2 = DmsfFolder.find_by_id 2 + @file1 = DmsfFile.find_by_id 1 + @file4 = DmsfFile.find_by_id 4 + @folder_link = DmsfLink.find_by_id 1 + @file_link = DmsfLink.find_by_id 2 + end + + def test_truth + assert_kind_of Project, @project1 + assert_kind_of DmsfFolder, @folder1 + assert_kind_of DmsfFolder, @folder2 + assert_kind_of DmsfFile, @file1 + assert_kind_of DmsfFile, @file4 + assert_kind_of DmsfLink, @folder_link + assert_kind_of DmsfLink, @file_link + end + + def test_create + # Folder link + folder_link = DmsfLink.new( + :target_project_id => @project1.id, + :target_id => @folder1.id, + :target_type => DmsfFolder.model_name, + :name => 'folder1_link2', + :project_id => @project1.id, + :created_at => DateTime.now(), + :updated_at => DateTime.now()) + assert folder_link.save + + # File link + file_link = DmsfLink.new( + :target_project_id => @project1.id, + :target_id => @file1.id, + :target_type => DmsfFile.model_name, + :name => 'file1_link2', + :project_id => @project1.id, + :created_at => DateTime.now(), + :updated_at => DateTime.now()) + assert file_link.save + end + + def test_validate_name_length + @folder_link.name = 'a' * 256 + assert !@folder_link.save + assert_equal 1, @folder_link.errors.count + end + + def test_validate_name_presence + @folder_link.name = '' + assert !@folder_link.save + assert_equal 1, @folder_link.errors.count + end + + def test_validate_target_id_presence + @folder_link.target_id = nil + assert !@folder_link.save + assert_equal 1, @folder_link.errors.count + end + + def test_belongs_to_project + @project1.destroy + assert_nil DmsfLink.find_by_id 1 + assert_nil DmsfLink.find_by_id 2 + end + + def test_belongs_to_dmsf_folder + @folder1.destroy + assert_nil DmsfLink.find_by_id 1 + assert_nil DmsfLink.find_by_id 2 + end + + def test_target_folder_id + assert_equal 2, @file_link.target_folder_id + assert_equal 1, @folder_link.target_folder_id + end + + def test_target_folder + assert_equal @folder2, @file_link.target_folder + assert_equal @folder1, @folder_link.target_folder + end + + def test_target_file_id + assert_equal 4, @file_link.target_file_id + assert_nil @folder_link.target_file + end + + def test_target_file + assert_equal @file4, @file_link.target_file + assert_nil @folder_link.target_file + end + + def test_target_project + assert_equal @project1, @file_link.target_project + assert_equal @project1, @folder_link.target_project + end + + def test_folder + assert_equal @folder1, @file_link.folder + assert_nil @folder_link.folder + end + + def test_title + assert_equal @file_link.name, @file_link.title + assert_equal @folder_link.name, @folder_link.title + end + + def test_find_link_by_file_name + assert_equal @file_link, + DmsfLink.find_link_by_file_name(@file_link.project, @file_link.folder, @file_link.target_file.name) + end + + def test_path + assert_equal @file_link.path, + @file_link.target_file.dmsf_path_str + assert_equal @folder_link.path, + @folder_link.target_folder.dmsf_path_str + end + + def test_copy_to + # File link + file_link_copy = @file_link.copy_to @folder2.project, @folder2 + assert_not_nil file_link_copy + assert_equal file_link_copy.target_project_id, @file_link.target_project_id + assert_equal file_link_copy.target_id, @file_link.target_id + assert_equal file_link_copy.target_type, @file_link.target_type + assert_equal file_link_copy.name, @file_link.name + assert_equal file_link_copy.project_id, @folder2.project.id + assert_equal file_link_copy.dmsf_folder_id, @folder2.id + + # Folder link + folder_link_copy = @folder_link.copy_to @folder2.project, @folder2 + assert_not_nil folder_link_copy + assert_equal folder_link_copy.target_project_id, @folder_link.target_project_id + assert_equal folder_link_copy.target_id, @folder_link.target_id + assert_equal folder_link_copy.target_type, @folder_link.target_type + assert_equal folder_link_copy.name, @folder_link.name + assert_equal folder_link_copy.project_id, @folder2.project.id + assert_equal folder_link_copy.dmsf_folder_id, @folder2.id + end + + def test_destroy + @folder_link.destroy + assert_nil DmsfLink.find_by_id 1 + end + +end \ No newline at end of file diff --git a/test/unit/dmsf_workflow_test.rb b/test/unit/dmsf_workflow_test.rb index 1aea1aeb..251af6b0 100644 --- a/test/unit/dmsf_workflow_test.rb +++ b/test/unit/dmsf_workflow_test.rb @@ -38,10 +38,23 @@ class DmsfWorkflowTest < RedmineDmsf::Test::UnitTest @revision1 = DmsfFileRevision.find_by_id 1 @revision2 = DmsfFileRevision.find_by_id 2 @project = Project.find_by_id 2 + @project5 = Project.find_by_id 5 end - def test_truth - assert_kind_of DmsfWorkflow, @wf1 + def test_truth + assert_kind_of DmsfWorkflow, @wf1 + assert_kind_of DmsfWorkflow, @wf2 + assert_kind_of DmsfWorkflowStep, @wfs1 + assert_kind_of DmsfWorkflowStep, @wfs2 + assert_kind_of DmsfWorkflowStep, @wfs3 + assert_kind_of DmsfWorkflowStep, @wfs4 + assert_kind_of DmsfWorkflowStep, @wfs5 + assert_kind_of DmsfWorkflowStepAssignment, @wfsa1 + assert_kind_of DmsfWorkflowStepAction, @wfsac1 + assert_kind_of DmsfFileRevision, @revision1 + assert_kind_of DmsfFileRevision, @revision2 + assert_kind_of Project, @project + assert_kind_of Project, @project5 end def test_create @@ -152,8 +165,8 @@ class DmsfWorkflowTest < RedmineDmsf::Test::UnitTest def test_delegates delegates = @wf1.delegates(nil, nil, nil) - assert_equal delegates.size, User.active.all.size - delegates = @wf1.delegates('Redmine', nil, nil) + assert_equal(delegates.all.count + 1, @project5.users.all.count) + delegates = @wf1.delegates('Dave', nil, nil) assert_equal delegates.size, 1 delegates = @wf1.delegates(nil, @wfsa1.id, 2) assert !delegates.any?{|user| user.id == @wfsa1.user_id} @@ -180,22 +193,18 @@ class DmsfWorkflowTest < RedmineDmsf::Test::UnitTest end end - def test_try_finish - #def try_finish(dmsf_file_revision_id, action, user_id) - # TODO: There is a strange error: 'ActiveRecord::RecordNotFound: Couldn't find Project with id=0' - # while saving the revision -# @revision1.set_workflow @wf1.id, 'start' -# @wf1.try_finish @revision1.id, @wfsac1, User.current.id -# @revision1.reload -# assert_equal @revision1.workflow, DmsfWorkflow::STATE_APPROVED -# @revision2.set_workflow @wf1.id, 'start' -# @wf1.try_finish @revision2.id, @wfsac1, User.current.id -# assert_equal @revision2.workflow, DmsfWorkflow::STATE_WAITING_FOR_APPROVAL - assert true + def test_try_finish + @revision1.set_workflow @wf1.id, 'start' + @wf1.try_finish @revision1, @wfsac1, User.current.id + @revision1.reload + assert_equal @revision1.workflow, DmsfWorkflow::STATE_APPROVED + @revision2.set_workflow @wf1.id, 'start' + @wf1.try_finish @revision2, @wfsac1, User.current.id + assert_equal @revision2.workflow, DmsfWorkflow::STATE_WAITING_FOR_APPROVAL end def test_participiants - # TODO: - assert true + participiants = @wf1.participiants + assert_equal participiants.count, 2 end end