From 2c23d4e1aa1fa0ddd95870f92f129a4a80c9c24f Mon Sep 17 00:00:00 2001 From: "vit.jonas@gmail.com" Date: Thu, 5 May 2011 19:29:34 +0000 Subject: [PATCH] * finishing import git-svn-id: http://redmine-dmsf.googlecode.com/svn/trunk/redmine_dmsf@4 5e329b0b-a2ee-ea63-e329-299493fc886d --- README.txt | 80 ++ app/controllers/dmsf_controller.rb | 200 +++++ app/controllers/dmsf_detail_controller.rb | 215 +++++ app/controllers/dmsf_state_controller.rb | 106 +++ app/helpers/dmsf_access_error.rb | 3 + app/helpers/dmsf_content_error.rb | 3 + app/helpers/dmsf_helper.rb | 26 + app/helpers/dmsf_upload.rb | 59 ++ app/helpers/dmsf_zip.rb | 41 + app/models/dmsf_file.rb | 257 ++++++ app/models/dmsf_file_lock.rb | 5 + app/models/dmsf_file_revision.rb | 213 +++++ app/models/dmsf_folder.rb | 60 ++ app/models/dmsf_mailer.rb | 86 ++ app/models/dmsf_user_pref.rb | 19 + app/views/dmsf/_path.html.erb | 9 + app/views/dmsf/_user_pref.html.erb | 15 + app/views/dmsf/email_entries.html.erb | 43 + app/views/dmsf/index.html.erb | 305 ++++++++ app/views/dmsf_detail/_file_locked.html.erb | 1 + .../dmsf_detail/_file_new_revision.html.erb | 68 ++ app/views/dmsf_detail/_upload_file.html.erb | 38 + .../dmsf_detail/_upload_file_locked.html.erb | 31 + app/views/dmsf_detail/file_detail.html.erb | 114 +++ app/views/dmsf_detail/folder_detail.html.erb | 29 + app/views/dmsf_detail/upload_file.html.erb | 2 + app/views/dmsf_detail/upload_files.html.erb | 41 + .../dmsf_mailer/files_deleted.text.html.rhtml | 16 + .../files_deleted.text.plain.rhtml | 5 + .../dmsf_mailer/files_updated.text.html.rhtml | 21 + .../files_updated.text.plain.rhtml | 7 + app/views/settings/_dmsf_settings.erb | 42 + assets/images/approve.png | Bin 0 -> 535 bytes assets/images/approved.png | Bin 0 -> 262 bytes assets/images/askforapproval.png | Bin 0 -> 641 bytes assets/images/delete.png | Bin 0 -> 459 bytes assets/images/dmsf.png | Bin 0 -> 3588 bytes assets/images/filedetails.png | Bin 0 -> 803 bytes assets/images/lock.png | Bin 0 -> 797 bytes assets/images/locked.png | Bin 0 -> 530 bytes assets/images/lockedbycurrent.png | Bin 0 -> 1306 bytes assets/images/notify.png | Bin 0 -> 569 bytes assets/images/notifynot.png | Bin 0 -> 578 bytes assets/images/operations.png | Bin 0 -> 459 bytes assets/images/plupload/backgrounds.gif | Bin 0 -> 2977 bytes assets/images/plupload/buttons-disabled.png | Bin 0 -> 1292 bytes assets/images/plupload/buttons.png | Bin 0 -> 1439 bytes assets/images/plupload/delete.gif | Bin 0 -> 180 bytes assets/images/plupload/done.gif | Bin 0 -> 1024 bytes assets/images/plupload/error.gif | Bin 0 -> 994 bytes assets/images/plupload/plupload-bw.png | Bin 0 -> 2105 bytes assets/images/plupload/plupload.png | Bin 0 -> 3641 bytes assets/images/plupload/throbber.gif | Bin 0 -> 1922 bytes assets/images/plupload/transp50.png | Bin 0 -> 399 bytes assets/images/unlock.png | Bin 0 -> 748 bytes assets/images/unlockdisabled.png | Bin 0 -> 662 bytes assets/images/waitingforapproval.png | Bin 0 -> 412 bytes assets/images/workflowdisabled.png | Bin 0 -> 1392 bytes assets/javascripts/plupload/gears_init.js | 86 ++ assets/javascripts/plupload/i18n/cs.js | 14 + assets/javascripts/plupload/i18n/da.js | 12 + assets/javascripts/plupload/i18n/de.js | 25 + assets/javascripts/plupload/i18n/es.js | 25 + assets/javascripts/plupload/i18n/fr.js | 12 + assets/javascripts/plupload/i18n/it.js | 21 + assets/javascripts/plupload/i18n/nl.js | 21 + assets/javascripts/plupload/i18n/pt-br.js | 35 + assets/javascripts/plupload/i18n/ru.js | 21 + assets/javascripts/plupload/i18n/sv.js | 12 + .../plupload/jquery.plupload.queue.min.js | 1 + .../plupload/jquery.ui.plupload.js | 737 ++++++++++++++++++ .../plupload/plupload.browserplus.min.js | 1 + .../plupload/plupload.flash.min.js | 1 + .../javascripts/plupload/plupload.flash.swf | Bin 0 -> 14493 bytes .../javascripts/plupload/plupload.full.min.js | 2 + .../plupload/plupload.gears.min.js | 1 + .../plupload/plupload.html4.min.js | 1 + .../plupload/plupload.html5.min.js | 1 + assets/javascripts/plupload/plupload.min.js | 2 + .../plupload/plupload.silverlight.min.js | 1 + .../plupload/plupload.silverlight.xap | Bin 0 -> 43313 bytes assets/stylesheets/dmsf.css | 119 +++ .../plupload/jquery.ui.plupload.css | 138 ++++ .../stylesheets/plupload/plupload.queue.css | 177 +++++ config/locales/cs.yml | 15 + config/locales/de.yml | 16 + config/locales/en-GB.yml | 16 + config/locales/en.yml | 16 + config/locales/es.yml | 16 + config/locales/fr.yml | 16 + config/locales/ru.yml | 16 + db/migrate/01_create_hierarchy.rb | 78 ++ init.rb | 60 ++ lib/tasks/dmsf_convert_documents.rake | 127 +++ test/fixtures/dmsf_folders.yml | 24 + test/functional/dmsf_controller_test.rb | 8 + test/test_helper.rb | 5 + test/unit/dmsf_folder_test.rb | 11 + 98 files changed, 4050 insertions(+) create mode 100644 README.txt create mode 100644 app/controllers/dmsf_controller.rb create mode 100644 app/controllers/dmsf_detail_controller.rb create mode 100644 app/controllers/dmsf_state_controller.rb create mode 100644 app/helpers/dmsf_access_error.rb create mode 100644 app/helpers/dmsf_content_error.rb create mode 100644 app/helpers/dmsf_helper.rb create mode 100644 app/helpers/dmsf_upload.rb create mode 100644 app/helpers/dmsf_zip.rb create mode 100644 app/models/dmsf_file.rb create mode 100644 app/models/dmsf_file_lock.rb create mode 100644 app/models/dmsf_file_revision.rb create mode 100644 app/models/dmsf_folder.rb create mode 100644 app/models/dmsf_mailer.rb create mode 100644 app/models/dmsf_user_pref.rb create mode 100644 app/views/dmsf/_path.html.erb create mode 100644 app/views/dmsf/_user_pref.html.erb create mode 100644 app/views/dmsf/email_entries.html.erb create mode 100644 app/views/dmsf/index.html.erb create mode 100644 app/views/dmsf_detail/_file_locked.html.erb create mode 100644 app/views/dmsf_detail/_file_new_revision.html.erb create mode 100644 app/views/dmsf_detail/_upload_file.html.erb create mode 100644 app/views/dmsf_detail/_upload_file_locked.html.erb create mode 100644 app/views/dmsf_detail/file_detail.html.erb create mode 100644 app/views/dmsf_detail/folder_detail.html.erb create mode 100644 app/views/dmsf_detail/upload_file.html.erb create mode 100644 app/views/dmsf_detail/upload_files.html.erb create mode 100644 app/views/dmsf_mailer/files_deleted.text.html.rhtml create mode 100644 app/views/dmsf_mailer/files_deleted.text.plain.rhtml create mode 100644 app/views/dmsf_mailer/files_updated.text.html.rhtml create mode 100644 app/views/dmsf_mailer/files_updated.text.plain.rhtml create mode 100644 app/views/settings/_dmsf_settings.erb create mode 100644 assets/images/approve.png create mode 100644 assets/images/approved.png create mode 100644 assets/images/askforapproval.png create mode 100644 assets/images/delete.png create mode 100644 assets/images/dmsf.png create mode 100644 assets/images/filedetails.png create mode 100644 assets/images/lock.png create mode 100644 assets/images/locked.png create mode 100644 assets/images/lockedbycurrent.png create mode 100644 assets/images/notify.png create mode 100644 assets/images/notifynot.png create mode 100644 assets/images/operations.png create mode 100644 assets/images/plupload/backgrounds.gif create mode 100644 assets/images/plupload/buttons-disabled.png create mode 100644 assets/images/plupload/buttons.png create mode 100644 assets/images/plupload/delete.gif create mode 100644 assets/images/plupload/done.gif create mode 100644 assets/images/plupload/error.gif create mode 100644 assets/images/plupload/plupload-bw.png create mode 100644 assets/images/plupload/plupload.png create mode 100644 assets/images/plupload/throbber.gif create mode 100644 assets/images/plupload/transp50.png create mode 100644 assets/images/unlock.png create mode 100644 assets/images/unlockdisabled.png create mode 100644 assets/images/waitingforapproval.png create mode 100644 assets/images/workflowdisabled.png create mode 100644 assets/javascripts/plupload/gears_init.js create mode 100644 assets/javascripts/plupload/i18n/cs.js create mode 100644 assets/javascripts/plupload/i18n/da.js create mode 100644 assets/javascripts/plupload/i18n/de.js create mode 100644 assets/javascripts/plupload/i18n/es.js create mode 100644 assets/javascripts/plupload/i18n/fr.js create mode 100644 assets/javascripts/plupload/i18n/it.js create mode 100644 assets/javascripts/plupload/i18n/nl.js create mode 100644 assets/javascripts/plupload/i18n/pt-br.js create mode 100644 assets/javascripts/plupload/i18n/ru.js create mode 100644 assets/javascripts/plupload/i18n/sv.js create mode 100644 assets/javascripts/plupload/jquery.plupload.queue.min.js create mode 100644 assets/javascripts/plupload/jquery.ui.plupload.js create mode 100644 assets/javascripts/plupload/plupload.browserplus.min.js create mode 100644 assets/javascripts/plupload/plupload.flash.min.js create mode 100644 assets/javascripts/plupload/plupload.flash.swf create mode 100644 assets/javascripts/plupload/plupload.full.min.js create mode 100644 assets/javascripts/plupload/plupload.gears.min.js create mode 100644 assets/javascripts/plupload/plupload.html4.min.js create mode 100644 assets/javascripts/plupload/plupload.html5.min.js create mode 100644 assets/javascripts/plupload/plupload.min.js create mode 100644 assets/javascripts/plupload/plupload.silverlight.min.js create mode 100644 assets/javascripts/plupload/plupload.silverlight.xap create mode 100644 assets/stylesheets/dmsf.css create mode 100644 assets/stylesheets/plupload/jquery.ui.plupload.css create mode 100644 assets/stylesheets/plupload/plupload.queue.css create mode 100644 config/locales/cs.yml create mode 100644 config/locales/de.yml create mode 100644 config/locales/en-GB.yml create mode 100644 config/locales/en.yml create mode 100644 config/locales/es.yml create mode 100644 config/locales/fr.yml create mode 100644 config/locales/ru.yml create mode 100644 db/migrate/01_create_hierarchy.rb create mode 100644 init.rb create mode 100644 lib/tasks/dmsf_convert_documents.rake create mode 100644 test/fixtures/dmsf_folders.yml create mode 100644 test/functional/dmsf_controller_test.rb create mode 100644 test/test_helper.rb create mode 100644 test/unit/dmsf_folder_test.rb diff --git a/README.txt b/README.txt new file mode 100644 index 00000000..f03e046f --- /dev/null +++ b/README.txt @@ -0,0 +1,80 @@ +1 Installation and Setup + +1.1. Required packages + +For zipped content download you must have rubyzip gem installed. + +To use file/document search capabilities you must install xapian (http://xapian.org) search engine. +That means libxapian-ruby1.8 and xapian-omega packages. To index some files with omega you may have to install some other +packages like xpdf, antiword, ... + +From Omega documentation: + + * PDF (.pdf) if pdftotext is available (comes with xpdf) + * PostScript (.ps, .eps, .ai) if ps2pdf (from ghostscript) and pdftotext (comes with xpdf) are available + * OpenOffice/StarOffice documents (.sxc, .stc, .sxd, .std, .sxi, .sti, .sxm, .sxw, .sxg, .stw) if unzip is available + * OpenDocument format documents (.odt, .ods, .odp, .odg, .odc, .odf, .odb, .odi, .odm, .ott, .ots, .otp, .otg, .otc, .otf, .oti, .oth) if unzip is available + * MS Word documents (.doc, .dot) if antiword is available + * MS Excel documents (.xls, .xlb, .xlt) if xls2csv is available (comes with catdoc) + * MS Powerpoint documents (.ppt, .pps) if catppt is available (comes with catdoc) + * MS Office 2007 documents (.docx, .dotx, .xlsx, .xlst, .pptx, .potx, .ppsx) if unzip is available + * Wordperfect documents (.wpd) if wpd2text is available (comes with libwpd) + * MS Works documents (.wps, .wpt) if wps2text is available (comes with libwps) + * AbiWord documents (.abw) + * Compressed AbiWord documents (.zabw) if gzip is available + * Rich Text Format documents (.rtf) if unrtf is available + * Perl POD documentation (.pl, .pm, .pod) if pod2text is available + * TeX DVI files (.dvi) if catdvi is available + * DjVu files (.djv, .djvu) if djvutxt is available + * XPS files (.xps) if unzip is available + +On Debinan (Squeeze) use: +apt-get install xapian-ruby1.8 xapian-omega libxapian-dev xpdf antiword unzip antiword\ + catdoc libwpd8c2a libwps-0.1-1 gzip unrtf catdvi djview djview3 libzip-ruby1.8 + +In case of package shortage it is possible to use: +gem install xapian-full rubyzip + +1.2. Plugin installation + +Install redmine_dmsf into vendor/plugins directory with: +* Put redmine_dmsf plugin content into vendor/plugins +* Initialize database: + rake db:migrate:plugins RAILS_ENV="production" +* The access rights must be set for web server + Example: + chown -R www-data:www-data /opt/redmine/vendor/plugins/redmine_dmsf +* Restart web server + +Then you must configure plugin in Administration -> Plugins -> DMSF -> Configure + +It is also neccessary to assign DMSF permissions to appropriate roles. + +DMSF act as project module so you must check DMSF in project settings. + +Search options will now contain "Dmsf files" check, that allows you to search DMSF content. + +To include Wiki DMSF link help: +* In file public/help/wiki_syntax_detailed.html include after document link description: + + +1.3. Setup + +It is necessary to index DMSF files with omega before searching attemts to recieve some output: + +omindex -s english -l 1 --db {path to index database from plugin configuration} {path to storage from plugin configuration} + +This command must be run on regular basis (e.g. from cron) + +Example of cron job (once per hour at 8th minute): +* 8 * * * root /usr/bin/omindex -s english -l 1 --db /opt/redmine/files/dmsf_index /opt/redmine/files/dmsf + +Use omindex -h for help. diff --git a/app/controllers/dmsf_controller.rb b/app/controllers/dmsf_controller.rb new file mode 100644 index 00000000..2742508e --- /dev/null +++ b/app/controllers/dmsf_controller.rb @@ -0,0 +1,200 @@ +class DmsfController < ApplicationController + unloadable + + before_filter :find_project + before_filter :authorize + before_filter :find_folder, :only => [:index, :entries_operation, :email_entries_send] + before_filter :find_file, :only => [:download_file] + + helper :sort + include SortHelper + + def index + sort_init ["title", "asc"] + sort_update ["title", "size", "modified", "version", "author"] + + if @folder.nil? + @subfolders = DmsfFolder.project_root_folders(@project) + @files = DmsfFile.project_root_files(@project) + else + @subfolders = @folder.subfolders + @files = @folder.files + end + + @files.sort! do |a,b| + case @sort_criteria.first_key + when "size" then a.last_revision.size <=> b.last_revision.size + when "modified" then a.last_revision.updated_at <=> b.last_revision.updated_at + when "version" then + result = a.last_revision.major_version <=> b.last_revision.major_version + result == 0 ? a.last_revision.minor_version <=> b.last_revision.minor_version : result + when "author" then a.last_revision.user <=> b.last_revision.user + else a.last_revision.title <=> b.last_revision.title + end + end + + if !@sort_criteria.first_asc? + @subfolders.reverse! + @files.reverse! + end + + respond_to do |format| + format.html { render :layout => !request.xhr? } + format.api + end + + end + + def download_file + @revision = @file.last_revision + Rails.logger.info "#{Time.now} from #{request.remote_ip}/#{request.env["HTTP_X_FORWARDED_FOR"]}: #{User.current.login} downloaded #{@project.identifier}://#{@file.dmsf_path_str} revision #{@revision.id}" + send_revision + end + + def download_revision + @revision = DmsfFileRevision.find(params[:revision_id]) + if @revision.deleted + render_404 + else + Rails.logger.info "#{Time.now} from #{request.remote_ip}/#{request.env["HTTP_X_FORWARDED_FOR"]}: #{User.current.login} downloaded #{@project.identifier}://#{@revision.file.dmsf_path_str} revision #{@revision.id}" + check_project(@revision.file) + send_revision + end + rescue DmsfAccessError + render_403 + end + + def entries_operation + selected_folders = params[:subfolders] + selected_files = params[:files] + + if selected_folders.nil? && selected_files.nil? + flash[:warning] = l(:warning_no_entries_selected) + redirect_to :action => "index", :id => @project, :folder_id => @folder + return + end + + if !params[:email_entries].blank? + email_entries(selected_folders, selected_files) + else + download_entries(selected_folders, selected_files) + end + rescue ZipMaxFilesError + flash[:error] = l(:error_max_files_exceeded) + Setting.plugin_redmine_dmsf["dmsf_max_file_download"].to_i.to_s + redirect_to({:controller => "dmsf", :action => "index", :id => @project, :folder_id => @folder}) + rescue DmsfAccessError + render_403 + end + + def email_entries_send + @email_params = params[:email] + if @email_params["to"].strip.blank? + flash[:error] = l(:error_email_to_must_be_entered) + render :action => "email_entries" + return + end + DmsfMailer.deliver_send_documents(User.current, @email_params["to"], @email_params["cc"], + @email_params["subject"], @email_params["zipped_content"], @email_params["body"]) + File.delete(@email_params["zipped_content"]) + flash[:notice] = l(:notice_email_sent) + redirect_to({:controller => "dmsf", :action => "index", :id => @project, :folder_id => @folder}) + end + + class ZipMaxFilesError < StandardError + end + + private + + def email_entries(selected_folders, selected_files) + zip = DmsfZip.new + zip_entries(zip, selected_folders, selected_files) + + ziped_content = "#{DmsfHelper.temp_dir}/#{DmsfHelper.temp_filename("dmsf_email_sent_documents.zip")}"; + + File.open(ziped_content, "wb") do |f| + zip_file = File.open(zip.finish, "rb") + while (buffer = zip_file.read(8192)) + f.write(buffer) + end + end + + Rails.logger.info "#{Time.now} from #{request.remote_ip}/#{request.env["HTTP_X_FORWARDED_FOR"]}: #{User.current.login} emailed from project #{@project.identifier}:" + selected_folders.each {|folder| Rails.logger.info "\tFolder #{folder}"} unless selected_folders.nil? + selected_files.each {|file| Rails.logger.info "\tFile #{file}"} unless selected_files.nil? + + @email_params = {"zipped_content" => ziped_content} + render :action => "email_entries" + ensure + zip.close unless zip.nil? + end + + def download_entries(selected_folders, selected_files) + zip = DmsfZip.new + zip_entries(zip, selected_folders, selected_files) + + Rails.logger.info "#{Time.now} from #{request.remote_ip}/#{request.env["HTTP_X_FORWARDED_FOR"]}: #{User.current.login} downloaded from project #{@project.identifier}:" + selected_folders.each {|folder| Rails.logger.info "\tFolder #{folder}"} unless selected_folders.nil? + selected_files.each {|file| Rails.logger.info "\tFile #{file}"} unless selected_files.nil? + + send_file(zip.finish, + :filename => filename_for_content_disposition(@project.name + "-" + DateTime.now.strftime("%y%m%d%H%M%S") + ".zip"), + :type => "application/zip", + :disposition => "attachment") + ensure + zip.close unless zip.nil? + end + + 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.find(selected_folder_id)) + zip.add_folder(folder) unless folder.nil? + end + end + if selected_files && selected_files.is_a?(Array) + selected_files.each do |selected_file_id| + check_project(file = DmsfFile.find(selected_file_id)) + zip.add_file(file) unless file.nil? + end + end + + max_files = 0 + max_files = Setting.plugin_redmine_dmsf["dmsf_max_file_download"].to_i + if max_files > 0 && zip.file_count > max_files + raise ZipMaxFilesError, zip.file_count + end + + zip + end + + def send_revision + send_file(@revision.disk_file, + :filename => filename_for_content_disposition(@revision.file.name), + :type => @revision.detect_content_type, + :disposition => "attachment") + end + + def find_project + @project = Project.find(params[:id]) + end + + def find_folder + @folder = DmsfFolder.find(params[:folder_id]) if params.keys.include?("folder_id") + check_project(@folder) + rescue DmsfAccessError + render_403 + end + + def find_file + check_project(@file = DmsfFile.find(params[:file_id])) + rescue DmsfAccessError + render_403 + end + + def check_project(entry) + if !entry.nil? && entry.project != @project + raise DmsfAccessError, "Entry project doesn't match current project" + end + end + +end diff --git a/app/controllers/dmsf_detail_controller.rb b/app/controllers/dmsf_detail_controller.rb new file mode 100644 index 00000000..fab12cf2 --- /dev/null +++ b/app/controllers/dmsf_detail_controller.rb @@ -0,0 +1,215 @@ +class DmsfDetailController < ApplicationController + unloadable + + before_filter :find_project + before_filter :authorize + before_filter :find_folder, :only => [:create_folder, :delete_folder, :save_folder, + :upload_files, :commit_files, :folder_detail] + before_filter :find_file, :only => [:save_file, :delete_file, :file_detail] + + def create_folder + @new_folder = DmsfFolder.create_from_params(@project, @folder, params[:dmsf_folder]) + if @new_folder.valid? + flash[:notice] = "Folder created" + Rails.logger.info "#{Time.now} from #{request.remote_ip}/#{request.env["HTTP_X_FORWARDED_FOR"]}: #{User.current.login} created folder #{@project.identifier}://#{@new_folder.dmsf_path_str}" + else + flash[:error] = "Folder creation failed: Title must be entered" + end + + redirect_to({:controller => "dmsf", :action => "index", :id => @project, :folder_id => @folder}) + end + + def delete_folder + check_project(@delete_folder = DmsfFolder.find(params[:delete_folder_id])) + if !@delete_folder.nil? + if @delete_folder.subfolders.empty? && @delete_folder.files.empty? + @delete_folder.destroy + flash[:notice] = "Folder deleted" + Rails.logger.info "#{Time.now} from #{request.remote_ip}/#{request.env["HTTP_X_FORWARDED_FOR"]}: #{User.current.login} deleted folder #{@project.identifier}://#{@delete_folder.dmsf_path_str}" + else + flash[:error] = "Folder is not empty" + end + end + + redirect_to :controller => "dmsf", :action => "index", :id => @project, :folder_id => @folder + rescue DmsfAccessError + render_403 + end + + def folder_detail + end + + def save_folder + @folder.description = params[:description] + + if params[:title].blank? + flash.now[:error] = "Title must be entered" + render "folder_detail" + return + end + + @folder.name = params[:title] + + if !@folder.valid? + flash.now[:error] = "Title is already used" + render "folder_detail" + return + end + + @folder.save! + Rails.logger.info "#{Time.now} from #{request.remote_ip}/#{request.env["HTTP_X_FORWARDED_FOR"]}: #{User.current.login} updated folder #{@project.identifier}://#{@folder.dmsf_path_str}" + flash[:notice] = "Folder details were saved" + redirect_to :controller => "dmsf", :action => "index", :id => @project, :folder_id => @folder + end + + #TODO: show lock/unlock history + def file_detail + end + + def delete_file + if !@file.nil? + if @file.locked_for_user? + flash[:error] = "File is locked" + else + @file.deleted = true + @file.deleted_by_user = User.current + @file.save + flash[:notice] = "File deleted" + Rails.logger.info "#{Time.now} from #{request.remote_ip}/#{request.env["HTTP_X_FORWARDED_FOR"]}: #{User.current.login} deleted file #{@project.identifier}://#{@file.dmsf_path_str}" + DmsfMailer.deliver_files_deleted(User.current, [@file]) + end + end + redirect_to :controller => "dmsf", :action => "index", :id => @project, :folder_id => @file.folder + end + + def delete_revision + @revision = DmsfFileRevision.find(params[:revision_id]) + check_project(@revision.file) + if @revision.file.locked_for_user? + flash[:error] = "File is locked" + else + if !@revision.nil? && !@revision.deleted + if @revision.file.revisions.size <= 1 + flash[:error] = "At least one revision must be present" + else + @revision.deleted = true + @revision.deleted_by_user = User.current + @revision.save + flash[:notice] = "Revision deleted" + Rails.logger.info "#{Time.now} from #{request.remote_ip}/#{request.env["HTTP_X_FORWARDED_FOR"]}: #{User.current.login} deleted revision #{@project.identifier}://#{@revision.file.dmsf_path_str}/#{@revision.id}" + end + end + end + redirect_to :action => "file_detail", :id => @project, :file_id => @revision.file + end + + def upload_files + uploaded_files = params[:uploaded_files] + @uploads = [] + if uploaded_files && uploaded_files.is_a?(Hash) + # standard file input uploads + uploaded_files.each_value do |uploaded_file| + @uploads.push(DmsfUpload.create_from_uploaded_file(@project, @folder, uploaded_file)) + end + else + # plupload multi upload completed + uploaded = params[:uploaded] + if uploaded && uploaded.is_a?(Hash) + uploaded.each_value do |uploaded_file| + @uploads.push(DmsfUpload.new(@project, @folder, uploaded_file)) + end + end + end + end + + # plupload single file multi upload handling + def upload_file + @tempfile = params[:file] + @disk_filename = DmsfHelper.temp_filename(@tempfile.original_filename) + File.open("#{DmsfHelper.temp_dir}/#{@disk_filename}", "wb") do |f| + while (buffer = @tempfile.read(8192)) + f.write(buffer) + end + end + + render :layout => false + 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 = [] + commited_files.each_value do |commited_file| + file = DmsfFile.from_commited_file(@project, @folder, commited_file) + if file.locked_for_user? + flash[:error] = "One of files locked" + else + revision = DmsfFileRevision.from_commited_file(file, commited_file) + file.unlock if file.locked? + file.reload + files.push(file) + end + end + Rails.logger.info "#{Time.now} from #{request.remote_ip}/#{request.env["HTTP_X_FORWARDED_FOR"]}: #{User.current.login} uploaded for project #{@project.identifier}:" + files.each {|file| Rails.logger.info "\t#{file.dmsf_path_str}:"} + begin + DmsfMailer.deliver_files_updated(User.current, files) + rescue ActionView::MissingTemplate => e + Rails.logger.error "Could not send email notifications: " + e + end + end + redirect_to :controller => "dmsf", :action => "index", :id => @project, :folder_id => @folder + end + + #TODO: separate control for approval + #TODO: don't create revision if nothing change + def save_file + if @file.locked_for_user? + flash[:error] = "File locked" + else + saved_file = params[:file] + DmsfFileRevision.from_saved_file(@file, saved_file) + if @file.locked? + @file.unlock + flash[:notice] = "File unlocked, " + end + @file.reload + flash[:notice] = (flash[:notice].nil? ? "" : flash[:notice]) + "File revision created" + Rails.logger.info "#{Time.now} from #{request.remote_ip}/#{request.env["HTTP_X_FORWARDED_FOR"]}: #{User.current.login} created new revision of file #{@project.identifier}://#{@file.dmsf_path_str}" + begin + DmsfMailer.deliver_files_updated(User.current, [@file]) + rescue ActionView::MissingTemplate => e + Rails.logger.error "Could not send email notifications: " + e + end + end + redirect_to :action => "file_detail", :id => @project, :file_id => @file + end + + private + + def find_project + @project = Project.find(params[:id]) + end + + def find_folder + @folder = DmsfFolder.find(params[:folder_id]) if params.keys.include?("folder_id") + check_project(@folder) + rescue DmsfAccessError + render_403 + end + + def find_file + check_project(@file = DmsfFile.find(params[:file_id])) + rescue DmsfAccessError + render_403 + end + + def check_project(entry) + if !entry.nil? && entry.project != @project + raise DmsfAccessError, "Entry project doesn't match current project" + end + end + +end \ No newline at end of file diff --git a/app/controllers/dmsf_state_controller.rb b/app/controllers/dmsf_state_controller.rb new file mode 100644 index 00000000..5cf3be55 --- /dev/null +++ b/app/controllers/dmsf_state_controller.rb @@ -0,0 +1,106 @@ +class DmsfStateController < ApplicationController + unloadable + + before_filter :find_project + before_filter :authorize + before_filter :find_folder, :only => [:folder_notify_activate, :folder_notify_deactivate] + before_filter :find_file, :only => [:lock_file, :unlock_file, :file_notify_activate, :file_notify_deactivate] + + def lock_file + if @file.locked? + flash[:warning] = l(:warning_file_already_locked) + else + @file.lock + flash[:notice] = l(:notice_file_locked) + end + redirect_to :controller => "dmsf", :action => "index", :id => @project, :folder_id => @file.folder + end + + def unlock_file + if !@file.locked? + flash[:warning] = l(:warning_file_not_locked) + else + if @file.locks[0].user == User.current || User.current.allowed_to?(:force_file_unlock, @file.project) + @file.unlock + flash[:notice] = l(:notice_file_unlocked) + else + flash[:error] = l(:error_only_user_that_locked_file_can_unlock_it) + end + end + redirect_to :controller => "dmsf", :action => "index", :id => @project, :folder_id => @file.folder + end + + def user_pref + @user_pref = DmsfUserPref.for(@project, User.current) + @user_pref.email_notify = params[:email_notify]; + @user_pref.save + flash[:notice] = "Your preferences was saved" + redirect_to URI.unescape(params[:current]) + end + + def folder_notify_activate + if @folder.notification + flash[:warning] = "Folder notifications already activated" + else + @folder.notify_activate + flash[:notice] = "Folder notifications activated" + end + redirect_to :controller => "dmsf", :action => "index", :id => @project, :folder_id => @folder.folder + end + + def folder_notify_deactivate + if !@folder.notification + flash[:warning] = "Folder notifications already deactivated" + else + @folder.notify_deactivate + flash[:notice] = "Folder notifications deactivated" + end + redirect_to :controller => "dmsf", :action => "index", :id => @project, :folder_id => @folder.folder + end + + def file_notify_activate + if @file.notification + flash[:warning] = "File notifications already activated" + else + @file.notify_activate + flash[:notice] = "File notifications activated" + end + redirect_to :controller => "dmsf", :action => "index", :id => @project, :folder_id => @file.folder + end + + def file_notify_deactivate + if !@file.notification + flash[:warning] = "File notifications already deactivated" + else + @file.notify_deactivate + flash[:notice] = "File notifications deactivated" + end + redirect_to :controller => "dmsf", :action => "index", :id => @project, :folder_id => @file.folder + end + + private + + def find_project + @project = Project.find(params[:id]) + end + + def find_folder + @folder = DmsfFolder.find(params[:folder_id]) if params.keys.include?("folder_id") + check_project(@folder) + rescue DmsfAccessError + render_403 + end + + def find_file + check_project(@file = DmsfFile.find(params[:file_id])) + rescue DmsfAccessError + render_403 + end + + def check_project(entry) + if !entry.nil? && entry.project != @project + raise DmsfAccessError, "Entry project doesn't match current project" + end + end + +end \ No newline at end of file diff --git a/app/helpers/dmsf_access_error.rb b/app/helpers/dmsf_access_error.rb new file mode 100644 index 00000000..8a8d7722 --- /dev/null +++ b/app/helpers/dmsf_access_error.rb @@ -0,0 +1,3 @@ +class DmsfAccessError < StandardError + +end diff --git a/app/helpers/dmsf_content_error.rb b/app/helpers/dmsf_content_error.rb new file mode 100644 index 00000000..fbbd2f29 --- /dev/null +++ b/app/helpers/dmsf_content_error.rb @@ -0,0 +1,3 @@ +class DmsfContentError < StandardError + +end diff --git a/app/helpers/dmsf_helper.rb b/app/helpers/dmsf_helper.rb new file mode 100644 index 00000000..7f815d1e --- /dev/null +++ b/app/helpers/dmsf_helper.rb @@ -0,0 +1,26 @@ +require "tmpdir" + +module DmsfHelper + + def self.temp_dir + Dir.tmpdir + end + + def self.temp_filename(filename) + filename = sanitize_filename(filename) + timestamp = DateTime.now.strftime("%y%m%d%H%M%S") + while File.exist?(File.join(temp_dir, "#{timestamp}_#{filename}")) + timestamp.succ! + end + "#{timestamp}_#{filename}" + end + + def self.sanitize_filename(filename) + # get only the filename, not the whole path + just_filename = File.basename(filename.gsub('\\\\', '/')) + + # Finally, replace all non alphanumeric, hyphens or periods with underscore + just_filename.gsub(/[^\w\.\-]/,'_') + end + +end diff --git a/app/helpers/dmsf_upload.rb b/app/helpers/dmsf_upload.rb new file mode 100644 index 00000000..f81fc9ce --- /dev/null +++ b/app/helpers/dmsf_upload.rb @@ -0,0 +1,59 @@ +class DmsfUpload + attr_accessor :name + + attr_accessor :disk_filename + attr_reader :size + attr_accessor :mime_type + attr_accessor :title + attr_accessor :description + + attr_accessor :comment + attr_accessor :major_version + attr_accessor :minor_version + attr_accessor :locked + + def disk_file + "#{DmsfHelper.temp_dir}/#{self.disk_filename}" + end + + 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, + } + + File.open("#{DmsfHelper.temp_dir}/#{uploaded["disk_filename"]}", "wb") do |f| + while (buffer = uploaded_file.read(8192)) + f.write(buffer) + end + end + DmsfUpload.new(project, folder, uploaded) + end + + def initialize(project, folder, uploaded) + @name = uploaded["original_filename"] + + dmsf_file = DmsfFile.find_file_by_name(project, folder, @name) + + @disk_filename = uploaded["disk_filename"] + @mime_type = uploaded["content_type"] + @size = File.size(disk_file) + + if dmsf_file.nil? || dmsf_file.last_revision.nil? + @title = DmsfFileRevision.filename_to_title(@name) + @description = nil + @major_version = 0 + @minor_version = 0 + else + last_revision = dmsf_file.last_revision + @title = last_revision.title + @description = last_revision.description + @major_version = last_revision.major_version + @minor_version = last_revision.minor_version + end + + @locked = !dmsf_file.nil? && dmsf_file.locked_for_user? + end + +end \ No newline at end of file diff --git a/app/helpers/dmsf_zip.rb b/app/helpers/dmsf_zip.rb new file mode 100644 index 00000000..4a6d56ee --- /dev/null +++ b/app/helpers/dmsf_zip.rb @@ -0,0 +1,41 @@ +require 'zip/zip' +require 'zip/zipfilesystem' + +class DmsfZip + + attr_reader :file_count + + def initialize() + @zip = Tempfile.new(["dmsf_zip",".zip"]) + @zip_file = Zip::ZipOutputStream.new(@zip.path) + @file_count = 0 + end + + def finish + @zip_file.close unless @zip_file.nil? + @zip.path unless @zip.nil? + end + + def close + @zip_file.close unless @zip_file.nil? + @zip.close unless @zip.nil? + end + + def add_file(file) + @zip_file.put_next_entry(file.dmsf_path_str) + File.open(file.last_revision.disk_file, "rb") do |f| + buffer = "" + while (buffer = f.read(8192)) + @zip_file.write(buffer) + end + end + @file_count += 1 + end + + def add_folder(folder) + @zip_file.put_next_entry(folder.dmsf_path_str + "/") + folder.subfolders.each { |subfolder| self.add_folder(subfolder) } + folder.files.each { |file| self.add_file(file) } + end + +end \ No newline at end of file diff --git a/app/models/dmsf_file.rb b/app/models/dmsf_file.rb new file mode 100644 index 00000000..03151710 --- /dev/null +++ b/app/models/dmsf_file.rb @@ -0,0 +1,257 @@ +begin + require 'xapian' + $xapian_bindings_available = true +rescue LoadError + Rails.logger.info "REDMAIN_XAPIAN ERROR: No Ruby bindings for Xapian installed !!. PLEASE install Xapian search engine interface for Ruby." + $xapian_bindings_available = false +end + +class DmsfFile < ActiveRecord::Base + unloadable + belongs_to :project + belongs_to :folder, :class_name => "DmsfFolder", :foreign_key => "dmsf_folder_id" + has_many :revisions, :class_name => "DmsfFileRevision", :foreign_key => "dmsf_file_id", + :order => "major_version DESC, minor_version DESC, updated_at DESC", + :conditions => { :deleted => false } + has_many :locks, :class_name => "DmsfFileLock", :foreign_key => "dmsf_file_id", + :order => "updated_at DESC" + belongs_to :deleted_by_user, :class_name => "User", :foreign_key => "deleted_by_user_id" + + validates_presence_of :name + validate_on_create :validates_name_uniqueness + + def validates_name_uniqueness + errors.add(:name, "has already been taken") if + !DmsfFile.find_file_by_name(self.project, self.folder, self.name).nil? + end + + acts_as_event :title => Proc.new {|o| "#{o.name}"}, + :description => Proc.new {|o| "#{o.last_revision.title} - #{o.last_revision.description}" }, + :url => Proc.new {|o| {:controller => "dmsf", :action => "download_file", :id => o.project, :file_id => o}}, + :datetime => Proc.new {|o| o.updated_at }, + :author => Proc.new {|o| o.last_revision.user } + + def self.storage_path + storage_dir = Setting.plugin_redmine_dmsf["dmsf_storage_directory"].strip + if !File.exists?(storage_dir) + Dir.mkdir(storage_dir) + end + storage_dir + end + + def self.search(tokens, projects=nil, options={}) + tokens = [] << tokens unless tokens.is_a?(Array) + projects = [] << projects unless projects.nil? || projects.is_a?(Array) + + find_options = {:include => [:project,:revisions]} + find_options[:order] = "dmsf_files.updated_at " + (options[:before] ? 'DESC' : 'ASC') + + limit_options = {} + limit_options[:limit] = options[:limit] if options[:limit] + if options[:offset] + limit_options[:conditions] = "(dmsf_files.updated_at " + (options[:before] ? '<' : '>') + "'#{connection.quoted_date(options[:offset])}')" + end + + columns = ["dmsf_files.name","dmsf_file_revisions.title", "dmsf_file_revisions.description"] + columns = ["dmsf_file_revisions.title"] if options[:titles_only] + + token_clauses = columns.collect {|column| "(LOWER(#{column}) LIKE ?)"} + + sql = (['(' + token_clauses.join(' OR ') + ')'] * tokens.size).join(options[:all_words] ? ' AND ' : ' OR ') + find_options[:conditions] = [sql, * (tokens.collect {|w| "%#{w.downcase}%"} * token_clauses.size).sort] + + project_conditions = [] + project_conditions << (Project.allowed_to_condition(User.current, :view_dmsf_files)) + project_conditions << "project_id IN (#{projects.collect(&:id).join(',')})" unless projects.nil? + + results = [] + results_count = 0 + + with_scope(:find => {:conditions => [project_conditions.join(' AND ') + " AND #{DmsfFile.table_name}.deleted = :false", {:false => false}]}) do + with_scope(:find => find_options) do + results_count = count(:all) + results = find(:all, limit_options) + end + end + + if !options[:titles_only] && $xapian_bindings_available + database = Xapian::Database.new(Setting.plugin_redmine_dmsf["dmsf_index_database"].strip) + enquire = Xapian::Enquire.new(database) + + queryString = tokens.join(' ') + qp = Xapian::QueryParser.new() + stemmer = Xapian::Stem.new(Setting.plugin_redmine_dmsf['dmsf_stemming_lang'].strip) + qp.stemmer = stemmer + qp.database = database + + case Setting.plugin_redmine_dmsf['dmsf_stemming_strategy'].strip + when "STEM_NONE" then qp.stemming_strategy = Xapian::QueryParser::STEM_NONE + when "STEM_SOME" then qp.stemming_strategy = Xapian::QueryParser::STEM_SOME + when "STEM_ALL" then qp.stemming_strategy = Xapian::QueryParser::STEM_ALL + end + + if options[:all_words] + qp.default_op = Xapian::Query::OP_AND + else + qp.default_op = Xapian::Query::OP_OR + end + + query = qp.parse_query(queryString) + + enquire.query = query + matchset = enquire.mset(0, 1000) + + if !matchset.nil? + matchset.matches.each {|m| + docdata = m.document.data{url} + dochash = Hash[*docdata.scan(/(url|sample|modtime|type|size)=\/?([^\n\]]+)/).flatten] + filename = dochash["url"] + if !filename.nil? + dmsf_attrs = filename.split("_") + next unless results.select{|f| f.id.to_s == dmsf_attrs[1]}.empty? + + find_conditions = DmsfFile.merge_conditions(limit_options[:conditions], :id => dmsf_attrs[1] ) + dmsf_file = DmsfFile.find(:first, :conditions => find_conditions ) + + if !dmsf_file.nil? + if options[:offset] + if options[:before] + next if dmsf_file.updated_at < options[:offset] + else + next if dmsf_file.updated_at > options[:offset] + end + end + + allowed = User.current.allowed_to?(:view_dmsf_files, dmsf_file.project) + project_included = false + project_included = true if projects.nil? + if !project_included + projects.each {|x| + project_included = true if x[:id] == dmsf_file.project.id + } + end + + if (allowed && project_included) + results.push(dmsf_file) + results_count += 1 + end + end + end + } + end + end + + [results, results_count] + end + + def self.project_root_files(project) + find(:all, :conditions => + ["dmsf_folder_id is NULL and project_id = :project_id and deleted = :deleted", + {:project_id => project.id, :deleted => false}], :order => "name ASC") + end + + def self.find_file_by_name(project, folder, name) + if folder.nil? + find(:first, :conditions => + ["dmsf_folder_id is NULL and project_id = :project_id and name = :name and deleted = :deleted", + {:project_id => project.id, :name => name, :deleted => false}]) + else + find(:first, :conditions => + ["dmsf_folder_id = :folder_id and project_id = :project_id and name = :name and deleted = :deleted", + {:project_id => project.id, :folder_id => folder.id, :name => name, :deleted => false}]) + end + end + + def self.from_commited_file(project, folder, commited_file) + file = find_file_by_name(project, folder, commited_file["name"]) + + if file.nil? + file = DmsfFile.new + file.project = project + file.folder = folder + file.name = commited_file["name"] + file.save + end + + return file + end + + def new_storage_filename + filename = DmsfHelper.sanitize_filename(self.name) + timestamp = DateTime.now.strftime("%y%m%d%H%M%S") + while File.exist?(File.join(DmsfFile.storage_path, "#{timestamp}_#{self.id}_#{filename}")) + timestamp.succ! + end + "#{timestamp}_#{id}_#{filename}" + end + + def locked? + self.locks.empty? ? false : self.locks[0].locked + end + + def locked_for_user? + self.locked? && self.locks[0].user != User.current + end + + def lock + lock = DmsfFileLock.new + lock.file = self + lock.user = User.current + lock.locked = true + lock.save + self.reload + return lock + end + + def unlock + lock = DmsfFileLock.new + lock.file = self + lock.user = User.current + lock.locked = false + lock.save + self.reload + return lock + end + + def last_revision + self.revisions.empty? ? nil : self.revisions[0] + end + + def dmsf_path + path = self.folder.nil? ? [] : self.folder.dmsf_path + path.push(self) + path + end + + def dmsf_path_str + path = self.dmsf_path + string_path = path.map { |element| element.name } + string_path.join("/") + end + + def notify? + return true if self.notification + return true if folder && folder.notify? + return false + end + + def notify_deactivate + self.notification = false + self.save! + end + + def notify_activate + self.notification = true + self.save! + end + + def display_name + #if self.name.length > 33 + # extension = File.extname(self.name) + # return self.name[0, self.name.length - extension.length][0, 25] + "..." + extension + #else + return self.name + #end + end + +end \ No newline at end of file diff --git a/app/models/dmsf_file_lock.rb b/app/models/dmsf_file_lock.rb new file mode 100644 index 00000000..fdf28ce1 --- /dev/null +++ b/app/models/dmsf_file_lock.rb @@ -0,0 +1,5 @@ +class DmsfFileLock < ActiveRecord::Base + unloadable + belongs_to :file, :class_name => "DmsfFile", :foreign_key => "dmsf_file_id" + belongs_to :user +end \ No newline at end of file diff --git a/app/models/dmsf_file_revision.rb b/app/models/dmsf_file_revision.rb new file mode 100644 index 00000000..3e370135 --- /dev/null +++ b/app/models/dmsf_file_revision.rb @@ -0,0 +1,213 @@ +class DmsfFileRevision < ActiveRecord::Base + unloadable + belongs_to :file, :class_name => "DmsfFile", :foreign_key => "dmsf_file_id" + belongs_to :source_revision, :class_name => "DmsfFileRevision", :foreign_key => "source_dmsf_file_revision_id" + belongs_to :user + belongs_to :folder, :class_name => "DmsfFolder", :foreign_key => "dmsf_folder_id" + belongs_to :deleted_by_user, :class_name => "User", :foreign_key => "deleted_by_user_id" + + acts_as_event :title => Proc.new {|o| "DMSF updated: #{o.file.dmsf_path_str}"}, + :url => Proc.new {|o| {:controller => 'dmsf_detail', :action => 'file_detail', :id => o.file.project, :file_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}] + } + + def self.remove_extension(filename) + filename[0, (filename.length - File.extname(filename).length)] + end + + def self.filename_to_title(filename) + remove_extension(filename).gsub(/_+/, " "); + end + + def self.from_commited_file(dmsf_file, commited_file) + revision = DmsfFileRevision.new + + commited_disk_filename = commited_file["disk_filename"] + commited_disk_filepath = "#{DmsfHelper.temp_dir}/#{commited_disk_filename}" + commited_file["file"] = File.new(commited_disk_filepath, "rb") + commited_file["mime_type"] = Redmine::MimeType.of(commited_disk_filepath) + + begin + revision.from_form_post(dmsf_file, commited_file) + ensure + commited_file["file"].close + end + + revision.save + File.delete(commited_disk_filepath) + revision + end + + def self.from_saved_file(dmsf_file, saved_file) + revision = DmsfFileRevision.new + + if !saved_file["file"].nil? + #TODO: complete this for file renaming + #dmsf_file.name = saved_file["file"].original_filename + #dmsf_file.save + saved_file["mime_type"] = Redmine::MimeType.of(saved_file["file"].original_filename) + end + + revision.from_form_post(dmsf_file, saved_file) + + revision.save! + end + + def version + "#{self.major_version}.#{self.minor_version}" + end + + def project + self.file.project + end + + def disk_file + "#{DmsfFile.storage_path}/#{self.disk_filename}" + end + + def detect_content_type + content_type = self.mime_type + if content_type.blank? + content_type = Redmine::MimeType.of(self.disk_filename) + end + content_type.to_s + end + + def from_form_post(file, posted, source_revision = nil) + source_revision = file.last_revision if source_revision.nil? + + self.file = file + self.source_revision = source_revision + + if source_revision.nil? + from_form_post_create(posted) + else + from_form_post_existing(posted, source_revision) + end + + self.user = User.current + self.title = posted["title"] + self.description = posted["description"] + self.comment = posted["comment"] + + self + end + + def clone + new_revision = DmsfFileRevision.new + new_revision.file = self.file + new_revision.disk_filename = self.disk_filename + new_revision.size = self.size + new_revision.mime_type = self.mime_type + new_revision.title = self.title + new_revision.description = self.description + new_revision.workflow = self.workflow + new_revision.major_version = self.major_version + new_revision.minor_version = self.minor_version + + new_revision.source_revision = self + new_revision.user = User.current + return new_revision + end + + def set_workflow(workflow) + if User.current.allowed_to?(:file_approval, self.file.project) + self.workflow = workflow + else + if self.source_revision.nil? + self.workflow = workflow == 2 ? 1 : workflow + else + if workflow == 2 || self.source_revision.workflow == 1 || self.source_revision.workflow == 2 + self.workflow = 1 + else + self.workflow = workflow + end + end + end + end + + def display_title + #if self.title.length > 35 + # return self.title[0, 30] + "..." + #else + return self.title + #end + end + + private + + def from_form_post_create(posted) + if posted["file"].nil? + raise DmsfContentError, "First revision require uploaded file" + else + copy_file_content(posted) + self.major_version = case posted["version"] + when "major" then 1 + else 0 + end + self.minor_version = case posted["version"] + when "major" then 0 + else 1 + end + end + + set_workflow(posted["workflow"]) + end + + def from_form_post_existing(posted, source_revision) + self.major_version = source_revision.major_version + self.minor_version = source_revision.minor_version + if posted["file"].nil? + self.disk_filename = source_revision.disk_filename + self.minor_version = case posted["version"] + when "same" then self.minor_version + when "major" then 0 + else self.minor_version + 1 + end + self.mime_type = source_revision.mime_type + self.size = source_revision.size + else + copy_file_content(posted) + self.minor_version = case posted["version"] + when "major" then 0 + else self.minor_version + 1 + end + end + + self.major_version = case posted["version"] + when "major" then self.major_version + 1 + else self.major_version + end + + set_workflow(posted["workflow"]) + end + + def copy_file_content(posted) + self.disk_filename = self.file.new_storage_filename + File.open(self.disk_file, "wb") do |f| + buffer = "" + while (buffer = posted["file"].read(8192)) + f.write(buffer) + end + end + self.mime_type = posted["mime_type"] + self.size = File.size(self.disk_file) + + #TODO: move this to better place + self.name = self.file.name + self.folder = self.file.folder + end + +end \ No newline at end of file diff --git a/app/models/dmsf_folder.rb b/app/models/dmsf_folder.rb new file mode 100644 index 00000000..3b1a37df --- /dev/null +++ b/app/models/dmsf_folder.rb @@ -0,0 +1,60 @@ +class DmsfFolder < ActiveRecord::Base + unloadable + 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 => "name ASC" + has_many :files, :class_name => "DmsfFile", :foreign_key => "dmsf_folder_id", :order => "name ASC", + :conditions => { :deleted => false } + belongs_to :user + + validates_presence_of :name + validates_uniqueness_of :name, :scope => [:dmsf_folder_id, :project_id] + + def self.project_root_folders(project) + find(:all, :conditions => + ["dmsf_folder_id is NULL and project_id = :project_id", {:project_id => project.id}], :order => "name ASC") + end + + def self.create_from_params(project, parent_folder, params) + new_folder = DmsfFolder.new(params) + new_folder.project = project + new_folder.folder = parent_folder + new_folder.user = User.current + new_folder.save + new_folder + end + + def dmsf_path + folder = self + path = [] + while !folder.nil? + path.unshift(folder) + folder = folder.folder + end + path + end + + def dmsf_path_str + path = self.dmsf_path + string_path = path.map { |element| element.name } + string_path.join("/") + end + + def notify? + return true if self.notification + return true if folder && folder.notify? + return false + end + + def notify_deactivate + self.notification = false + self.save! + end + + def notify_activate + self.notification = true + self.save! + end + +end + diff --git a/app/models/dmsf_mailer.rb b/app/models/dmsf_mailer.rb new file mode 100644 index 00000000..5a036fa2 --- /dev/null +++ b/app/models/dmsf_mailer.rb @@ -0,0 +1,86 @@ +require "mailer" + +class DmsfMailer < Mailer + + def files_updated(user, files) + project = files[0].project + files = files.select { |file| file.notify? } + + redmine_headers "Project" => project.identifier + recipients get_notify_user_emails(user, files) + subject project.name + ": Dmsf files updated" + body :user => user, :files => files, :project => project + # TODO: correct way should be render_multipart("files_updated", body), but other plugin broke it + render_multipart(File.expand_path(File.dirname(__FILE__) + "/../views/dmsf_mailer/" + "files_updated"), body) + end + + def files_deleted(user, files) + project = files[0].project + files = files.select { |file| file.notify? } + + redmine_headers "Project" => project.identifier + recipients get_notify_user_emails(user, files) + subject project.name + ": Dmsf files deleted" + body :user => user, :files => files, :project => project + # TODO: correct way should be render_multipart("files_updated", body), but other plugin broke it + render_multipart(File.expand_path(File.dirname(__FILE__) + "/../views/dmsf_mailer/" + "files_deleted"), body) + end + + def send_documents(user, email_to, email_cc, email_subject, zipped_content, email_plain_body) + recipients email_to + if !email_cc.strip.blank? + cc email_cc + end + subject email_subject + from user.mail + content_type "multipart/mixed" + + part "text/plain" do |p| + p.body = email_plain_body + end + + zipped_content_data = open(zipped_content, "rb") {|io| io.read } + + attachment :content_type => "application/zip", + :filename => "Documents.zip", + :body => zipped_content_data + end + + private + + def get_notify_user_emails(user, files) + if files.empty? + return [] + end + + project = 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 + false + else + dmsf_user_prefs = DmsfUserPref.for(project, notify_user) + if dmsf_user_prefs.email_notify.nil? + case notify_user.mail_notification + when 'all' + true + when 'selected' + notify_member.mail_notification? + when 'only_my_events' + notify_user.allowed_to?(:file_approval, project) ? true : false + when 'only_owner' + notify_user.allowed_to?(:file_approval, project) ? true : false + else + false + end + else dmsf_user_prefs.email_notify + end + end + end + + notify_members.collect {|m| m.user.mail } + end + +end diff --git a/app/models/dmsf_user_pref.rb b/app/models/dmsf_user_pref.rb new file mode 100644 index 00000000..4942d515 --- /dev/null +++ b/app/models/dmsf_user_pref.rb @@ -0,0 +1,19 @@ +class DmsfUserPref < ActiveRecord::Base + unloadable + belongs_to :project + belongs_to :user + + validates_presence_of :project, :user + validates_uniqueness_of :user_id, :scope => [:project_id] + + def self.for(project, user) + user_pref = find(:first, :conditions => + ["project_id = :project_id and user_id = :user_id", + {:project_id => project.id, :user_id => user.id}]) + user_pref = DmsfUserPref.new({:project_id => project.id, :user_id => user.id, + :email_notify => nil}) if user_pref.nil? + return user_pref + end + +end + diff --git a/app/views/dmsf/_path.html.erb b/app/views/dmsf/_path.html.erb new file mode 100644 index 00000000..47bed373 --- /dev/null +++ b/app/views/dmsf/_path.html.erb @@ -0,0 +1,9 @@ +<%= link_to("Documents", {:controller => "dmsf", :action => "index", :id=> @project }) %> +<% path.each do |path_element| %> + / + <%= link_to(h(path_element.name), {:controller => "dmsf", :action => "index", :id=> @project, :folder_id => path_element}) %> + <% if path_element.notification %> + <%= image_tag("notify.png", :plugin => "redmine_dmsf", :alt => "Not. act.", + :title => "Notifications active") %> + <% end %> +<% end %> diff --git a/app/views/dmsf/_user_pref.html.erb b/app/views/dmsf/_user_pref.html.erb new file mode 100644 index 00000000..4f53dac4 --- /dev/null +++ b/app/views/dmsf/_user_pref.html.erb @@ -0,0 +1,15 @@ +
+ <% form_tag({:controller => "dmsf_state", :action => "user_pref", :id => @project, :current => URI.escape(request.url)}, + :method=>:post) do %> +
+ <%= submit_tag("Save", :title => "Save preferences", :style => "font-size: 0.9em;", :tabindex => 10) %> +
+

Your <%= l(:dmsf) %> preferences for project

+
+ Notifications: + <%= select_tag("email_notify", + options_for_select([["Default", nil], ["Activated", true], ["Deactivated", false]], + :selected => DmsfUserPref.for(@project, User.current).email_notify), :tabindex => 1) %> +
+ <% end %> +
\ No newline at end of file diff --git a/app/views/dmsf/email_entries.html.erb b/app/views/dmsf/email_entries.html.erb new file mode 100644 index 00000000..7973d0d2 --- /dev/null +++ b/app/views/dmsf/email_entries.html.erb @@ -0,0 +1,43 @@ +<% html_title("DMSF") %> + +
+
+ +<% path = @folder.nil? ? [] : @folder.dmsf_path %> +

+<%= render(:partial => 'path', :locals => {:path => path}) %> +

+ +

Send documents by email

+ +<% form_tag({:action => "email_entries_send", :id => @project, :folder_id => @folder}, + { :method=>:post, :class => "tabular"}) do %> +
+

+ <%= label_tag("", "From:") %> + <%= h(User.current.mail) %> +

+

+ <%= label_tag("email[to]", "To:") %> + <%= text_field_tag("email[to]", @email_params["to"], :style => "width: 90%;") %> +

+

+ <%= label_tag("email[cc]", "CC:") %> + <%= text_field_tag("email[cc]", @email_params["cc"], :style => "width: 90%;") %> +

+

+ <%= label_tag("email[subject]", "Subject:") %> + <%= text_field_tag("email[subject]", @email_params["subject"], :style => "width: 90%;") %> +

+

+ <%= label_tag("", "Documents:") %> + Documents.zip + <%= hidden_field_tag("email[zipped_content]", @email_params["zipped_content"]) %> +

+

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

+

<%= submit_tag("Send") %>

+
+<% end %> \ No newline at end of file diff --git a/app/views/dmsf/index.html.erb b/app/views/dmsf/index.html.erb new file mode 100644 index 00000000..32c290ee --- /dev/null +++ b/app/views/dmsf/index.html.erb @@ -0,0 +1,305 @@ +<% html_title("DMSF") %> + +
+ <% if User.current.allowed_to?(:folder_manipulation, @project) %> + <% unless @folder.nil? %> + <%= link_to("Details", + {:controller => "dmsf_detail", :action => "folder_detail", :id => @project, :folder_id => @folder}) %> | + <% end %> + <% form_for(DmsfFolder.new, :url => {:controller => "dmsf_detail", :action => "create_folder", :id => @project, :folder_id => @folder}, :html => {:method=>:post}) do |f| %> + Title: <%= f.text_field(:name) %><%= f.submit("Create folder") %> + <% end %> + <% end %> +
+ +<% path = @folder.nil? ? [] : @folder.dmsf_path %> +

+ <%= render(:partial => 'path', :locals => {:path => path}) %> +

+ +
+<%= textilizable(@folder.description) unless @folder.nil? %> +
+ +<% +form_tag({:action => "entries_operation", :id => @project, :folder_id => @folder}, :method => :post, + :class => "dmfs_entries", :id => "Entries") do +%> + <%= hidden_field_tag("action") %> + + + + + <%= sort_header_tag("title", :caption => "Title") %> + <%= sort_header_tag("size", :caption => "Size") %> + <%= sort_header_tag("modified", :caption => "Modified") %> + <%= sort_header_tag("version", :caption => "Ver.") %> + <%= sort_header_tag("author", :caption => "Author") %> + + + + + <% @subfolders.each do |subfolder| %> + + + + + + + + + + <% end %> + <% @files.each do |file| %> + + + + + + + + + + <% end %> + +
+ +
<%= check_box_tag("subfolders[]", subfolder.id, false, :title => "Check for multi download or email") %> + <%= link_to(h(subfolder.name), + {:action => "index", :folder_id => subfolder}, + :class => "icon icon-folder") %> + ---- +
+ <% if User.current.allowed_to?(:folder_manipulation, @project) %> + <%= link_to(image_tag("delete.png", :plugin => "redmine_dmsf"), + {:controller => "dmsf_detail", :action => "delete_folder", :id => @project, + :folder_id => @folder, :delete_folder_id => subfolder}, :class => "delete-link", + :title => "Delete") %> + <% end %> + <% if User.current.allowed_to?(:file_approval, @project) %> + <% if subfolder.notification %> + <%= link_to(image_tag("notify.png", :plugin => "redmine_dmsf"), + {:controller => "dmsf_state", :action => "folder_notify_deactivate", :id => @project, + :folder_id => subfolder}, :title => "Notifications active: Deactivate") %> + <% else %> + <%= link_to(image_tag("notifynot.png", :plugin => "redmine_dmsf"), + {:controller => "dmsf_state", :action => "folder_notify_activate", :id => @project, + :folder_id => subfolder}, :title => "Notifications not active: Activate") %> + <% end %> + <% end %> +

+
<%= check_box_tag("files[]", file.id, false, :title => "Check for zip download or email") %> + <%= link_to(h(file.last_revision.display_title), + {:action => "download_file", :id => @project, :file_id => file}, + :class => "icon icon-file #{Redmine::MimeType.css_class_of(file.name)}", + :title => "#{h(file.last_revision.title)} version #{file.last_revision.version} download") %> +
+ (<%= h(file.display_name) %>)

+
<%= number_to_human_size(file.last_revision.size) unless file.last_revision.nil? %> + <%= file.last_revision.updated_at.strftime("%Y-%m-%d %H:%M") unless file.last_revision.nil? %> + <% if file.locked_for_user? %> + <%= link_to(image_tag("locked.png", :plugin => "redmine_dmsf"), + {:controller => "users", :action => "show", :id => file.locks[0].user }, + :title => "Locked by " + file.locks[0].user.to_s) %> + <% else + if file.locked? %> + <%= image_tag("lockedbycurrent.png", :title => "Locked by you", + :plugin => "redmine_dmsf") %> + <% end %> + <% end %> + + <%= file.last_revision.version unless file.last_revision.nil? %> + <% case file.last_revision.workflow + when 1 then %><%= image_tag("waitingforapproval.png", :title => "Waiting for Approval", + :plugin => "redmine_dmsf") %> + <% when 2 then %><%= image_tag("approved.png", :title => "Approved", + :plugin => "redmine_dmsf") %> + <% end %> + <%= h(file.last_revision.user) unless file.last_revision.nil? %> +
+ <%= link_to(image_tag("filedetails.png", :plugin => "redmine_dmsf"), + {:controller => "dmsf_detail", :action => "file_detail", :id => @project, :file_id => file }, + :title => "#{h(file.last_revision.title)} details") %> +   + <% unless file.locked_for_user? && !User.current.allowed_to?(:force_file_unlock, @project)%> + <% if file.locked? %> + <%= link_to(image_tag("unlock.png", :plugin => "redmine_dmsf"), + {:controller => "dmsf_state", :action => "unlock_file", :id => @project, :file_id => file }, + :title => "Unlock to allow changes for other members") %> + <% else %> + <%= link_to(image_tag("lock.png", :plugin => "redmine_dmsf"), + {:controller => "dmsf_state", :action => "lock_file", :id => @project, :file_id => file }, + :title => "Lock to prevent changes for other members") %> + <% end %> +   + <% end %> + <% if User.current.allowed_to?(:file_manipulation, @project) %> + <%= link_to(image_tag("delete.png", :plugin => "redmine_dmsf"), + {:controller => "dmsf_detail", :action => "delete_file", :id => @project, + :file_id => file}, :class => "delete-link", :title => "Delete") %> + <% end %> + <% if User.current.allowed_to?(:file_approval, @project) %> + <% if file.notification %> + <%= link_to(image_tag("notify.png", :plugin => "redmine_dmsf"), + {:controller => "dmsf_state", :action => "file_notify_deactivate", :id => @project, + :file_id => file}, :title => "Notifications active: Deactivate") %> + <% else %> + <%= link_to(image_tag("notifynot.png", :plugin => "redmine_dmsf"), + {:controller => "dmsf_state", :action => "file_notify_activate", :id => @project, + :file_id => file}, :title => "Notifications not active: Activate") %> + <% end %> + <% end %> +

+
+
+ <%= submit_tag("Download", :title => "Download checked in zip archive", :name => "download_entries") %> + + <%= submit_tag("Email", :title => "Send checked by email", :name => "email_entries") %> +
+
+<% end %> + +
+

File Upload

+<% form_tag({:controller => "dmsf_detail", :action => "upload_files", :id => @project, :folder_id => @folder}, + :id => "uploadform", :method=>:post, :multipart => true) do %> +
+
+ File size: +
+There can be uploaded maximum of 20 files at once. To upload files greater than 2GB you must have 64b browser. +
+
+
+

+ +<%= file_field_tag("uploaded_files[1]", :size => 30, :id => nil) %> + +
+<%= link_to(l(:label_add_another_file), "#", :onclick => "dmsfAddFileField(); return false;" ) %> +(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>) + +

+<%= submit_tag("Upload") %> +
+<% end %> +
+ +<%= render(:partial => 'user_pref') %> + +<% content_for :header_tags do %> + <%= stylesheet_link_tag "dmsf", :plugin => "redmine_dmsf" %> + <%= stylesheet_link_tag "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/themes/base/jquery-ui.css" %> + <%= stylesheet_link_tag "plupload/jquery.ui.plupload.css", :plugin => "redmine_dmsf" %> + <%= javascript_include_tag "https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js" %> + <%= javascript_include_tag "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/jquery-ui.min.js" %> + <%= javascript_include_tag "plupload/gears_init.js", :plugin => "redmine_dmsf" %> + <%= javascript_include_tag "plupload/plupload.full.min.js", :plugin => "redmine_dmsf" %> + <%= javascript_include_tag "plupload/jquery.ui.plupload.js", :plugin => "redmine_dmsf" %> + +<% end %> \ No newline at end of file diff --git a/app/views/dmsf_detail/_file_locked.html.erb b/app/views/dmsf_detail/_file_locked.html.erb new file mode 100644 index 00000000..9a9d74a4 --- /dev/null +++ b/app/views/dmsf_detail/_file_locked.html.erb @@ -0,0 +1 @@ +

File locked!

\ No newline at end of file diff --git a/app/views/dmsf_detail/_file_new_revision.html.erb b/app/views/dmsf_detail/_file_new_revision.html.erb new file mode 100644 index 00000000..2bc3e816 --- /dev/null +++ b/app/views/dmsf_detail/_file_new_revision.html.erb @@ -0,0 +1,68 @@ +<% +disabled_workflow = [] +selected_workflow = nil +if !User.current.allowed_to?(:file_approval, @project) + disabled_workflow << 2 + current_workflow = @file.last_revision.workflow + if current_workflow == 1 || current_workflow == 2 + disabled_workflow << nil + selected_workflow = 1 + end +else + selected_workflow = @file.last_revision.workflow +end + +form_tag({:action => "save_file", :id => @project, :file_id => @file}, + :method=>:post, :multipart => true, :class => "tabular") do +%> +
+
+ Create New Revision [-] +
+
+

+ <%= label_tag("file[title]", "Title:") %> + <%= text_field_tag("file[title]", @file.last_revision.title, :size => "32") %> +

+

+ <%= label_tag("file[description]", "Description:") %> + <%= text_area_tag("file[description]", @file.last_revision.description, :rows=> "6") %> +

+ +

+ <%= label_tag("file[file]", "New content:") %> + <%= file_field_tag("file[file]", :size => 30, :id => "fileFileUpload") %> +
+ + (<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>) + +

+

+ <%= label_tag("file[version]_minor", "Version:") %> + <%= radio_button_tag("file[version]", "same", true, :id => "fileSameVersionRadio") %> + <%= @file.last_revision.major_version %>.<%= @file.last_revision.minor_version %> Same
+ <%= radio_button_tag("file[version]", "minor", false, :id => "fileMinorVersionRadio") %> + <%= @file.last_revision.major_version %>.<%= @file.last_revision.minor_version + 1 %> Minor
+ <%= radio_button_tag("file[version]", "major") %> + <%= @file.last_revision.major_version + 1 %>.0 Major
+

+

+ <%= label_tag("file[workflow]", "Workflow:") %> + <%= select_tag("file[workflow]", + options_for_select([["None", nil], ["Waiting for approval", 1], ["Approved", 2]], + :selected => selected_workflow, :disabled => disabled_workflow)) %> +

+
+
+

+ <%= label_tag("file[comment]", "Comment:") %> + <%= text_area_tag("file[comment]", "", :rows=> "2") %> +

+
+
+
+ <%= submit_tag("Create") %> +
+
+
+<% end %> diff --git a/app/views/dmsf_detail/_upload_file.html.erb b/app/views/dmsf_detail/_upload_file.html.erb new file mode 100644 index 00000000..35afb078 --- /dev/null +++ b/app/views/dmsf_detail/_upload_file.html.erb @@ -0,0 +1,38 @@ +
+ <%= hidden_field_tag("commited_files[#{i}][disk_filename]", upload.disk_filename) %> +
+ <%= h(upload.name) %> + <%= hidden_field_tag("commited_files[#{i}][name]", upload.name) %> +
+

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

+

+ <%= label_tag("commited_files[#{i}][description]", "Description:") %> + <%= text_area_tag("commited_files[#{i}][description]", upload.description, :rows=> "6") %> +

+

+ <%= label_tag("commited_files[#{i}][version]_minor", "Version:") %> + <%= radio_button_tag("commited_files[#{i}][version]", "minor", true) %> + <%= upload.major_version %>.<%= upload.minor_version + 1 %>
+ <%= radio_button_tag("commited_files[#{i}][version]", "major") %> + <%= upload.major_version + 1 %>.0
+

+
+
+

+ <%= label_tag("commited_files[#{i}][comment]", "Comment:") %> + <%= text_area_tag("commited_files[#{i}][comment]", upload.comment, :rows=> "2") %> +

+

+ <%= label_tag("", "Mime:") %> + <%= h(upload.mime_type) %> +

+

+ <%= label_tag("", "Size:") %> + <%= number_to_human_size(upload.size) %> +

+
+
+
\ No newline at end of file diff --git a/app/views/dmsf_detail/_upload_file_locked.html.erb b/app/views/dmsf_detail/_upload_file_locked.html.erb new file mode 100644 index 00000000..ff2ccad0 --- /dev/null +++ b/app/views/dmsf_detail/_upload_file_locked.html.erb @@ -0,0 +1,31 @@ +
+ <%= hidden_field_tag("commited_files[#{i}][disk_filename]", upload.disk_filename) %> +
+ <%= h(upload.name) %> +

File locked!

+
+

+ <%= label_tag("", "Title:") %> + <%= h(upload.title) %> +

+

+ <%= label_tag("", "Description:") %> + <%= h(upload.description) %> +

+

+ <%= label_tag("", "Version:") %> + <%= upload.major_version %>.<%= upload.minor_version %> +

+
+
+

+ <%= label_tag("", "Mime:") %> + <%= h(upload.mime_type) %> +

+

+ <%= label_tag("", "Size:") %> + <%= number_to_human_size(upload.size) %> +

+
+
+
diff --git a/app/views/dmsf_detail/file_detail.html.erb b/app/views/dmsf_detail/file_detail.html.erb new file mode 100644 index 00000000..1f436738 --- /dev/null +++ b/app/views/dmsf_detail/file_detail.html.erb @@ -0,0 +1,114 @@ +<% html_title("DMSF") %> + +
+
+ +<% path = @file.folder.nil? ? [] : @file.folder.dmsf_path %> +

+<%= render(:partial => "/dmsf/path", :locals => {:path => path}) %> +/ +<%= h(@file.last_revision.title) %>
<%= h(@file.name) %>
+<% if @file.notification %> + <%= image_tag("notify.png", :plugin => "redmine_dmsf", :alt => "Not. act.", + :title => "Notifications active") %> +<% end %> +

+ +<% if User.current.allowed_to?(:file_manipulation, @file.project) %> + <% if @file.locked_for_user? %> + <%= render(:partial => 'file_locked') %> + <% else %> + <%= render(:partial => 'file_new_revision') %> + <% end %> +<% end %> + +

Revisions

+<% @file.revisions.each do |revision| %> +
+
+ + <%= revision.source_revision.nil? ? "Created:" : "Changed:" %> + <%= revision.updated_at.strftime("%Y-%m-%d %H:%M:%S") %> + by + <%= h(revision.user) %> + +
+

+ <%= label_tag("", "Title:") %> + <%= h(revision.title) %> +

+

+ <%= label_tag("", "Description:") %> + <%= h(revision.description) %> +

+

+ <%= label_tag("", "Version:") %> + <%= revision.major_version %>.<%= revision.minor_version %> +

+

+ <%= label_tag("", "Workflow:") %> + <%= case revision.workflow + when 1 then "Waiting for approval" + when 2 then "Approved" + else "None" + end %> +

+
+
+

+ <%= label_tag("", "Comment:") %> + <%= h(revision.comment) %> +

+

+ <%= label_tag("", "Mime:") %> + <%= h(revision.mime_type) %> +

+

+ <%= label_tag("", "Size:") %> + <%= number_to_human_size(revision.size) %> +

+
+
+
+ <%= link_to("Download", + {:controller=>"dmsf", :action => "download_revision", :id => @project, :revision_id => revision}) %> + <% if User.current.allowed_to?(:file_approval, @project) %> + | <%= link_to("Delete", + {:action => "delete_revision", :id => @project, :revision_id => revision}, + :class => "delete-link", :title => "Delete revision") %> + <% end %> +
+
+
+<% end %> + +<% content_for :header_tags do %> + <%= stylesheet_link_tag "dmsf", :plugin => "redmine_dmsf" %> + <%= javascript_include_tag "https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js" %> + +<% end %> \ No newline at end of file diff --git a/app/views/dmsf_detail/folder_detail.html.erb b/app/views/dmsf_detail/folder_detail.html.erb new file mode 100644 index 00000000..e879493a --- /dev/null +++ b/app/views/dmsf_detail/folder_detail.html.erb @@ -0,0 +1,29 @@ +<% html_title("DMSF") %> + +
+
+ +

+<%= render(:partial => "/dmsf/path", :locals => {:path => @folder.dmsf_path}) %> +

+ +<% form_tag({:action => "save_folder", :id => @project, :folder_id => @folder}, + :method=>:post, :multipart => true, :class => "tabular") do %> +
+

+ <%= label_tag("title", "Title:") %> + <%= text_field_tag("title", @folder.name, :size => "32") %> +

+

+ <%= label_tag("description", "Description:") %> + <%= text_area_tag("description", @folder.description, :rows => 15, :class => "wiki-edit") %> +

+
+ <%= submit_tag("Save") %> +<% end %> + +<%= wikitoolbar_for "description" %> + +<% content_for :header_tags do %> + <%= stylesheet_link_tag "dmsf", :plugin => "redmine_dmsf" %> +<% end %> diff --git a/app/views/dmsf_detail/upload_file.html.erb b/app/views/dmsf_detail/upload_file.html.erb new file mode 100644 index 00000000..ab384492 --- /dev/null +++ b/app/views/dmsf_detail/upload_file.html.erb @@ -0,0 +1,2 @@ +{"original_filename":"<%= h(@tempfile.original_filename) %>", "content_type":"<%= h(@tempfile.content_type) %>", +"disk_filename":"<%= h(@disk_filename) %>"} \ No newline at end of file diff --git a/app/views/dmsf_detail/upload_files.html.erb b/app/views/dmsf_detail/upload_files.html.erb new file mode 100644 index 00000000..242b86c2 --- /dev/null +++ b/app/views/dmsf_detail/upload_files.html.erb @@ -0,0 +1,41 @@ +<% html_title("DMSF") %> + +
+
+ +<% path = @folder.nil? ? [] : @folder.dmsf_path %> +

+ <%= render(:partial => '/dmsf/path', :locals => {:path => path}) %> +

+ +
+<%= textilizable(@folder.description) unless @folder.nil? %> +
+ +

Uploaded Files

+<% +i = 1 +form_tag({:action => "commit_files", :id => @project, :folder_id => @folder}, + :method=>:post, :class => "tabular") do +%> + <% @uploads.each do |upload| %> + <% if upload.locked %> + <%= render(:partial => 'upload_file_locked', :locals => {:upload => upload, :i => i}) %> + <% else %> + <%= render(:partial => 'upload_file', :locals => {:upload => upload, :i => i}) %> + <% end %> + <% i += 1 %> + <% end %> + <%= submit_tag("Commit") %> +<% end %> + +<% content_for :header_tags do %> + <%= stylesheet_link_tag "dmsf", :plugin => "redmine_dmsf" %> + <%= javascript_include_tag "https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js" %> + +<% end %> \ No newline at end of file diff --git a/app/views/dmsf_mailer/files_deleted.text.html.rhtml b/app/views/dmsf_mailer/files_deleted.text.html.rhtml new file mode 100644 index 00000000..db55afd1 --- /dev/null +++ b/app/views/dmsf_mailer/files_deleted.text.html.rhtml @@ -0,0 +1,16 @@ + + + + + + + User <%= @user %> deleted following files in project <%= @project.name %>: + <% @files.each do |file| %> +

+ <%= h(file.dmsf_path_str) %> + (<%= number_to_human_size(file.last_revision.size) %>), + version: <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %> +

+ <% end %> + + \ No newline at end of file diff --git a/app/views/dmsf_mailer/files_deleted.text.plain.rhtml b/app/views/dmsf_mailer/files_deleted.text.plain.rhtml new file mode 100644 index 00000000..764b3a3e --- /dev/null +++ b/app/views/dmsf_mailer/files_deleted.text.plain.rhtml @@ -0,0 +1,5 @@ +User <%= @user %> deleted following files in project <%= @project.name %>: +<% @files.each do |file| %> + <%= file.dmsf_path_str %> (<%= number_to_human_size(file.last_revision.size) %>), version: <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %> + +<% end %> \ No newline at end of file diff --git a/app/views/dmsf_mailer/files_updated.text.html.rhtml b/app/views/dmsf_mailer/files_updated.text.html.rhtml new file mode 100644 index 00000000..fc651d36 --- /dev/null +++ b/app/views/dmsf_mailer/files_updated.text.html.rhtml @@ -0,0 +1,21 @@ + + + + + + + User <%= @user %> actualized following files in project <%= @project.name %>: + <% @files.each do |file| %> +

+ <%= link_to(h(file.dmsf_path_str), + {:only_path => false, :controller => "dmsf", :action => "download_file", :id => file.project, + :file_id => file}) %> + (<%= number_to_human_size(file.last_revision.size) %>), + version: <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %>, + <%= link_to("Details", + {:only_path => false, :controller => "dmsf_detail", :action => "file_detail", :id => file.project, + :file_id => file }) %> +

+ <% end %> + + \ No newline at end of file diff --git a/app/views/dmsf_mailer/files_updated.text.plain.rhtml b/app/views/dmsf_mailer/files_updated.text.plain.rhtml new file mode 100644 index 00000000..a1f3ed64 --- /dev/null +++ b/app/views/dmsf_mailer/files_updated.text.plain.rhtml @@ -0,0 +1,7 @@ +User <%= @user %> actualized following files in project <%= @project.name %>: +<% @files.each do |file| %> + <%= file.dmsf_path_str %> (<%= number_to_human_size(file.last_revision.size) %>), version: <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %> + <%= url_for({:only_path => false, :controller => "dmsf_detail", :action => "file_detail", :id => file.project, + :file_id => file }) %> + +<% end %> \ No newline at end of file diff --git a/app/views/settings/_dmsf_settings.erb b/app/views/settings/_dmsf_settings.erb new file mode 100644 index 00000000..5d614afb --- /dev/null +++ b/app/views/settings/_dmsf_settings.erb @@ -0,0 +1,42 @@ +

+ <%=content_tag(:label, "Maximum files download:") %> + <%=text_field_tag "settings[dmsf_max_file_download]", @settings["dmsf_max_file_download"], :size=>10 %>
+ (<%=l(:label_default)%>: 0) +
+ Limits maximum number of files downloaded in zip or sent via email. "0" means unlimited. +

+ +

+ <%=content_tag(:label, "File storage directory:") %> + <%=text_field_tag "settings[dmsf_storage_directory]", @settings["dmsf_storage_directory"], :size=>50 %>
+ (<%=l(:label_default)%>: <%="#{RAILS_ROOT}/files/dmsf"%>) +

+ +

+ <%=content_tag(:label, "Index database:") %> + <%=text_field_tag 'settings[dmsf_index_database]', @settings['dmsf_index_database'], :size=>50 %>
+ (<%=l(:label_default)%>: <%="#{RAILS_ROOT}/files/dmsf_index"%>) +

+ +

+ <%=content_tag(:label, "Stemming Language:") %> + <%=text_field_tag 'settings[dmsf_stemming_lang]', @settings['dmsf_stemming_lang'] %>
+ (<%=l(:label_default)%>: english )
+
+ Possible values: danish dutch english finnish french german german2 hungarian italian kraaij_pohlmann lovins norwegian porter portuguese romanian russian spanish swedish turkish (pass 'none' to disable stemming) +

+ +

+ <%=content_tag(:label, "Stem strategy:")%> + <%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_NONE', @settings['dmsf_stemming_strategy'] == 'STEM_NONE', :checked=>true %> Stem none (default)
+ <%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_SOME', @settings['dmsf_stemming_strategy'] == 'STEM_SOME' %> Stem some
+ <%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_ALL', @settings['dmsf_stemming_strategy'] == 'STEM_ALL' %> Stem all
+
+ This controls how the query parser will apply the stemming algorithm. The default value is STEM_NONE. The possible values are: +
+ * STEM_NONE: Don't perform any stemming.
+ * STEM_SOME: Search for stemmed forms of terms except for those which start with a capital letter, or are followed by certain characters (currently: (/@\<\>=*[{\" ), or are used with operators which need positional information. Stemmed terms are prefixed with 'Z'.
+ * STEM_ALL: Search for stemmed forms of all words (note: no 'Z' prefix is added).
+
+ Note that the stemming algorithm is only applied to words in probabilistic fields - boolean filter terms are never stemmed.
+

diff --git a/assets/images/approve.png b/assets/images/approve.png new file mode 100644 index 0000000000000000000000000000000000000000..fc25958ac6f97b4bf2cf483d593cddb8e83be9fe GIT binary patch literal 535 zcmV+y0_gpTP)}Iq3IoUi+*)0klzL{a?_kGKB0bT!tv)HK^)#MmPVg&ue zFqQai%CiAVudboJ7(|=)Q;78=qJ4;n4_CnY76rTN!nWjYb$pZO{KO%5p!|MVUK-e@ zXiyVaW?r(=L^#-1gAJ5ZXtgH>6?$UeeQ!syEn(SO{dvUH2e;!Vn_OOzG{Fbi|2 zp>B6TdmE5}1$1SsDF>xIB;^}YzUau>{Ao<_hV1>V?tplDo?3Zc$|D`}bxNs0Df=B+ z`*KnSUXr=H!5zrQ&Y&eii1<0gyR(SdesVL%n(`?}E`ILCT$w=`TF;5l#yWojYUC*5 z^;K%ckCFt{T$J4NUbL@u`M8viQ2cZdGe7OfSK#{ literal 0 HcmV?d00001 diff --git a/assets/images/approved.png b/assets/images/approved.png new file mode 100644 index 0000000000000000000000000000000000000000..da85f26df2d93b6407d52b869fb13c52b3797136 GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s3?%0jwTl3$gaDrq*Z&L*_nzN7ckf)(lpv=T zr@hzqK7ao_YEDqpQXtoI@3pPPTeFkbMpYjx&fc2ba;`dgX_Qxw)0}HliuWd0@6GPn z+cM|al$K+~-~WGKdTq{>b3IGX%~^VGY4O(NwdaaZ48Qo<`@IKIWA*;*1{Rj3DHVA#j+%xz6HSiMZf> z1K)W7>!(%u=C<+CmFTBS>#thw!hOfq=kfOWxXt2MhPQK?!|SVE_~W$ZpEBvIJNW6S zroPyXt-zlt`1}3c=keaAvwz3lP$=kWOE z@Au^H_xbz%;OX*kn8NF=Uhu<%{PWZN^U?b4vR;e2^7s1e^ZM!W_~PvL=kNFB?DTAv z!0xtj^wFO1%6Ra{a`47(^7i`g_4@Ag`t9@j>+|{I>ho`x!SK0x{Px@U=bZTEmG|S1 ze4xj@(d6;=`tkMp@b&uX@b;|3+>NZx^u>(*_ucsDqWI^X%-idGpvPZ~yI_pFT8Ow| zjk{=)zNWv~@o z^}>bpzkc+(cJa1u@Ums_tzCoUq^JM@00DGTPE!Ct=GbNc004POL_t&-83nmu$=hrWg!qSQ&m+}RaI40Rr&zHdyT$T@!O^v-3f_mbYSj4 zr8mqSe;bS@EOQTx7Oa!S?P(ZIu(;Xpj3#hA^LawA0D;i~Twc)6S&sm?ma1yttE#HX b_E1&-{~a7)@`du200000NkvXXu0mjf&HJJF literal 0 HcmV?d00001 diff --git a/assets/images/delete.png b/assets/images/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..ec01ecd32b987b0fa0ff4ca286abb0718e62fd5d GIT binary patch literal 459 zcmV;+0W|)JP)&b__8wWXz{LPEWBbMM~X_xJbCV`Ihu0L}may$}$!LPFl& z-o0aEwGa@!006Z)IlU<<)&Ky#N=n9abNA=x#yL6O007Pi2*wZ)=dG>h007QwYu*S5 zy+T6&_xHw9Qtto&&O$=(IXU+T2=B(m&Pq!6T3YvGWA{o*&Qem=b93)&YxjG5_k@K1 zl$73cbN`&2)?;J;rKSI^t>=u4-khB8&d&d}wceDJ)=Em}dwbSvYtC9))>>NUwY7|{ z2qypl00DGTPE!Ct=GbNc005^+L_t&-83n;RP6A;72Eh5)g~SN4HCkXSXgGwGH}?Sc zCd7tDI|C^DKh_xl0C;>2*azYz$@FG_--pA2!OgN&+ie@?Rxgu>oPyW)HlAr(p002ovPDHLkV1f~d B+s6O^ literal 0 HcmV?d00001 diff --git a/assets/images/dmsf.png b/assets/images/dmsf.png new file mode 100644 index 0000000000000000000000000000000000000000..fc221f94236b857079d89d7a332fd660c33cc5c7 GIT binary patch literal 3588 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 KMNUMnLSTa6kg)pz literal 0 HcmV?d00001 diff --git a/assets/images/filedetails.png b/assets/images/filedetails.png new file mode 100644 index 0000000000000000000000000000000000000000..0ef642fa157023b1b4059c5936ae9b772eb807fe GIT binary patch literal 803 zcmV+;1Kj+HP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02y>eSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+*LgRNQs00NUqL_t(IPi@j$NYhaq2k^i4RIj~+^k6R$ z1eOiDJXA^+CS`PiBuz9dZ&2EWG;5lbQ!{WjHkIX-P^ndBVQ#|`G1Qz>=d8KumabiF zo4ftDzjNES6~TqQi($Dn5l;5D@3 z@2Bg~b!29bHzmf6^Y`tfTm#PuoCITRB&e1kEl?}V%5%kiBF!T$d`IrY8 zFNR0iB}D{CL944ktIFV>V#EEjfS~&!v^bFl+h{SYKN`UN{tkM&2lBe)MatTh3zv)s z&rA!!^>E7Bun*l>y(t||4Tn^~hjpYBrrvUx`*_~T=4>Hl5v9T=z6rPTC45#f!8NcA zTp>3RG{XU75)haV$K)+&+mmQT{bl|L?@|P1d6x#q;1ihN#KAdA-fXG@ddWrDRnJxt zIaHkmv-m1>4X5}@ZtO1Ei?}e#@-`J-od_0bE{vi?=(;nYA7sO8tOYZl7eXdU8g$ir zG0TaJRaG4f5B-6K*XLpRa1DQ2&p{zL0cF=cX!?s`pU#4d#Mqq-EkRoLR_KbtH{Nlf z^(@Qu>LhEZ`dCVTa|SH(c8m#5V_0w)GGV-1&W+)!%Oa0!i?*<|h19n7FSj-*&xtzF z_VkRO84_T=BL~ug{l3n;-L+rY(UBA7hyIthSxbu99-WyG!8G1Eviz+o!64xrE$PhP hv!kalDr~d;{{WEr1Xq=u)D!>!002ovPDHLkV1i5gXU+fs literal 0 HcmV?d00001 diff --git a/assets/images/lock.png b/assets/images/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..653ce5552f8f9f3ef7ad5572bf8b67b7cbeb0e93 GIT binary patch literal 797 zcmV+&1LFLNP)%haOS?_eE1)8q@whMjc zlz#%U!o+J6x5nI0%NGL7YtVWw#s;`(P}0aRW(K=5Zv8{T`%j z3yJmc;J`oVE27V9(VTP5=BL$c!9m)HJJ$yj8wYU50U%%LdN{K*w<0ji2b%VJ#H6P| zda4yCGLGB)g2qBfZ>m-GYUDY@xI2VQPm-lyjo%cH^=FQN7P;BZtrJ!hmNd35x_iQfO)^YQ&Kszy@fFGO<3cW03s`l zx>fhMCHYBn8#?c{vtuJ;Iknwv-S4w;trD_2xxub$h0v84X1`A4u9~CYqsSAD5B8() zc?Y9We|2jb#CHckk1TL5 literal 0 HcmV?d00001 diff --git a/assets/images/locked.png b/assets/images/locked.png new file mode 100644 index 0000000000000000000000000000000000000000..d7341e717cbfb553fdec7af478c28fd38f932e9c GIT binary patch literal 530 zcmV+t0`2{YP)JP00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02y>eSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+*LgRNQs00DwYL_t(2Q-zUVC_`Zw#*fZiaU;sySuR|; z=T2sqyHE-@QY70>OqGIOh|N znSAx@{l4#gp7;I!P?T(hLLpeK)-R;BJpW@}EdDh9X(|@T3zY2gX^PwTElq|b~@c#g0(XGFx(40l&GoN5J_-g@|_8ooR5DG!of6A?e2S-`G!BWP-Yu(NEY_A0Jqu?h@zJX3TY8`h=yH5M~H!oGvGz? zT#&$SjKVwEh{U7{DRViZ17+CPDqz#lx22tKP_T|Z;FVBvAeSY-Y z%VXbOo%sIx^!ImXzrQ>G>)n|j?=JlQbp6klOMkvx`Sa<{pHKJxetQVCcNC0M1MG8<*vcxr_Bsf2Mk}?Z3=)K5>rt``cnnv6Tyq17F_g zcUn1P#fqEXV^*HH_2KYd8K!F`8VHiJD$~KOB^h NJYD@<);T3K0RT<~!La}U literal 0 HcmV?d00001 diff --git a/assets/images/notify.png b/assets/images/notify.png new file mode 100644 index 0000000000000000000000000000000000000000..24067ba7d2ec4781f6530c0ee3aed71e45f8b01b GIT binary patch literal 569 zcmV-90>=G`P)bsA9Bh{CB2`e3~sQyRA0opoDjDY?N+Al)qRRcIKz;Ka(2G1S{piA*( zINxso@(ccZd0;UJ!4R0uxrH_>?A?)@(=!lJWsrrT5KTZv7Mo14 zo)%2rf7a}TEKba+Fo&(n)H-!e-74%I@Mg(XQU@#A?L?D*T6>IYK5o$@f+o*Hwq1Dy$r~RjMqkQ@Hk=Z`K01X24 z7v98}bkc1ChR*a8kYNNj&ye$y892a*c!BT^GO}tL%R2>0A|7CDy`UZ09DWua{xO@CvKppW02iN`0+1;rE}$C)aQlkRhB!{=VC@!h?(*TyN3mK@!9Xwe*8QmDDEa;# zcDx)HKrQKb3|;g782fVuUW`M{V?XZ5UbIWMrA3A2@nAiQV_%EWGCVb}nrfKqKZ{>2 zaB#C9q2i8Pq6@t}PI>Ka>_UP3=>}XANm`$0O%+(e-NYH1LjIotnRy6|Xsm2JfctzO z*6Tskx(H@+2&-cs)r01!<13wWOtb(38Y(&dpm}kXch?cD$$O}fobH<>w;sOb#E5mY zlncDME~xc&915=5R2?9dGU&Pg-n>3+T$X#$S?V9KR3vjGESeJ~L}SjvS1Y-%w?vlV QvH$=807*qoM6N<$f*t$|9RL6T literal 0 HcmV?d00001 diff --git a/assets/images/operations.png b/assets/images/operations.png new file mode 100644 index 0000000000000000000000000000000000000000..8bf76fbbe5af5530a73743c61cc3d2ac5ac8ee17 GIT binary patch literal 459 zcmV;+0W|)JP)APtL+ zxmrl$Wc`zf3n*g`v;0rbIxj*dHiZTr(m6?gBTa=mqF6QotNZ~m#F3_akIwCAOSP@W zH9<<)RMz+vlE{$UIr?75NZmBOzJ*kxEUe+X}JB?9)jT!y^oDKLQExwUe@XGFqhsIZaLP7 zbI?9%3)Yi}p{w3$>$ba`|E~FKKCee=m)!r({S66-T?q1-?uGyW002ovPDHLkV1gB6 B%HRM1 literal 0 HcmV?d00001 diff --git a/assets/images/plupload/backgrounds.gif b/assets/images/plupload/backgrounds.gif new file mode 100644 index 0000000000000000000000000000000000000000..39e33ebc02114ebea6bb33dee2fb76af3a6dd4dc GIT binary patch literal 2977 zcmdUu`#Tc~1II^hosi0dq?7P6w^9i&)fvJz8*&{RhTJ9Zdro+CGnWyPC?vVFx!;*f zwGhKxa!+DIj18M@Hka9Z&Y$sqpU=;q=kv?g24ZdUZyyK10});iz~_F@@$vEA-kwk> zWUv>uxr;|fM|?hCAQ0^D?{m4_!^1<~@jCbD7n4UhI5^ni{N(X?EIxH-Z^_Wu)X2ol z*z~rE*&WkcAhSE>w?TJr{b6~>-0BYtYxBEc3oEF(6+}nx3g>Wj_h4mDuy)tRmM^63 z9j{-xc7uPs!8!WHK3u(V(`0vlnSHRr6|5Otzp1NlaLv$I|LS!sFwD~0mM5fn`S^SL z224&(`}hT-eFHpw?t7yBP+q?7C~u^v4+81wfkL~vqg)Y47dMZI$th=7gf-M|Zti<} zMppFWX9%SCi|=rDf$WQq#?D zgPh%vgpRJ6*|~V^i^V0%>aR8G@(PVc1H&99{uWKoK;ZYjO-xQqOxioSZEtUXpa1b= z;U}F=FDWfYdikw;y?A6o;>FZf!&R1ropX!0hj#(0-1t9=3LlPOcu# zuI^4Q2(YcauYa)FZF6^|w}X?b4HOP@aGsu-wFX1)2Zq`?I6Jwx-?f1`IJw+&MmV}4 z?Cv?;G%>Zu$?>SjoLvDjCEv�TRS%oFI%_+CO)aErq(YYgtoB}8U3WF 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 literal 0 HcmV?d00001 diff --git a/assets/images/plupload/buttons-disabled.png b/assets/images/plupload/buttons-disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..afa11af9b93bcbd261faa6a5b0835e82493712b2 GIT binary patch literal 1292 zcmdUu{ZrBh9LK*cyLmOMbKNd&>ujyJA?aEC~~zn}NBXR)7W|sN7p`Q5FZPn}b6cI@%6mmG6E;YBgxp`z{B(g5F$wZ=3sZ1s_ zSC&-JjgriDGG9<|+N|qMu@kQi3=5^!SOKSoDr|hcS(nIx$>w?Y_}6KwQisDq>&KG^ zi_0{bH8nN$L-@}5#{z*sBoaNE0dajON?%cxq_BD%E0@ccmzSCPYH$)y9xazhBp?Wy zUW~~c0*SrbXfep`0_;EzT}hj=j$8hk%pbhjWCDo>+~W2Ey6qa7OfJ*smgutVcDun~ zSlF7q(-W^)6e^WU)-+{gRXu4PhfzX}D_Sm>+tJZcA<4$~W(gggQWLMqNW>4`&XFa_ z$3!!>siGdV#WsysWemKMKU9@bO;z~byHZ;hcj8h1f)Le`$WYSm3XtfYt5+Vvb7jeO zgQY!|W}>X9N|sm9i>xpe2_0QhQ#Uw-4W|TER#r;we7>#?N(rLTXx4SJ)F8wtQZNc+ zSl!QEQw+)IQy!0}(P+9R8TIw`I2>+hXozZlz?dNiR|l%q>NIvj3=5*P3tQ*t1OkC* zC>b^D!|N|d=Q_pnJe5jCr_)(1R(WqW-@+bTl54fv#l^+eIYyo)oj2S@PH#7u!_Wj;T^_|aLmv2xXf1{tLw~Rol zMoUA@How0E1c$wV+ArX@;j{i*jAO|Id0fXo7lQ`5PGpH1zO4M(nl*Mp{}LWd`hK|i zC)z~qJL}3hPjh=E7oAdn6;@F-8hmB7-wWMnK5+UfQVc|-V<4srKAn&t+EJ{=mmYl2 zKcaDu*Oh7emN4xwv%H%>guu-^L(CJO)8q82H6!eRXGegq`lySYbVKCRBLA{94hZml z7C3$h9*J#@{i7xV;SvW5&ZiMh)pYp3e*Pw$un))vg(brQiLq|T00b-=`ZO%H@?V~M B8n^%e literal 0 HcmV?d00001 diff --git a/assets/images/plupload/buttons.png b/assets/images/plupload/buttons.png new file mode 100644 index 0000000000000000000000000000000000000000..153e73885ac4a1fd1a98dccd5df73b8d72a2df10 GIT binary patch literal 1439 zcmds$`CHNl0LH(md$h8mXU=DKYCYR>x|XZunH7j(vtnANdE^0}Q_%3q3ljwuNd0Px zXc>}1icY2+UgSJVML-S7gET2S?9e#8(;R=t-XGrM_s>g4UOcyB+n#Lz0POHUxOoA< zmMDEUGcnYAQk=_}J}hu>Uz|549CtG$0S%l*VM5WC9gwmupEox**VosVmzP&oR@TtHrkX-4q2s9uaXQg^SBNc4sxUG3Q2Gz~8cBP?UB&cROAb9#H0uYYyN|uyA#k zDkn-tl*8#=QP!E;(t2WArns5~b#fPK{N1zYdVd1%T6!M_Y{z4}!cdH>lD9p4X)U=W zrl1>tGItNNkCH7&BuEq83e1VT8#NM;B#fqXC!ZjHPZVGYyja%ryR7~^4C~b7dnI?8 z6<6(0DkYI*$=qqSL?O=Lg(yA_)hX_AdYPrqs8LmpSMS5w!!$Rl6Rf}?q19izq`pc& zlF}PfJy?t^@(@hd)4Q|67$-Y#GC}{yJCun2yayZqMuVix+6>$Pz;Xq?)cP6bX!#;N_ zM&L`A=G>9x^7#OMwe1(E#R%zSn0EYb9t^_rHAf6$xY*4IiB-fK&4Caqqw3c8R0C1aH>9E zo(0W2i4piUJ+E;lo#>bMJ$fxfGktK){>)xV@d%~kl`!*#;6hnh1`EDUrHPrlVu7bnk7vzs?(F%PW9p(Us@ByaDr1@N8id1 zq#EK|qf7fpp;hjA9jU`(GJHqGKZ7*?yVfBUmkUMdF;(`|w#2fh)Y<_s^1<(XG1r9( zp(&{7x-0IW1%HBtZ;hc0;q|=DlB{j?98clAE_&Up(=|^ z)y^4+k!d{)ayxq=5;3zqN@!&=e~dt&XdI2tZQl&^gc+E=1kFpxziBUw(u>+6B8ew{ zHT#4adb9s$)lNf5ZqWEWp|o~9mNBKZF@6n~4eLc`ZtQi) zEeZC+@YX2J-oQrPCqWJpanQl%fhgK|SHZ)!AkX2~Oo-gT(((Wk)zSW;rL3+K1a1L<0hS=shBh1KUx9VI6LTcP+1A$fTOVUSbj_}t^TxwA z>);uvlQWimVfuDfQ*;3A$LePE3n4Lk!TR4vFstfStKdAkc%W__W|RR{rpRfCtdE^o z^PM_}-KWf;<+}`y{((slzS?_8m0wvIoRWUWhih`I+Qw`E`olNIr-M})!19Q^wA?Y< zq@_nD!#8PWLw8%hgbQrU?2C`SIj|0x%a6XB8T34O-bxlYF*54+(^w#u7e+Q(Mw+~5 zI{4nmu`t>**kHfo1zn&9jdV!B)Ds3d11+CukDTL!Q1QOEPeFD(V%IO7$IQC+FIkzm e!^(pI%TA#Gq{YC>!%?W7RKNp%(X9a*ocSNer(gg8 literal 0 HcmV?d00001 diff --git a/assets/images/plupload/delete.gif b/assets/images/plupload/delete.gif new file mode 100644 index 0000000000000000000000000000000000000000..78ca8b3b49e8f739df6ecfa4ef1119058b40e035 GIT binary patch literal 180 zcmZ?wbhEHb6krfw*v!H39|+Ez`G02S|CvCP_CGD{3a`TQPIW08y z+@NmXYwfboIA>eftmEx7PPc8lxOLj;wk@}o)$U1Kdw+KQj?7u7+G7q_7aUKWu%~bR zon_CzK797&@#5>#ChqKCeR=lWbKTo6Y;8YOwD9`W!0mdKC$n$9xnkdKU4N?J%dby0 zyHXy!eqg;&z4BnntSh~RJEP0@B-9|3oj-CkkZZ+PIz&iq4(r6)4xUz)J}=GOefiTPV2bGL_Yxw$lNYefFmh~ndE zIY+&>-d_`Q*w(b(@Zp<>wcFFSKin|$Y?u8^o3fLc-6tyEet(^I)N{&-{-V>Nn&qme z-|XLbclqX#$!XmLDils2^$_i=Vfo6|K!7s z$3S&5b~>3_CysS9Ff(!pa1CI4JIO94&@9fw!C(yl Dux)$+ literal 0 HcmV?d00001 diff --git a/assets/images/plupload/error.gif b/assets/images/plupload/error.gif new file mode 100644 index 0000000000000000000000000000000000000000..4682b63007c89fae09f6640e1a968a073d98b90d GIT binary patch literal 994 zcmZ?wbhEHb6krfw_};*9b)oIQ-*5l@e)sRs`)iBszTS%X_xshqKVQDwin+ST?*G5P zzrQ^G_xt_f9Jc2NYJa}F{`Gdu&zDm^T?_qix&OnZo-emz|NVaR_t)1i*Zuzedh_v0 z>zAA1pKtV@Ea!c=%JBE6!+*Zrczrtj)3uOqx1+w?ivDyh;``H`|9*XZd!q2y`>j8o zPq{fm`Na;auQ!6uw~GD$_w&u+sDHnnzd8{2bbrVDb8TPluY7%O^{INT7l+$F-w1!O zT=&{!g-_QbzC1qg{l($GKi~d(fA!P#uus>+|9rjj_t&$3f4+Tvyzkqcn7=>n{r&m; z>&=i$lU?pF(SEVZ_4lV+Z%tCF$iDo6LW`p7(5<$N&HT8Aidtg@EEu7Dfh!1_m9F6F_-_fnz@dBd3hV zh6RoeTpun37Hm*p=S}!xB2%GsN>(9zl@H4iweC3*!Wlj#haUN`*h|{jWN<7##?Qpk zmawqs0!x>8vXF{_aD)HsCf8Xtia|ir6_cteb~+gFu`;I!>|qpOVG?4uVAFdM{QR&W zONNt2z@nss^K_X~eLR`cSvVIfMeSfTbmmr&58_**5um`{FC={>;XzVD=Tv()J_7-T ogaZvn8JQwxBtJeTEbH?rX5;20cPa5$D~HSgwKHzbjEoG{0NEsHRR910 literal 0 HcmV?d00001 diff --git a/assets/images/plupload/plupload-bw.png b/assets/images/plupload/plupload-bw.png new file mode 100644 index 0000000000000000000000000000000000000000..bb4147e8ef676988e03187f3439b1d1724cdc272 GIT binary patch literal 2105 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 literal 0 HcmV?d00001 diff --git a/assets/images/plupload/plupload.png b/assets/images/plupload/plupload.png new file mode 100644 index 0000000000000000000000000000000000000000..74fa3ad3a5fadd02edd5212426767e2b6e6ad685 GIT binary patch literal 3641 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 LNkvXXu0mjfYMjR1 literal 0 HcmV?d00001 diff --git a/assets/images/plupload/throbber.gif b/assets/images/plupload/throbber.gif new file mode 100644 index 0000000000000000000000000000000000000000..4ae8b16a5a474c3da1e426afc20d2167ebd360f1 GIT binary patch literal 1922 zcma)-eM}Q~9LBG`Uf-_0UZFJTFt*nsTO5ueC8MQCX?YP5d8q^!#7gUiuF(Vv0y0uv zMJsO=7zu_gofyX-W+{m?E;Ac+grVzRB*kTp0b7W13o~jO6PL#Adz^qwjJtm>_sjk9 z`#jI{eZINdbKXqbPa-7ZMiBKT^}xA-;P=6WKNl)0E2eKxhaZKneR-`QzrcOMJvKfT zx)lmP4A1{QA9)h7lvsMZdqb029JH3~(nv`^VO`dQxDnc;-1oz!sEu0QJXhB~$nrssc=;5d6bWb>~J{i+O9hUE>s zy$3K&J8PlS3s zuf&l@@FJcg=@u`pfhDq)R}nk`!N({wk0c0{S65>YWU5;9TUls-qL>gRi_rR);{wgY zMVdw2=B8#K29K9L3s zc2K-aUfk?E&kByZwEo`KQU%&(ulDp;Rd2jF$4C4_mBa4FtT?M{W386p329VQR0mG+mdz;^%6}=b+X&!0PAJghY6b|U<(&T>?OpL^EQ|Dg8v(US+(!5}gNN>68C zjIJsuuSa13hEay{0=HqHA>PcGLdSs$kl{5DmyWOoKy3@Be5XpZZB{~bv-Rt+_KBtg z?I&+!J&28PB^xhXaT!{!c-ZpmN=~tqb8%>d`}1|A?BGOz>Q{U7drQo%qk+Jq)wF|Bjbw4$i)(?^O-2qNn1c<7SK zeh64nT!z?PU-wbRweq{O<*Cxk-%v+|o7P1Kr$(&P;`Dngu`P{NC&`fTITT02B_YRx zMm@NDi25Ks=~!vhsbn#_65^=UAs+Z>fLS&$h+rtQa@x?&qIF}sS{QcM$9 zyj8I_JtbX`?QWmL>a)qPxTV4W0{AhtaEw8J%zHgmpSG<=!@1xzkcC#tDkpClZTQ9T{9{ XMrH}sh*f7CD@Dcca7lI@6tMnZ`e4## literal 0 HcmV?d00001 diff --git a/assets/images/plupload/transp50.png b/assets/images/plupload/transp50.png new file mode 100644 index 0000000000000000000000000000000000000000..eb0efe104bdcc277ddcc3f6efdb54e1d533a5179 GIT binary patch literal 399 zcmeAS@N?(olHy`uVBq!ia0vp^(?FPm4M^HB7Cr(}Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+AuIMDtfv&hE&{ob7`X>g8|2p4HHh@ z{1e$QDM;$zbFWMMeYKnhV#f|eBu!Qva;S;tqB;S44$rjF6*2UngC6Cic*90&0C_ntdXxr(@H6fSwFJoHC4mDS4Q>JPFCQGcAUMuidAvbNAllO5CMDLso8 z=BZ_^OGxb;Gqap1m#1W$?vDHYGEHkaw%-4K-{05w``zbDutuVH@{U;8*!f!t&Er97 zFg!S-o%l9>qC}y8w%j3xHb(3KgKxSqBlzSY$$`KburT&#EiZE(iuRtG;%;5gsz8*R z(LulCK20I<>j3H#^lB!CdYLHA)gwNgjj}==KDNpam-kYQ27&aWj+*ETfm~bBYk)yP zJHbFn9tCm!2O@dw#3+C5(__OpkFY@;Aa-P+UMZ1 zO9*>U59D6Tr<3lFGgbk%Q86jb#K=gF1ubt85P${;Q!|!eu&uB`XpP&6+4RMVNy9)| zfTboP&YSD#F|>38!sLTy3qiphY=H=~BOS;|C>YPI)mW?mBAr{(F zYNa>uyh4#*BuCteF`jnega$;1KJq>0i?`*e*cWVrhAJk;Rm!F6m%;C}N~H@cR^ai9 zk95|<#MQ~eRUVyjp0wOehkwW>R6kvhwpT4PV=A=+S!^1cU`pBn-QR~U0jIVT8_O*= z{gTpLZeu|fw8KhtHmkuR%&)9jMl+#^_TQ(DsnxO~ovw_8JHeR4H|xLCbZ>Kwv>2lU zt>}6`4t;8|vtxfuR_E7h96y?QOLda8)Mz z4aDv>Ri87YXNCWW%44BO~hVIFG6^294`G%_j67x eeUC2uM}7gt?GPX)ww#Cn0000Px#1ZP1_K>z@;j|==^1poj532;bRa{vGmbN~PnbOGLGA9w%&02y>eSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+*LgRNQs00IU{L_t(IPlb~^h#FxK$B!#gU4K;?!7{#!lHvmGK%_g+lZTd8y&nFCUI2_knt#+GWFc`pWHiOk_1;^u&8f&-PfALPBR;%yH z>b@0=ML@`8GNA$loX=;tTrLog$I(QF7hp6RpDUFL%;$5!HSYI&IGs*Zh~2Z4_vaog<{5{U$LK9x!V!!W-U3dN&R zseCjT46k^_B>9@qujnpzj6@>%M!dMaTrU4AlgYlk42eYIp)ewTAl-i+4hK9AdSz0n zlq;1=AQlAhg95Z#EsN$93I*_ZJRE_2z$9LtSlj?+v-uCpvVi8f-R{5Sy$>Y$ksJyh wi(p_4oq!ngL{`UHESB%2``iD1sxJ!u0VH9SQ)@@7uK)l507*qoM6N<$f{nKpxc~qF literal 0 HcmV?d00001 diff --git a/assets/images/waitingforapproval.png b/assets/images/waitingforapproval.png new file mode 100644 index 0000000000000000000000000000000000000000..9236c0e6cead403110bc580856e2ab2b4f14b2e0 GIT binary patch literal 412 zcmV;N0b~A&P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02y>eSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+*LgRNQs009h1L_t(2Q+<%VP6AO7h4&eF1mi>T0G2k? zhMHJdT3C=66ASH3h#^*5sf7uIzX&V8>jI+usHnufb2xKlaSLFQbCbFAow;|$iKntL z>Xe#M*q|%cwjCZ9XE#;+1_qlVbKlTrH8liA1miD3|6Slu1l~x{8Vc&Kg4&CLOKI#K z-@@5FytYY?pg$IL2shsZPYbxxGbs6ghW+!0f5S83HoIDQAgJ~Y6fP3Zdl5tUCH+o= zUEP7fBjIw_pg=fBIPV4ah2+T{mP6|t(a_g&9b@Oxhz=dDH3(Ol&X$(m(Z>%oXRTn@ zx+++3yTfS+elt(51oe`|gQLr$cKoCgYrzG)MW{~gtOMt%mtj<^-8i`b0000NSs56Z83KGlT!G@XwYBy2^$iUTjg5^>O-;?s%`Gi0t*xyc9UYyWon2jBJv}|W zy}kYY{Szikm^^v%%$YN1&6>4n(V}I`mMveteATK|>(;GXzkdCO4I4IY+_-u3=B-<| zZriqP`}XZSckbM^YuE1GyZ7zew}1csLx&C>K79Ddkt4^CA3t&8#L1H$h*;e*F0H z>({T}zkmPv^XKp1zd+B9g3%B_4uMCv*lz&y2}4PcU-18t4H$yIv#SEbhO@vUvY3HE zs04%=@1@CC00kvWTq8Ealo z5uDn0T&&4JfFb#4pGMgmQSB}+CM6dElNbO0`|^COylDC6wt4Xl)*tL2^g}Au*G~WZ zvf{@330xaKaaFc0c0Ky6HK4(1o~c$?xpVN;$BFufwM4o9FVQlZo-*lT$hLGQn@=9k zr|om!z;1J(`K{9&)jzXeTYV_gW4I$0d)(&--xl4=MxR|cn(~&cKe0_uKvL&GQ&i@Q zyQl0pCa^sgZkW9*DW0x=F6*2UngHG+IqU!c literal 0 HcmV?d00001 diff --git a/assets/javascripts/plupload/gears_init.js b/assets/javascripts/plupload/gears_init.js new file mode 100644 index 00000000..5f44f09b --- /dev/null +++ b/assets/javascripts/plupload/gears_init.js @@ -0,0 +1,86 @@ +// Copyright 2007, Google Inc. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// 3. Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Sets up google.gears.*, which is *the only* supported way to access Gears. +// +// Circumvent this file at your own risk! +// +// In the future, Gears may automatically define google.gears.* without this +// file. Gears may use these objects to transparently fix bugs and compatibility +// issues. Applications that use the code below will continue to work seamlessly +// when that happens. + +(function() { + // We are already defined. Hooray! + if (window.google && google.gears) { + return; + } + + var factory = null; + + // Firefox + if (typeof GearsFactory != 'undefined') { + factory = new GearsFactory(); + } else { + // IE + try { + factory = new ActiveXObject('Gears.Factory'); + // privateSetGlobalObject is only required and supported on WinCE. + if (factory.getBuildInfo().indexOf('ie_mobile') != -1) { + factory.privateSetGlobalObject(this); + } + } catch (e) { + // Safari + if ((typeof navigator.mimeTypes != 'undefined') + && navigator.mimeTypes["application/x-googlegears"]) { + factory = document.createElement("object"); + factory.style.display = "none"; + factory.width = 0; + factory.height = 0; + factory.type = "application/x-googlegears"; + document.documentElement.appendChild(factory); + } + } + } + + // *Do not* define any objects if Gears is not installed. This mimics the + // behavior of Gears defining the objects in the future. + if (!factory) { + return; + } + + // Now set up the objects, being careful not to overwrite anything. + // + // Note: In Internet Explorer for Windows Mobile, you can't add properties to + // the window object. However, global objects are automatically added as + // properties of the window object in all browsers. + if (!window.google) { + google = {}; + } + + if (!google.gears) { + google.gears = {factory: factory}; + } +})(); diff --git a/assets/javascripts/plupload/i18n/cs.js b/assets/javascripts/plupload/i18n/cs.js new file mode 100644 index 00000000..1ee5d5fc --- /dev/null +++ b/assets/javascripts/plupload/i18n/cs.js @@ -0,0 +1,14 @@ +// .po file like language pack +plupload.addI18n({ + 'Select files' : 'Vyberte soubory', + 'Add files to the upload queue and click the start button.' : 'Přidejte soubory do fronty a pak spusťte nahrávání.', + 'Filename' : 'Název souboru', + 'Status' : 'Status', + 'Size' : 'Velikost', + 'Add Files' : 'Přidat soubory', + 'Stop current upload' : 'Zastavit nahrávání', + 'Start uploading queue' : 'Spustit frontu nahrávání', + 'Drag files here.' : 'Sem přetáhněte soubory.', + 'Start Upload': 'Spustit nahrávání', + 'Uploaded %d/%d files': 'Nahráno %d/%d souborů' +}); \ No newline at end of file diff --git a/assets/javascripts/plupload/i18n/da.js b/assets/javascripts/plupload/i18n/da.js new file mode 100644 index 00000000..fc958965 --- /dev/null +++ b/assets/javascripts/plupload/i18n/da.js @@ -0,0 +1,12 @@ +// .po file like language pack +plupload.addI18n({ + 'Select files' : 'Vælg filer', + 'Add files to the upload queue and click the start button.' : 'Tilføj filer til køen, og tryk på start.', + 'Filename' : 'Filnavn', + 'Status' : 'Status', + 'Size' : 'Størrelse', + 'Add files' : 'Tilføj filer', + 'Stop current upload' : 'Stop upload', + 'Start uploading queue' : 'Start upload', + 'Drag files here.' : 'Træk filer her.' +}); \ No newline at end of file diff --git a/assets/javascripts/plupload/i18n/de.js b/assets/javascripts/plupload/i18n/de.js new file mode 100644 index 00000000..96429d31 --- /dev/null +++ b/assets/javascripts/plupload/i18n/de.js @@ -0,0 +1,25 @@ +// German +plupload.addI18n({ + 'Select files' : 'Wählen Sie die Dateien:', + 'Add files to the upload queue and click the start button.' : 'Dateien hinzufügen und danach auf \'Starten des Uploads\' klicken und die Datei hochzuladen.', + 'Filename' : 'Dateiname', + 'Status' : 'Status', + 'Size' : 'Größe', + 'Add files' : 'Hinzufügen von Dateien', + 'Stop current upload' : 'Stop aktuellen Upload', + 'Start uploading queue' : 'Starte Upload', + 'Uploaded %d/%d files': '%d/%d Dateien sind Hochgeladen', + 'N/A' : 'Nicht verfügbar', + 'Drag files here.' : 'Ziehen Sie die Dateien hier hin', + 'File extension error.': 'Dateiendungs Fehler.', + 'File size error.': 'Dateigrößen Fehler.', + 'Init error.': 'Initialisierungs Fehler.', + 'HTTP Error.': 'HTTP Fehler.', + 'Security error.': 'Sicherheits Fehler.', + 'Generic error.': 'Generic Fehler.', + 'IO error.': 'Ein/Ausgabe Fehler.', + 'Stop Upload': 'Stoppen des Uploads.', + 'Add Files': 'Dateien hinzufügen', + 'Start Upload': 'Starten des Uploads.', + '%d files queued': '%d Dateien in der Warteschlange.' +}); \ No newline at end of file diff --git a/assets/javascripts/plupload/i18n/es.js b/assets/javascripts/plupload/i18n/es.js new file mode 100644 index 00000000..2379421f --- /dev/null +++ b/assets/javascripts/plupload/i18n/es.js @@ -0,0 +1,25 @@ +// Spanish +plupload.addI18n({ + 'Select files' : 'Elija archivos:', + 'Add files to the upload queue and click the start button.' : 'Agregue archivos a la cola de subida y haga click en el boton de iniciar.', + 'Filename' : 'Nombre de archivo', + 'Status' : 'Estado', + 'Size' : 'Tamaño', + 'Add files' : 'Agregue archivos', + 'Stop current upload' : 'Detener subida actual', + 'Start uploading queue' : 'Iniciar subida de cola', + 'Uploaded %d/%d files': 'Subidos %d/%d archivos', + 'N/A' : 'No disponible', + 'Drag files here.' : 'Arrastre archivos aquí', + 'File extension error.': 'Error de extensión de archivo.', + 'File size error.': 'Error de tamaño de archivo.', + 'Init error.': 'Error de inicialización.', + 'HTTP Error.': 'Error de HTTP.', + 'Security error.': 'Error de seguridad.', + 'Generic error.': 'Error genérico.', + 'IO error.': 'Error de entrada/salida.', + 'Stop Upload': 'Detener Subida.', + 'Add Files': 'Agregar Archivos', + 'Start Upload': 'Comenzar Subida.', + '%d files queued': '%d archivos en cola.' +}); \ No newline at end of file diff --git a/assets/javascripts/plupload/i18n/fr.js b/assets/javascripts/plupload/i18n/fr.js new file mode 100644 index 00000000..724706a2 --- /dev/null +++ b/assets/javascripts/plupload/i18n/fr.js @@ -0,0 +1,12 @@ +// .po file like language pack +plupload.addI18n({ + 'Select files' : 'Sélectionner les fichiers', + 'Add files to the upload queue and click the start button.' : 'Ajouter des fichiers à la file et appuyer sur le bouton démarrer.', + 'Filename' : 'Nom de fichier', + 'Status' : 'Status', + 'Size' : 'Taille', + 'Add Files' : 'Ajouter fichiers', + 'Stop current upload' : 'Arrêter téléversement', + 'Start Upload' : 'Démarrer téléversement', + 'Drag files here.' : 'Déposer les fichiers ici.' +}); \ No newline at end of file diff --git a/assets/javascripts/plupload/i18n/it.js b/assets/javascripts/plupload/i18n/it.js new file mode 100644 index 00000000..7946578a --- /dev/null +++ b/assets/javascripts/plupload/i18n/it.js @@ -0,0 +1,21 @@ +// .po file like language pack +plupload.addI18n({ + 'Select files' : 'Seleziona i files', + 'Add files to the upload queue and click the start button.' : 'Aggiungi i file alla coda di caricamento e clicca il pulsante di avvio.', + 'Filename' : 'Nome file', + 'Status' : 'Stato', + 'Size' : 'Dimensione', + 'Add files' : 'Aggiungi file', + 'Stop current upload' : 'Interrompi il caricamento', + 'Start uploading queue' : 'Avvia il caricamento', + 'Uploaded %d/%d files': 'Caricati %d/%d file', + 'N/A' : 'N/D', + 'Drag files here.' : 'Trascina i file qui.', + 'File extension error.': 'Errore estensione file.', + 'File size error.': 'Errore dimensione file.', + 'Init error.': 'Errore inizializzazione.', + 'HTTP Error.': 'Errore HTTP.', + 'Security error.': 'Errore sicurezza.', + 'Generic error.': 'Errore generico.', + 'IO error.': 'Errore IO.' +}); \ No newline at end of file diff --git a/assets/javascripts/plupload/i18n/nl.js b/assets/javascripts/plupload/i18n/nl.js new file mode 100644 index 00000000..8372c88b --- /dev/null +++ b/assets/javascripts/plupload/i18n/nl.js @@ -0,0 +1,21 @@ +// Dutch +plupload.addI18n({ + 'Select files' : 'Selecteer bestand(en):', + 'Add files to the upload queue and click the start button.' : 'Voeg bestanden toe aan de wachtrij en druk op \'Start\'.', + 'Filename' : 'Bestandsnaam', + 'Status' : 'Status', + 'Size' : 'Grootte', + 'Add files' : 'Voeg bestanden toe', + 'Stop current upload' : 'Stop upload', + 'Start uploading queue' : 'Start upload', + 'Uploaded %d/%d files': '%d/%d bestanden ge-upload', + 'N/A' : 'Niet beschikbaar', + 'Drag files here.' : 'Sleep bestanden hierheen.', + 'File extension error.': 'Ongeldig bestandstype.', + 'File size error.': 'Bestandsgrootte Error.', + 'Init error.': 'Initialisatie error.', + 'HTTP Error.': 'HTTP Error.', + 'Security error.': 'Beveiliging error.', + 'Generic error.': 'Onbekende error.', + 'IO error.': 'IO error.' +}); \ No newline at end of file diff --git a/assets/javascripts/plupload/i18n/pt-br.js b/assets/javascripts/plupload/i18n/pt-br.js new file mode 100644 index 00000000..bd696d3f --- /dev/null +++ b/assets/javascripts/plupload/i18n/pt-br.js @@ -0,0 +1,35 @@ +// Brazilian Portuguese +plupload.addI18n({ + 'Select files' : 'Escolha os arquivos', + 'Add files to the upload queue and click the start button.' : 'Adicione os arquivos abaixo e clique no botão "Iniciar o envio".', + 'Filename' : 'Nome do arquivo', + 'Status' : 'Status', + 'Size' : 'Tamanho', + 'Add Files' : 'Adicionar arquivo(s)', + 'Stop Upload' : 'Parar o envio', + 'Start Upload' : 'Iniciar o envio', + 'Add files' : 'Adicionar arquivo(s)', + 'Add files.' : 'Adicionar arquivo(s)', + 'Stop upload' : 'Parar o envio', + 'Start upload' : 'Iniciar o envio', + 'Uploaded %d/%d files': 'Enviado(s) %d/%d arquivo(s)', + 'N/A' : 'N/D', + 'Drag files here.' : 'Arraste os arquivos pra cá', + 'File extension error.': 'Tipo de arquivo não permitido.', + 'File size error.': 'Tamanho de arquivo não permitido.', + 'File count error.': 'Erro na contagem dos arquivos', + 'Init error.': 'Erro inicializando.', + 'HTTP Error.': 'Erro HTTP.', + 'Security error.': 'Erro de segurança.', + 'Generic error.': 'Erro genérico.', + 'IO error.': 'Erro de E/S.', + 'File: %s': 'Arquivo: %s', + 'Close': 'Fechar', + '%d files queued': '%d arquivo(s)', + 'Using runtime: ': 'Usando: ', + 'File: %f, size: %s, max file size: %m': 'Arquivo: %f, tamanho: %s, máximo: %m', + 'Upload element accepts only %d file(s) at a time. Extra files were stripped.': 'Só são aceitos %d arquivos por vez. O que passou disso foi descartado.', + 'Upload URL might be wrong or doesn\'t exist': 'URL de envio está errada ou não existe', + 'Error: File to large: ': 'Erro: Arquivo muito grande: ', + 'Error: Invalid file extension: ': 'Erro: Tipo de arquivo não permitido: ' +}); diff --git a/assets/javascripts/plupload/i18n/ru.js b/assets/javascripts/plupload/i18n/ru.js new file mode 100644 index 00000000..a78af8e5 --- /dev/null +++ b/assets/javascripts/plupload/i18n/ru.js @@ -0,0 +1,21 @@ +// Russian +plupload.addI18n({ + 'Select files' : 'Выберите файлы', + 'Add files to the upload queue and click the start button.' : 'Добавьте файлы в очередь и нажмите кнопку "Загрузить файлы".', + 'Filename' : 'Имя файла', + 'Status' : 'Статус', + 'Size' : 'Размер', + 'Add files' : 'Добавить файлы', + 'Stop current upload' : 'Остановить загрузку', + 'Start uploading queue' : 'Загрузить файлы', + 'Uploaded %d/%d files': 'Загружено %d из %d файлов', + 'N/A' : 'N/D', + 'Drag files here.' : 'Перетащите файлы сюда.', + 'File extension error.': 'Неправильное расширение файла.', + 'File size error.': 'Неправильный размер файла.', + 'Init error.': 'Ошибка инициализации.', + 'HTTP Error.': 'Ошибка HTTP.', + 'Security error.': 'Ошибка безопасности.', + 'Generic error.': 'Общая ошибка.', + 'IO error.': 'Ошибка ввода-вывода.' +}); \ No newline at end of file diff --git a/assets/javascripts/plupload/i18n/sv.js b/assets/javascripts/plupload/i18n/sv.js new file mode 100644 index 00000000..11c75245 --- /dev/null +++ b/assets/javascripts/plupload/i18n/sv.js @@ -0,0 +1,12 @@ +// .po file like language pack +plupload.addI18n({ + 'Select files' : 'Välj filer', + 'Add files to the upload queue and click the start button.' : 'Lägg till filer till kön och tryck på start.', + 'Filename' : 'Filnamn', + 'Status' : 'Status', + 'Size' : 'Storlek', + 'Add files' : 'Lägg till filer', + 'Stop current upload' : 'Stoppa uppladdningen', + 'Start uploading queue' : 'Starta uppladdningen', + 'Drag files here.' : 'Dra filer hit' +}); \ No newline at end of file diff --git a/assets/javascripts/plupload/jquery.plupload.queue.min.js b/assets/javascripts/plupload/jquery.plupload.queue.min.js new file mode 100644 index 00000000..e4379fb8 --- /dev/null +++ b/assets/javascripts/plupload/jquery.plupload.queue.min.js @@ -0,0 +1 @@ +(function(c){var d={};function a(e){return plupload.translate(e)||e}function b(f,e){e.contents().each(function(g,h){h=c(h);if(!h.is(".plupload")){h.remove()}});e.prepend('
'+a("Select files")+'
'+a("Add files to the upload queue and click the start button.")+'
'+a("Filename")+'
 
'+a("Status")+'
'+a("Size")+'
 
    ')}c.fn.pluploadQueue=function(e){if(e){this.each(function(){var j,i,k;i=c(this);k=i.attr("id");if(!k){k=plupload.guid();i.attr("id",k)}j=new plupload.Uploader(c.extend({dragdrop:true,container:k},e));d[k]=j;function h(l){var n;if(l.status==plupload.DONE){n="plupload_done"}if(l.status==plupload.FAILED){n="plupload_failed"}if(l.status==plupload.QUEUED){n="plupload_delete"}if(l.status==plupload.UPLOADING){n="plupload_uploading"}var m=c("#"+l.id).attr("class",n).find("a").css("display","block");if(l.hint){m.attr("title",l.hint)}}function f(){c("span.plupload_total_status",i).html(j.total.percent+"%");c("div.plupload_progress_bar",i).css("width",j.total.percent+"%");c("span.plupload_upload_status",i).text(a("Uploaded %d/%d files").replace(/%d\/%d/,j.total.uploaded+"/"+j.files.length))}function g(){var m=c("ul.plupload_filelist",i).html(""),n=0,l;c.each(j.files,function(p,o){l="";if(o.status==plupload.DONE){if(o.target_name){l+=''}l+='';l+='';n++;c("#"+k+"_count").val(n)}m.append('
  • '+o.name+'
    '+o.percent+'%
    '+plupload.formatSize(o.size)+'
     
    '+l+"
  • ");h(o);c("#"+o.id+".plupload_delete a").click(function(q){c("#"+o.id).remove();j.removeFile(o);q.preventDefault()})});c("span.plupload_total_file_size",i).html(plupload.formatSize(j.total.size));if(j.total.queued===0){c("span.plupload_add_text",i).text(a("Add files."))}else{c("span.plupload_add_text",i).text(j.total.queued+" files queued.")}c("a.plupload_start",i).toggleClass("plupload_disabled",j.files.length==(j.total.uploaded+j.total.failed));m[0].scrollTop=m[0].scrollHeight;f();if(!j.files.length&&j.features.dragdrop&&j.settings.dragdrop){c("#"+k+"_filelist").append('
  • '+a("Drag files here.")+"
  • ")}}j.bind("UploadFile",function(l,m){c("#"+m.id).addClass("plupload_current_file")});j.bind("Init",function(l,m){b(k,i);if(!e.unique_names&&e.rename){c("#"+k+"_filelist div.plupload_file_name span",i).live("click",function(s){var q=c(s.target),o,r,n,p="";o=l.getFile(q.parents("li")[0].id);n=o.name;r=/^(.+)(\.[^.]+)$/.exec(n);if(r){n=r[1];p=r[2]}q.hide().after('');q.next().val(n).focus().blur(function(){q.show().next().remove()}).keydown(function(u){var t=c(this);if(u.keyCode==13){u.preventDefault();o.name=t.val()+p;q.text(o.name);t.blur()}})})}c("a.plupload_add",i).attr("id",k+"_browse");l.settings.browse_button=k+"_browse";if(l.features.dragdrop&&l.settings.dragdrop){l.settings.drop_element=k+"_filelist";c("#"+k+"_filelist").append('
  • '+a("Drag files here.")+"
  • ")}c("#"+k+"_container").attr("title","Using runtime: "+m.runtime);c("a.plupload_start",i).click(function(n){if(!c(this).hasClass("plupload_disabled")){j.start()}n.preventDefault()});c("a.plupload_stop",i).click(function(n){n.preventDefault();j.stop()});c("a.plupload_start",i).addClass("plupload_disabled")});j.init();j.bind("Error",function(l,o){var m=o.file,n;if(m){n=o.message;if(o.details){n+=" ("+o.details+")"}if(o.code==plupload.FILE_SIZE_ERROR){alert(a("Error: File to large: ")+m.name)}if(o.code==plupload.FILE_EXTENSION_ERROR){alert(a("Error: Invalid file extension: ")+m.name)}m.hint=n;c("#"+m.id).attr("class","plupload_failed").find("a").css("display","block").attr("title",n)}});j.bind("StateChanged",function(){if(j.state===plupload.STARTED){c("li.plupload_delete a,div.plupload_buttons",i).hide();c("span.plupload_upload_status,div.plupload_progress,a.plupload_stop",i).css("display","block");c("span.plupload_upload_status",i).text("Uploaded "+j.total.uploaded+"/"+j.files.length+" files");if(e.multiple_queues){c("span.plupload_total_status,span.plupload_total_file_size",i).show()}}else{g();c("a.plupload_stop,div.plupload_progress",i).hide();c("a.plupload_delete",i).css("display","block")}});j.bind("QueueChanged",g);j.bind("FileUploaded",function(l,m){h(m)});j.bind("UploadProgress",function(l,m){c("#"+m.id+" div.plupload_file_status",i).html(m.percent+"%");h(m);f();if(e.multiple_queues&&j.total.uploaded+j.total.failed==j.files.length){c(".plupload_buttons,.plupload_upload_status",i).css("display","inline");c(".plupload_start",i).addClass("plupload_disabled");c("span.plupload_total_status,span.plupload_total_file_size",i).hide()}});if(e.setup){e.setup(j)}});return this}else{return d[c(this[0]).attr("id")]}}})(jQuery); \ No newline at end of file diff --git a/assets/javascripts/plupload/jquery.ui.plupload.js b/assets/javascripts/plupload/jquery.ui.plupload.js new file mode 100644 index 00000000..cc039f5a --- /dev/null +++ b/assets/javascripts/plupload/jquery.ui.plupload.js @@ -0,0 +1,737 @@ +/** + * jquery.ui.plupload.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under GPL License. + * + * License: http://www.plupload.com/license + * Contributing: http://www.plupload.com/contributing + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + * + * Optionally: + * jquery.ui.button.js + * jquery.ui.progressbar.js + * jquery.ui.sortable.js + */ + +// JSLint defined globals +/*global window:false, document:false, plupload:false, jQuery:false */ + +(function(window, document, plupload, $, undef) { + +var uploaders = {}; + +function _(str) { + return plupload.translate(str) || str; +} + +function renderUI(obj) { + obj.html( + '
    ' + + '
    ' + + '
    ' + + + '
    ' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
    ' + _('Filename') + '' + _('Status') + '' + _('Size') + ' 
    ' + + + '
    ' + + '
    ' + + '
    ' + + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '' + + '
    ' + ); +} + + +$.widget("ui.plupload", { + + contents_bak: '', + + runtime: null, + + options: { + browse_button_hover: 'ui-state-hover', + browse_button_active: 'ui-state-active', + + // widget specific + dragdrop : true, + multiple_queues: true, // re-use widget by default + + buttons: { + browse: true, + start: true, + stop: true + }, + autostart: false, + sortable: false, + rename: false, + max_file_count: 0 // unlimited + }, + + FILE_COUNT_ERROR: -9001, + + _create: function() { + var self = this, id, uploader; + + id = this.element.attr('id'); + if (!id) { + id = plupload.guid(); + this.element.attr('id', id); + } + this.id = id; + + // backup the elements initial state + this.contents_bak = this.element.html(); + renderUI(this.element); + + // container, just in case + this.container = $('.plupload_container', this.element).attr('id', id + '_container'); + + // list of files, may become sortable + this.filelist = $('.plupload_filelist_content', this.container).attr('id', id + '_filelist'); + + // buttons + this.browse_button = $('.plupload_add', this.container).attr('id', id + '_browse'); + this.start_button = $('.plupload_start', this.container).attr('id', id + '_start'); + this.stop_button = $('.plupload_stop', this.container).attr('id', id + '_stop'); + + if ($.ui.button) { + this.browse_button.button({ + icons: { primary: 'ui-icon-circle-plus' } + }); + + this.start_button.button({ + icons: { primary: 'ui-icon-circle-arrow-e' }, + disabled: true + }); + + this.stop_button.button({ + icons: { primary: 'ui-icon-circle-close' } + }); + } + + // all buttons are optional, so they can be disabled and hidden + if (!this.options.buttons.browse) { + this.browse_button.button('disable').hide(); + $('#' + id + self.runtime + '_container').hide(); + } + + if (!this.options.buttons.start) { + this.start_button.button('disable').hide(); + } + + if (!this.options.buttons.stop) { + this.stop_button.button('disable').hide(); + } + + // progressbar + this.progressbar = $('.plupload_progress_container', this.container); + + if ($.ui.progressbar) { + this.progressbar.progressbar(); + } + + // counter + this.counter = $('.plupload_count', this.element) + .attr({ + id: id + '_count', + name: id + '_count' + }); + + // initialize uploader instance + uploader = this.uploader = uploaders[id] = new plupload.Uploader($.extend({ + container: id , + browse_button: id + '_browse' + }, this.options)); + + + uploader.bind('Init', function(up, res) { + if (!self.options.unique_names && self.options.rename) { + self._enableRenaming(); + } + + if (uploader.features.dragdrop && self.options.dragdrop) { + self._enableDragAndDrop(); + } + + self.container.attr('title', _('Using runtime: ') + (self.runtime = res.runtime)); + + self.start_button.click(function(e) { + if (!$(this).button('option', 'disabled')) { + self.start(); + } + e.preventDefault(); + }); + + self.stop_button.click(function(e) { + uploader.stop(); + e.preventDefault(); + }); + }); + + + // check if file count doesn't exceed the limit + if (self.options.max_file_count) { + uploader.bind('FilesAdded', function(up, files) { + var length = files.length, removed = []; + + if (length > self.options.max_file_count) { + removed = files.splice(self.options.max_file_count); + + up.trigger('Error', { + code : self.FILE_COUNT_ERROR, + message : _('File count error.'), + file : removed + }); + } + }); + } + + // uploader internal events must run first + uploader.init(); + + uploader.bind('FilesAdded', function(up, files) { + self._trigger('selected', null, { up: up, files: files } ); + + if (self.options.autostart) { + self.start(); + } + }); + + uploader.bind('FilesRemoved', function(up, files) { + self._trigger('removed', null, { up: up, files: files } ); + }); + + uploader.bind('QueueChanged', function() { + self._updateFileList(); + }); + + uploader.bind('StateChanged', function() { + self._handleState(); + }); + + uploader.bind('UploadFile', function(up, file) { + self._handleFileStatus(file); + }); + + uploader.bind('FileUploaded', function(up, file) { + self._handleFileStatus(file); + + self._trigger('uploaded', null, { up: up, file: file } ); + }); + + uploader.bind('UploadProgress', function(up, file) { + // Set file specific progress + $('#' + file.id + ' .plupload_file_status', self.element).html(file.percent + '%'); + + self._handleFileStatus(file); + self._updateTotalProgress(); + + self._trigger('progress', null, { up: up, file: file } ); + }); + + uploader.bind('UploadComplete', function(up, files) { + self._trigger('complete', null, { up: up, files: files } ); + }); + + uploader.bind('Error', function(up, err) { + var file = err.file, message, details; + + if (file) { + message = '' + err.message + ''; + details = err.details; + + if (details) { + message += "
    " + err.details + ""; + } else { + + switch (err.code) { + case plupload.FILE_EXTENSION_ERROR: + details = _("File: %s").replace('%s', file.name); + break; + + case plupload.FILE_SIZE_ERROR: + details = _("File: %f, size: %s, max file size: %m").replace(/%([fsm])/g, function($0, $1) { + switch ($1) { + case 'f': return file.name; + case 's': return file.size; + case 'm': return plupload.parseSize(self.options.max_file_size); + } + }); + break; + + case self.FILE_COUNT_ERROR: + details = _("Upload element accepts only %d file(s) at a time. Extra files were stripped.") + .replace('%d', self.options.max_file_count); + break; + + case plupload.IMAGE_FORMAT_ERROR : + details = plupload.translate('Image format either wrong or not supported.'); + break; + + case plupload.IMAGE_MEMORY_ERROR : + details = plupload.translate('Runtime ran out of available memory.'); + break; + + case plupload.IMAGE_DIMENSIONS_ERROR : + details = plupload.translate('Resoultion out of boundaries! %s runtime supports images only up to %wx%hpx.').replace(/%([swh])/g, function($0, $1) { + switch ($1) { + case 's': return up.runtime; + case 'w': return up.features.maxWidth; + case 'h': return up.features.maxHeight; + } + }); + break; + + case plupload.HTTP_ERROR: + details = _("Upload URL might be wrong or doesn't exist"); + break; + } + message += "
    " + details + ""; + } + + self._notify('error', message); + } + }); + }, + + _setOption: function(key, value) { + + if (key == 'buttons' && typeof(value) == 'object') { + value = $.extend(this.options.buttons, value); + + if (!value.browse) { + this.browse_button.button('disable').hide(); + $('#' + this.id + self.runtime + '_container').hide(); + } else { + this.browse_button.button('enable').show(); + $('#' + this.id + self.runtime + '_container').show(); + } + + if (!value.start) { + this.start_button.button('disable').hide(); + } else { + this.start_button.button('enable').show(); + } + + if (!value.stop) { + this.stop_button.button('disable').hide(); + } else { + this.start_button.button('enable').show(); + } + } + + this.uploader.settings[key] = value; + }, + + + start: function() { + this.uploader.start(); + this._trigger('start', null); + }, + + stop: function() { + this.uploader.stop(); + this._trigger('stop', null); + }, + + getFile: function(id) { + var file; + + if (typeof id === 'number') { + file = this.uploader.files[id]; + } else { + file = this.uploader.getFile(id); + } + return file; + }, + + removeFile: function(id) { + var file = this.getFile(id); + if (file) { + this.uploader.removeFile(file); + } + }, + + clearQueue: function() { + this.uploader.splice(); + }, + + getUploader: function() { + return this.uploader; + }, + + refresh: function() { + this.uploader.refresh(); + }, + + + _handleState: function() { + var self = this, uploader = this.uploader; + + if (uploader.state === plupload.STARTED) { + + $(self.start_button).button('disable'); + + $([]) + .add(self.stop_button) + .add('.plupload_started') + .removeClass('plupload_hidden'); + + $('.plupload_upload_status', self.element).text( + _('Uploaded %d/%d files').replace('%d/%d', uploader.total.uploaded+'/'+uploader.files.length) + ); + + $('.plupload_header_content', self.element).addClass('plupload_header_content_bw'); + + } else { + + $([]) + .add(self.stop_button) + .add('.plupload_started') + .addClass('plupload_hidden'); + + if (self.options.multiple_queues) { + $(self.start_button).button('enable'); + + $('.plupload_header_content', self.element).removeClass('plupload_header_content_bw'); + } + + self._updateFileList(); + } + }, + + + _handleFileStatus: function(file) { + var actionClass, iconClass; + + switch (file.status) { + case plupload.DONE: + actionClass = 'plupload_done'; + iconClass = 'ui-icon ui-icon-circle-check'; + break; + + case plupload.FAILED: + actionClass = 'ui-state-error plupload_failed'; + iconClass = 'ui-icon ui-icon-alert'; + break; + + case plupload.QUEUED: + actionClass = 'plupload_delete'; + iconClass = 'ui-icon ui-icon-circle-minus'; + break; + + case plupload.UPLOADING: + actionClass = 'ui-state-highlight plupload_uploading'; + iconClass = 'ui-icon ui-icon-circle-arrow-w'; + + // scroll uploading file into the view if its bottom boundary is out of it + var scroller = $('.plupload_scroll', this.container), + scrollTop = scroller.scrollTop(), + scrollerHeight = scroller.height(), + rowOffset = $('#' + file.id).position().top + $('#' + file.id).height(); + + if (scrollerHeight < rowOffset) { + scroller.scrollTop(scrollTop + rowOffset - scrollerHeight); + } + break; + } + actionClass += ' ui-state-default plupload_file'; + + $('#' + file.id) + .attr('class', actionClass) + .find('.ui-icon') + .attr('class', iconClass); + }, + + + _updateTotalProgress: function() { + var uploader = this.uploader; + + this.progressbar.progressbar('value', uploader.total.percent); + + $('.plupload_total_status', this.element).html(uploader.total.percent + '%'); + + $('.plupload_upload_status', this.element).text( + _('Uploaded %d/%d files').replace('%d/%d', uploader.total.uploaded+'/'+uploader.files.length) + ); + + // All files are uploaded + if (uploader.total.uploaded === uploader.files.length) { + uploader.stop(); + } + }, + + + _updateFileList: function() { + var self = this, uploader = this.uploader, filelist = this.filelist, + count = 0, + id, prefix = this.id + '_', + fields; + + // destroy sortable if enabled + if ($.ui.sortable && this.options.sortable) { + $('tbody', filelist).sortable('destroy'); + } + + filelist.empty(); + + $.each(uploader.files, function(i, file) { + fields = ''; + id = prefix + count; + + if (file.status === plupload.DONE) { + if (file.target_name) { + fields += ''; + } + fields += ''; + fields += ''; + + count++; + self.counter.val(count); + } + + filelist.append( + '' + + '' + file.name + '' + + '' + file.percent + '%' + + '' + plupload.formatSize(file.size) + '' + + '
    ' + fields + '' + + '' + ); + + self._handleFileStatus(file); + + $('#' + file.id + '.plupload_delete .ui-icon, #' + file.id + '.plupload_done .ui-icon') + .click(function(e) { + $('#' + file.id).remove(); + uploader.removeFile(file); + + e.preventDefault(); + }); + + self._trigger('updatelist', null, filelist); + }); + + + $('.plupload_total_file_size', self.element).html(plupload.formatSize(uploader.total.size)); + + if (uploader.total.queued === 0) { + $('.ui-button-text', self.browse_button).text(_('Add Files')); + } else { + $('.ui-button-text', self.browse_button).text(_('%d files queued').replace('%d', uploader.total.queued)); + } + + + if (uploader.files.length === (uploader.total.uploaded + uploader.total.failed)) { + self.start_button.button('disable'); + } else { + self.start_button.button('enable'); + } + + + // Scroll to end of file list + filelist[0].scrollTop = filelist[0].scrollHeight; + + self._updateTotalProgress(); + + if (!uploader.files.length && uploader.features.dragdrop && uploader.settings.dragdrop) { + // Re-add drag message if there are no files + $('#' + id + '_filelist').append('' + _("Drag files here.") + ''); + } else { + // Otherwise re-initialize sortable + if (self.options.sortable && $.ui.sortable) { + self._enableSortingList(); + } + } + }, + + + _enableRenaming: function() { + var self = this; + + $('.plupload_file_name span', this.filelist).live('click', function(e) { + var targetSpan = $(e.target), file, parts, name, ext = ""; + + // Get file name and split out name and extension + file = self.uploader.getFile(targetSpan.parents('tr')[0].id); + name = file.name; + parts = /^(.+)(\.[^.]+)$/.exec(name); + if (parts) { + name = parts[1]; + ext = parts[2]; + } + + // Display input element + targetSpan.hide().after(''); + targetSpan.next().val(name).focus().blur(function() { + targetSpan.show().next().remove(); + }).keydown(function(e) { + var targetInput = $(this); + + if ($.inArray(e.keyCode, [13, 27]) !== -1) { + e.preventDefault(); + + // Rename file and glue extension back on + if (e.keyCode === 13) { + file.name = targetInput.val() + ext; + targetSpan.text(file.name); + } + targetInput.blur(); + } + }); + }); + }, + + + _enableDragAndDrop: function() { + this.filelist.append('' + _("Drag files here.") + ''); + + this.filelist.parent().attr('id', this.id + '_dropbox'); + + this.uploader.settings.drop_element = this.options.drop_element = this.id + '_dropbox'; + }, + + + _enableSortingList: function() { + var idxStart, self = this; + + if ($('tbody tr', this.filelist).length < 2) { + return; + } + + $('tbody', this.filelist).sortable({ + containment: 'parent', + items: '.plupload_delete', + + helper: function(e, el) { + return el.clone(true).find('td:not(.plupload_file_name)').remove().end().css('width', '100%'); + }, + + start: function(e, ui) { + idxStart = $('tr', this).index(ui.item); + }, + + stop: function(e, ui) { + var i, length, idx, files = [], idxStop = $('tr', this).index(ui.item); + + for (i = 0, length = self.uploader.files.length; i < length; i++) { + + if (i === idxStop) { + idx = idxStart; + } else if (i === idxStart) { + idx = idxStop; + } else { + idx = i; + } + files[files.length] = self.uploader.files[idx]; + } + + files.unshift(files.length); + files.unshift(0); + + // re-populate files array + Array.prototype.splice.apply(self.uploader.files, files); + } + }); + }, + + _notify: function(type, message) { + var popup = $( + '
    ' + + '' + + '

    ' + message + '

    ' + + '
    ' + ); + + popup + .addClass('ui-state-' + (type === 'error' ? 'error' : 'highlight')) + .find('p .ui-icon') + .addClass('ui-icon-' + (type === 'error' ? 'alert' : 'info')) + .end() + .find('.plupload_message_close') + .click(function() { + popup.remove(); + }) + .end() + .appendTo('.plupload_header_content', this.container); + }, + + + + destroy: function() { + // unbind all button events + $('.plupload_button', this.element).unbind(); + + // destroy buttons + if ($.ui.button) { + $('.plupload_add, .plupload_start, .plupload_stop', this.container) + .button('destroy'); + } + + // destroy progressbar + if ($.ui.progressbar) { + this.progressbar.progressbar('destroy'); + } + + // destroy sortable behavior + if ($.ui.sortable && this.options.sortable) { + $('tbody', this.filelist).sortable('destroy'); + } + + // destroy uploader instance + this.uploader.destroy(); + + // restore the elements initial state + this.element + .empty() + .html(this.contents_bak); + this.contents_bak = ''; + + $.Widget.prototype.destroy.apply(this); + } +}); + + +} (window, document, plupload, jQuery)); diff --git a/assets/javascripts/plupload/plupload.browserplus.min.js b/assets/javascripts/plupload/plupload.browserplus.min.js new file mode 100644 index 00000000..a5b20b3b --- /dev/null +++ b/assets/javascripts/plupload/plupload.browserplus.min.js @@ -0,0 +1 @@ +(function(a){a.runtimes.BrowserPlus=a.addRuntime("browserplus",{getFeatures:function(){return{dragdrop:true,jpgresize:true,pngresize:true,chunks:true,progress:true,multipart:true}},init:function(g,i){var e=window.BrowserPlus,h={},d=g.settings,c=d.resize;function f(n){var m,l,j=[],k,o;for(l=0;l0){q(++s,u)}else{j.status=a.DONE;m.trigger("FileUploaded",j,{response:w.value.body,status:v});if(v>=400){m.trigger("Error",{code:a.HTTP_ERROR,message:a.translate("HTTP Error."),file:j,status:v})}}}else{m.trigger("Error",{code:a.GENERIC_ERROR,message:a.translate("Generic Error."),file:j,details:w.error})}})}function p(s){j.size=s.size;if(k){e.FileAccess.chunk({file:s,chunkSize:k},function(v){if(v.success){var w=v.value,t=w.length;n=Array(t);for(var u=0;u';function m(){return b.getElementById(j.id+"_flash")}function l(){if(p++>5000){o({success:false});return}if(!g[j.id]){setTimeout(l,1)}}l();n=i=null;j.bind("Flash:Init",function(){var s={},r,q=j.settings.resize||{};m().setFileFilters(j.settings.filters,j.settings.multi_selection);if(g[j.id]){return}g[j.id]=true;j.bind("UploadFile",function(t,u){var v=t.settings;m().uploadFile(s[u.id],v.url,{name:u.target_name||u.name,mime:d.mimeTypes[u.name.replace(/^.+\.([^.]+)/,"$1")]||"application/octet-stream",chunk_size:v.chunk_size,width:q.width,height:q.height,quality:q.quality||90,multipart:v.multipart,multipart_params:v.multipart_params||{},file_data_name:v.file_data_name,format:/\.(jpg|jpeg)$/i.test(u.name)?"jpg":"png",headers:v.headers,urlstream_upload:v.urlstream_upload})});j.bind("Flash:UploadProcess",function(u,t){var v=u.getFile(s[t.id]);if(v.status!=d.FAILED){v.loaded=t.loaded;v.size=t.size;u.trigger("UploadProgress",v)}});j.bind("Flash:UploadChunkComplete",function(t,v){var w,u=t.getFile(s[v.id]);w={chunk:v.chunk,chunks:v.chunks,response:v.text};t.trigger("ChunkUploaded",u,w);if(u.status!=d.FAILED){m().uploadNextChunk()}if(v.chunk==v.chunks-1){u.status=d.DONE;t.trigger("FileUploaded",u,{response:v.text})}});j.bind("Flash:SelectFiles",function(t,w){var v,u,x=[],y;for(u=0;uqo+Rc3ld|Sn}_srZYUG0{iA@rP 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 literal 0 HcmV?d00001 diff --git a/assets/javascripts/plupload/plupload.full.min.js b/assets/javascripts/plupload/plupload.full.min.js new file mode 100644 index 00000000..e04e1362 --- /dev/null +++ b/assets/javascripts/plupload/plupload.full.min.js @@ -0,0 +1,2 @@ +/*1.4.2*/ +(function(){var f=0,k=[],m={},i={},a={"<":"lt",">":"gt","&":"amp",'"':"quot","'":"#39"},l=/[<>&\"\']/g,b,c=window.setTimeout,d={},e;function h(){this.returnValue=false}function j(){this.cancelBubble=true}(function(n){var o=n.split(/,/),p,r,q;for(p=0;p0){g.each(o,function(r,q){n[q]=r})}});return n},cleanName:function(n){var o,p;p=[/[\300-\306]/g,"A",/[\340-\346]/g,"a",/\307/g,"C",/\347/g,"c",/[\310-\313]/g,"E",/[\350-\353]/g,"e",/[\314-\317]/g,"I",/[\354-\357]/g,"i",/\321/g,"N",/\361/g,"n",/[\322-\330]/g,"O",/[\362-\370]/g,"o",/[\331-\334]/g,"U",/[\371-\374]/g,"u"];for(o=0;o0?"&":"?")+p}return o},each:function(q,r){var p,o,n;if(q){p=q.length;if(p===b){for(o in q){if(q.hasOwnProperty(o)){if(r(q[o],o)===false){return}}}}else{for(n=0;n1073741824){return Math.round(n/1073741824,1)+" GB"}if(n>1048576){return Math.round(n/1048576,1)+" MB"}if(n>1024){return Math.round(n/1024,1)+" KB"}return n+" b"},getPos:function(o,s){var t=0,r=0,v,u=document,p,q;o=o;s=s||u.body;function n(B){var z,A,w=0,C=0;if(B){A=B.getBoundingClientRect();z=u.compatMode==="CSS1Compat"?u.documentElement:u.body;w=A.left+z.scrollLeft;C=A.top+z.scrollTop}return{x:w,y:C}}if(o&&o.getBoundingClientRect&&(navigator.userAgent.indexOf("MSIE")>0&&u.documentMode!==8)){p=n(o);q=n(s);return{x:p.x-q.x,y:p.y-q.y}}v=o;while(v&&v!=s&&v.nodeType){t+=v.offsetLeft||0;r+=v.offsetTop||0;v=v.offsetParent}v=o.parentNode;while(v&&v!=s&&v.nodeType){t-=v.scrollLeft||0;r-=v.scrollTop||0;v=v.parentNode}return{x:t,y:r}},getSize:function(n){return{w:n.offsetWidth||n.clientWidth,h:n.offsetHeight||n.clientHeight}},parseSize:function(n){var o;if(typeof(n)=="string"){n=/^([0-9]+)([mgk]+)$/.exec(n.toLowerCase().replace(/[^0-9mkg]/g,""));o=n[2];n=+n[1];if(o=="g"){n*=1073741824}if(o=="m"){n*=1048576}if(o=="k"){n*=1024}}return n},xmlEncode:function(n){return n?(""+n).replace(l,function(o){return a[o]?"&"+a[o]+";":o}):n},toArray:function(p){var o,n=[];for(o=0;o=0;p--){if(r[p].key===q||r[p].orig===u){if(t.detachEvent){t.detachEvent("on"+o,r[p].func)}else{if(t.removeEventListener){t.removeEventListener(o,r[p].func,false)}}r[p].orig=null;r[p].func=null;r.splice(p,1);if(u!==b){break}}}if(!r.length){delete d[t[e]][o]}if(n(d[t[e]])){delete d[t[e]];try{delete t[e]}catch(s){t[e]=b}}},removeAllEvents:function(o){var n=arguments[1];if(o[e]===b||!o[e]){return}g.each(d[o[e]],function(q,p){g.removeEvent(o,p,n)})}};g.Uploader=function(q){var o={},t,s=[],p;t=new g.QueueProgress();q=g.extend({chunk_size:0,multipart:true,multi_selection:true,file_data_name:"file",filters:[]},q);function r(){var v,w=0,u;if(this.state==g.STARTED){for(u=0;u0?Math.ceil(t.uploaded/s.length*100):0}else{t.bytesPerSec=Math.ceil(t.loaded/((+new Date()-p||1)/1000));t.percent=t.size>0?Math.ceil(t.loaded/t.size*100):0}}g.extend(this,{state:g.STOPPED,runtime:"",features:{},files:s,settings:q,total:t,id:g.guid(),init:function(){var z=this,A,w,v,y=0,x;if(typeof(q.preinit)=="function"){q.preinit(z)}else{g.each(q.preinit,function(C,B){z.bind(B,C)})}q.page_url=q.page_url||document.location.pathname.replace(/\/[^\/]+$/g,"/");if(!/^(\w+:\/\/|\/)/.test(q.url)){q.url=q.page_url+q.url}q.chunk_size=g.parseSize(q.chunk_size);q.max_file_size=g.parseSize(q.max_file_size);z.bind("FilesAdded",function(B,E){var D,C,G=0,H,F=q.filters;if(F&&F.length){H=[];g.each(F,function(I){g.each(I.extensions.split(/,/),function(J){H.push("\\."+J.replace(new RegExp("["+("/^$.*+?|()[]{}\\".replace(/./g,"\\$&"))+"]","g"),"\\$&"))})});H=new RegExp(H.join("|")+"$","i")}for(D=0;Dq.max_file_size){B.trigger("Error",{code:g.FILE_SIZE_ERROR,message:g.translate("File size error."),file:C});continue}s.push(C);G++}if(G){c(function(){z.trigger("QueueChanged");z.refresh()},1)}else{return false}});if(q.unique_names){z.bind("UploadFile",function(B,C){var E=C.name.match(/\.([^.]+)$/),D="tmp";if(E){D=E[1]}C.target_name=C.id+"."+D})}z.bind("UploadProgress",function(B,C){C.percent=C.size>0?Math.ceil(C.loaded/C.size*100):100;n()});z.bind("StateChanged",function(B){if(B.state==g.STARTED){p=(+new Date())}});z.bind("QueueChanged",n);z.bind("Error",function(B,C){if(C.file){C.file.status=g.FAILED;n();if(B.state==g.STARTED){c(function(){r.call(z)},1)}}});z.bind("FileUploaded",function(B,C){C.status=g.DONE;C.loaded=C.size;B.trigger("UploadProgress",C);c(function(){r.call(z)},1)});if(q.runtimes){w=[];x=q.runtimes.split(/\s?,\s?/);for(A=0;A=0;u--){if(s[u].id===v){return s[u]}}},removeFile:function(v){var u;for(u=s.length-1;u>=0;u--){if(s[u].id===v.id){return this.splice(u,1)[0]}}},splice:function(w,u){var v;v=s.splice(w===b?0:w,u===b?s.length:u);this.trigger("FilesRemoved",v);this.trigger("QueueChanged");return v},trigger:function(v){var x=o[v.toLowerCase()],w,u;if(x){u=Array.prototype.slice.call(arguments);u[0]=this;for(w=0;w=0;v--){if(x[v].func===w){x.splice(v,1);break}}}else{x=[]}if(!x.length){delete o[u]}}},unbindAll:function(){var u=this;g.each(o,function(w,v){u.unbind(v)})},destroy:function(){this.trigger("Destroy");this.unbindAll()}})};g.File=function(q,o,p){var n=this;n.id=q;n.name=o;n.size=p;n.loaded=0;n.percent=0;n.status=0};g.Runtime=function(){this.getFeatures=function(){};this.init=function(n,o){}};g.QueueProgress=function(){var n=this;n.size=0;n.loaded=0;n.uploaded=0;n.failed=0;n.queued=0;n.percent=0;n.bytesPerSec=0;n.reset=function(){n.size=n.loaded=n.uploaded=n.failed=n.queued=n.percent=n.bytesPerSec=0}};g.runtimes={};window.plupload=g})();(function(e,b,c,d){var f={};function a(l,h,o,n,g){var p,j,i,k;j=google.gears.factory.create("beta.canvas");try{j.decode(l);k=Math.min(h/j.width,o/j.height);if(k<1){j.resize(Math.round(j.width*k),Math.round(j.height*k));return j.encode(g,{quality:n/100})}}catch(m){}return l}c.runtimes.Gears=c.addRuntime("gears",{getFeatures:function(){return{dragdrop:true,jpgresize:true,pngresize:true,chunks:true,progress:true,multipart:true}},init:function(i,k){var j;if(!e.google||!google.gears){return k({success:false})}try{j=google.gears.factory.create("beta.desktop")}catch(h){return k({success:false})}function g(n){var m,l,o=[],p;for(l=0;l0;s=Math.ceil(o.size/p);if(!l){p=o.size;s=1}function m(){var y,A,v=r.settings.multipart,u=0,z={name:o.target_name||o.name},w=r.settings.url;function x(C){var B,H="----pluploadboundary"+c.guid(),E="--",G="\r\n",D,F;if(v){y.setRequestHeader("Content-Type","multipart/form-data; boundary="+H);B=google.gears.factory.create("beta.blobbuilder");c.each(c.extend(z,r.settings.multipart_params),function(J,I){B.append(E+H+G+'Content-Disposition: form-data; name="'+I+'"'+G+G);B.append(J+G)});F=c.mimeTypes[o.name.replace(/^.+\.([^.]+)/,"$1")]||"application/octet-stream";B.append(E+H+G+'Content-Disposition: form-data; name="'+r.settings.file_data_name+'"; filename="'+o.name+'"'+G+"Content-Type: "+F+G+G);B.append(C);B.append(G+E+H+E+G);D=B.getAsBlob();u=D.length-C.length;C=D}y.send(C)}if(o.status==c.DONE||o.status==c.FAILED||r.state==c.STOPPED){return}if(l){z.chunk=t;z.chunks=s}A=Math.min(p,o.size-(t*p));if(!v){w=c.buildUrl(r.settings.url,z)}y=google.gears.factory.create("beta.httprequest");y.open("POST",w);if(!v){y.setRequestHeader("Content-Disposition",'attachment; filename="'+o.name+'"');y.setRequestHeader("Content-Type","application/octet-stream")}c.each(r.settings.headers,function(C,B){y.setRequestHeader(B,C)});y.upload.onprogress=function(B){o.loaded=q+B.loaded-u;r.trigger("UploadProgress",o)};y.onreadystatechange=function(){var B;if(y.readyState==4){if(y.status==200){B={chunk:t,chunks:s,response:y.responseText,status:y.status};r.trigger("ChunkUploaded",o,B);if(B.cancelled){o.status=c.FAILED;return}q+=A;if(++t>=s){o.status=c.DONE;r.trigger("FileUploaded",o,{response:y.responseText,status:y.status})}else{m()}}else{r.trigger("Error",{code:c.HTTP_ERROR,message:c.translate("HTTP Error."),file:o,chunk:t,chunks:s,status:y.status})}}};if(t3){k.pop()}while(k.length<4){k.push(0)}l=r.split(".");while(l.length>4){l.pop()}do{t=parseInt(l[p],10);m=parseInt(k[p],10);p++}while(p8?"":0.01});o.className="plupload silverlight";if(p.settings.container){k=b.getElementById(p.settings.container);k.style.position="relative"}k.appendChild(o);for(l=0;l';function j(){return b.getElementById(p.id+"_silverlight").content.Upload}p.bind("Silverlight:Init",function(){var i,r={};if(h[p.id]){return}h[p.id]=true;p.bind("Silverlight:StartSelectFiles",function(s){i=[]});p.bind("Silverlight:SelectFile",function(s,v,t,u){var w;w=d.guid();r[w]=v;r[v]=w;i.push(new d.File(w,t,u))});p.bind("Silverlight:SelectSuccessful",function(){if(i.length){p.trigger("FilesAdded",i)}});p.bind("Silverlight:UploadChunkError",function(s,v,t,w,u){p.trigger("Error",{code:d.IO_ERROR,message:"IO Error.",details:u,file:s.getFile(r[v])})});p.bind("Silverlight:UploadFileProgress",function(s,w,t,v){var u=s.getFile(r[w]);if(u.status!=d.FAILED){u.size=v;u.loaded=t;s.trigger("UploadProgress",u)}});p.bind("Refresh",function(s){var t,u,v;t=b.getElementById(s.settings.browse_button);if(t){u=d.getPos(t,b.getElementById(s.settings.container));v=d.getSize(t);d.extend(b.getElementById(s.id+"_silverlight_container").style,{top:u.y+"px",left:u.x+"px",width:v.w+"px",height:v.h+"px"})}});p.bind("Silverlight:UploadChunkSuccessful",function(s,v,t,y,x){var w,u=s.getFile(r[v]);w={chunk:t,chunks:y,response:x};s.trigger("ChunkUploaded",u,w);if(u.status!=d.FAILED){j().UploadNextChunk()}if(t==y-1){u.status=d.DONE;s.trigger("FileUploaded",u,{response:x})}});p.bind("Silverlight:UploadSuccessful",function(s,v,t){var u=s.getFile(r[v]);u.status=d.DONE;s.trigger("FileUploaded",u,{response:t})});p.bind("FilesRemoved",function(s,u){var t;for(t=0;t';function m(){return b.getElementById(j.id+"_flash")}function l(){if(p++>5000){o({success:false});return}if(!g[j.id]){setTimeout(l,1)}}l();n=i=null;j.bind("Flash:Init",function(){var s={},r,q=j.settings.resize||{};m().setFileFilters(j.settings.filters,j.settings.multi_selection);if(g[j.id]){return}g[j.id]=true;j.bind("UploadFile",function(t,u){var v=t.settings;m().uploadFile(s[u.id],v.url,{name:u.target_name||u.name,mime:d.mimeTypes[u.name.replace(/^.+\.([^.]+)/,"$1")]||"application/octet-stream",chunk_size:v.chunk_size,width:q.width,height:q.height,quality:q.quality||90,multipart:v.multipart,multipart_params:v.multipart_params||{},file_data_name:v.file_data_name,format:/\.(jpg|jpeg)$/i.test(u.name)?"jpg":"png",headers:v.headers,urlstream_upload:v.urlstream_upload})});j.bind("Flash:UploadProcess",function(u,t){var v=u.getFile(s[t.id]);if(v.status!=d.FAILED){v.loaded=t.loaded;v.size=t.size;u.trigger("UploadProgress",v)}});j.bind("Flash:UploadChunkComplete",function(t,v){var w,u=t.getFile(s[v.id]);w={chunk:v.chunk,chunks:v.chunks,response:v.text};t.trigger("ChunkUploaded",u,w);if(u.status!=d.FAILED){m().uploadNextChunk()}if(v.chunk==v.chunks-1){u.status=d.DONE;t.trigger("FileUploaded",u,{response:v.text})}});j.bind("Flash:SelectFiles",function(t,w){var v,u,x=[],y;for(u=0;u0){q(++s,u)}else{j.status=a.DONE;m.trigger("FileUploaded",j,{response:w.value.body,status:v});if(v>=400){m.trigger("Error",{code:a.HTTP_ERROR,message:a.translate("HTTP Error."),file:j,status:v})}}}else{m.trigger("Error",{code:a.GENERIC_ERROR,message:a.translate("Generic Error."),file:j,details:w.error})}})}function p(s){j.size=s.size;if(k){e.FileAccess.chunk({file:s,chunkSize:k},function(v){if(v.success){var w=v.value,t=w.length;n=Array(t);for(var u=0;u0&&navigator.vendor.indexOf("Apple")!==-1;return{html5:k,dragdrop:m.mozInnerScreenX!==c||j||d,jpgresize:l,pngresize:l,multipart:l||!!m.FileReader||!!m.FormData,progress:n,chunking:j||l,canOpenDialog:navigator.userAgent.indexOf("WebKit")!==-1}},init:function(m,n){var j={},k;function l(s){var q,p,r=[],t,o={};for(p=0;p";A=g.getElementById(m.id+"_html5");A.onchange=function(){l(this.files);this.value=""};B=g.getElementById(s.settings.browse_button);if(B){var u=s.settings.browse_button_hover,v=s.settings.browse_button_active,t=s.features.canOpenDialog?B:C;if(u){f.addEvent(t,"mouseover",function(){f.addClass(B,u)},s.id);f.addEvent(t,"mouseout",function(){f.removeClass(B,u)},s.id)}if(v){f.addEvent(t,"mousedown",function(){f.addClass(B,v)},s.id);f.addEvent(g.body,"mouseup",function(){f.removeClass(B,v)},s.id)}if(s.features.canOpenDialog){f.addEvent(B,"click",function(y){g.getElementById(s.id+"_html5").click();y.preventDefault()},s.id)}}});m.bind("PostInit",function(){var o=g.getElementById(m.settings.drop_element);if(o){if(d){f.addEvent(o,"dragenter",function(s){var r,p,q;r=g.getElementById(m.id+"_drop");if(!r){r=g.createElement("input");r.setAttribute("type","file");r.setAttribute("id",m.id+"_drop");r.setAttribute("multiple","multiple");f.addEvent(r,"change",function(){l(this.files);f.removeEvent(r,"change",m.id);r.parentNode.removeChild(r)},m.id);o.appendChild(r)}p=f.getPos(o,g.getElementById(m.settings.container));q=f.getSize(o);f.extend(o.style,{position:"relative"});f.extend(r.style,{position:"absolute",display:"block",top:0,left:0,width:q.w+"px",height:q.h+"px",opacity:0})},m.id);return}f.addEvent(o,"dragover",function(p){p.preventDefault()},m.id);f.addEvent(o,"drop",function(q){var p=q.dataTransfer;if(p&&p.files){l(p.files)}q.preventDefault()},m.id)}});m.bind("Refresh",function(o){var p,r,s,t,q;p=g.getElementById(m.settings.browse_button);if(p){r=f.getPos(p,g.getElementById(o.settings.container));s=f.getSize(p);t=g.getElementById(m.id+"_html5_container");f.extend(t.style,{top:r.y+"px",left:r.x+"px",width:s.w+"px",height:s.h+"px"});if(m.features.canOpenDialog){q=parseInt(p.parentNode.style.zIndex,10);if(isNaN(q)){q=0}f.extend(p.style,{position:"relative",zIndex:q});f.extend(t.style,{zIndex:q-1})}}});m.bind("UploadFile",function(o,q){var r=o.settings,t,p;function s(u){var x=0,w=0;function v(){var E=u,L,M,H,I,J=0,A="----pluploadboundary"+f.guid(),D,F,B,C="--",K="\r\n",G="",z,y=o.settings.url;if(q.status==f.DONE||q.status==f.FAILED||o.state==f.STOPPED){return}I={name:q.target_name||q.name};if(r.chunk_size&&k.chunking){D=r.chunk_size;H=Math.ceil(q.size/D);F=Math.min(D,q.size-(x*D));if(typeof(u)=="string"){E=u.substring(x*D,x*D+F)}else{E=u.slice(x*D,F)}I.chunk=x;I.chunks=H}else{F=q.size}L=new XMLHttpRequest();M=L.upload;if(M){M.onprogress=function(N){q.loaded=Math.min(q.size,w+N.loaded-J);o.trigger("UploadProgress",q)}}if(!o.settings.multipart||!k.multipart){y=f.buildUrl(o.settings.url,I)}else{I.name=q.target_name||q.name}L.open("post",y,true);L.onreadystatechange=function(){var N,P;if(L.readyState==4){try{N=L.status}catch(O){N=0}if(N>=400){o.trigger("Error",{code:f.HTTP_ERROR,message:f.translate("HTTP Error."),file:q,status:N})}else{if(H){P={chunk:x,chunks:H,response:L.responseText,status:N};o.trigger("ChunkUploaded",q,P);w+=F;if(P.cancelled){q.status=f.FAILED;return}q.loaded=Math.min(q.size,(x+1)*D)}else{q.loaded=q.size}o.trigger("UploadProgress",q);if(!H||++x>=H){q.status=f.DONE;o.trigger("FileUploaded",q,{response:L.responseText,status:N});t=u=j[q.id]=null}else{v()}}L=E=B=G=null}};f.each(o.settings.headers,function(O,N){L.setRequestHeader(N,O)});if(o.settings.multipart&&k.multipart){if(!L.sendAsBinary){B=new FormData();f.each(f.extend(I,o.settings.multipart_params),function(O,N){B.append(N,O)});B.append(o.settings.file_data_name,E);L.send(B);return}L.setRequestHeader("Content-Type","multipart/form-data; boundary="+A);f.each(f.extend(I,o.settings.multipart_params),function(O,N){G+=C+A+K+'Content-Disposition: form-data; name="'+N+'"'+K+K;G+=unescape(encodeURIComponent(O))+K});z=f.mimeTypes[q.name.replace(/^.+\.([^.]+)/,"$1")]||"application/octet-stream";G+=C+A+K+'Content-Disposition: form-data; name="'+o.settings.file_data_name+'"; filename="'+unescape(encodeURIComponent(q.name))+'"'+K+"Content-Type: "+z+K+K+E+K+C+A+C+K;J=G.length-E.length;E=G}else{L.setRequestHeader("Content-Type","application/octet-stream")}if(L.sendAsBinary){L.sendAsBinary(E)}else{L.send(E)}}v()}t=j[q.id];p=o.settings.resize;if(k.jpgresize){if(p&&/\.(png|jpg|jpeg)$/i.test(q.name)){b(t,p.width,p.height,/\.png$/i.test(q.name)?"image/png":"image/jpeg",function(u){if(u.success){q.size=u.data.length;s(u.data)}else{h(t,s)}})}else{h(t,s)}}else{s(t)}});m.bind("Destroy",function(o){var q,r,p=g.body,s={inputContainer:o.id+"_html5_container",inputFile:o.id+"_html5",browseButton:o.settings.browse_button,dropElm:o.settings.drop_element};for(q in s){r=g.getElementById(s[q]);if(r){f.removeAllEvents(r,o.id)}}f.removeAllEvents(g.body,o.id);if(o.settings.container){p=g.getElementById(o.settings.container)}p.removeChild(g.getElementById(s.inputContainer))});n({success:true})}});a=function(){var l,m,B,v,w,q,u,x,G,s,D,z,n,F,j,E,C,o,k;function r(){var K=false,I;function L(N,P){var M=K?0:-8*(P-1),Q=0,O;for(O=0;O>Math.abs(M+P*8))&255)}H(N,R,true)}return{II:function(M){if(M===j){return K}else{K=M}},init:function(M){I=M},SEGMENT:function(M,O,N){if(!arguments.length){return I}if(typeof O=="number"){return I.substr(parseInt(M,10),O)}H(M,O,N)},BYTE:function(M){return L(M,1)},SHORT:function(M){return L(M,2)},LONG:function(M,N){if(N===j){return L(M,4)}else{J(M,N,4)}},SLONG:function(M){var N=L(M,4);return(N>2147483647?N-4294967296:N)},STRING:function(M,N){var O="";for(N+=M;M4){K=G.LONG(K)+F}for(R=0;R4){K=G.LONG(K)+F}S[T]=G.STRING(K,M-1);continue;case 3:if(M>2){K=G.LONG(K)+F}for(R=0;R1){K=G.LONG(K)+F}for(R=0;R