* finishing import
git-svn-id: http://redmine-dmsf.googlecode.com/svn/trunk/redmine_dmsf@4 5e329b0b-a2ee-ea63-e329-299493fc886d
80
README.txt
Normal file
@ -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:
|
||||
<ul>
|
||||
<li>
|
||||
DMSF:
|
||||
<ul>
|
||||
<li><strong>{{dmsf(17)}}</strong> (link to file with id 17)</li>
|
||||
</ul>
|
||||
DMSF file id can be found in link for file download
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
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.
|
||||
200
app/controllers/dmsf_controller.rb
Normal file
@ -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
|
||||
215
app/controllers/dmsf_detail_controller.rb
Normal file
@ -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
|
||||
106
app/controllers/dmsf_state_controller.rb
Normal file
@ -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
|
||||
3
app/helpers/dmsf_access_error.rb
Normal file
@ -0,0 +1,3 @@
|
||||
class DmsfAccessError < StandardError
|
||||
|
||||
end
|
||||
3
app/helpers/dmsf_content_error.rb
Normal file
@ -0,0 +1,3 @@
|
||||
class DmsfContentError < StandardError
|
||||
|
||||
end
|
||||
26
app/helpers/dmsf_helper.rb
Normal file
@ -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
|
||||
59
app/helpers/dmsf_upload.rb
Normal file
@ -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
|
||||
41
app/helpers/dmsf_zip.rb
Normal file
@ -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
|
||||
257
app/models/dmsf_file.rb
Normal file
@ -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
|
||||
5
app/models/dmsf_file_lock.rb
Normal file
@ -0,0 +1,5 @@
|
||||
class DmsfFileLock < ActiveRecord::Base
|
||||
unloadable
|
||||
belongs_to :file, :class_name => "DmsfFile", :foreign_key => "dmsf_file_id"
|
||||
belongs_to :user
|
||||
end
|
||||
213
app/models/dmsf_file_revision.rb
Normal file
@ -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
|
||||
60
app/models/dmsf_folder.rb
Normal file
@ -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
|
||||
|
||||
86
app/models/dmsf_mailer.rb
Normal file
@ -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
|
||||
19
app/models/dmsf_user_pref.rb
Normal file
@ -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
|
||||
|
||||
9
app/views/dmsf/_path.html.erb
Normal file
@ -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 %>
|
||||
15
app/views/dmsf/_user_pref.html.erb
Normal file
@ -0,0 +1,15 @@
|
||||
<div class="box">
|
||||
<% form_tag({:controller => "dmsf_state", :action => "user_pref", :id => @project, :current => URI.escape(request.url)},
|
||||
:method=>:post) do %>
|
||||
<div style="padding-top: 0px; float: right;">
|
||||
<%= submit_tag("Save", :title => "Save preferences", :style => "font-size: 0.9em;", :tabindex => 10) %>
|
||||
</div>
|
||||
<h3>Your <%= l(:dmsf) %> preferences for project</h3>
|
||||
<div>
|
||||
Notifications:
|
||||
<%= select_tag("email_notify",
|
||||
options_for_select([["Default", nil], ["Activated", true], ["Deactivated", false]],
|
||||
:selected => DmsfUserPref.for(@project, User.current).email_notify), :tabindex => 1) %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
43
app/views/dmsf/email_entries.html.erb
Normal file
@ -0,0 +1,43 @@
|
||||
<% html_title("DMSF") %>
|
||||
|
||||
<div class="contextual">
|
||||
</div>
|
||||
|
||||
<% path = @folder.nil? ? [] : @folder.dmsf_path %>
|
||||
<h2>
|
||||
<%= render(:partial => 'path', :locals => {:path => path}) %>
|
||||
</h2>
|
||||
|
||||
<h3>Send documents by email</h3>
|
||||
|
||||
<% form_tag({:action => "email_entries_send", :id => @project, :folder_id => @folder},
|
||||
{ :method=>:post, :class => "tabular"}) do %>
|
||||
<div class="box">
|
||||
<p>
|
||||
<%= label_tag("", "From:") %>
|
||||
<%= h(User.current.mail) %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("email[to]", "To:") %>
|
||||
<%= text_field_tag("email[to]", @email_params["to"], :style => "width: 90%;") %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("email[cc]", "CC:") %>
|
||||
<%= text_field_tag("email[cc]", @email_params["cc"], :style => "width: 90%;") %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("email[subject]", "Subject:") %>
|
||||
<%= text_field_tag("email[subject]", @email_params["subject"], :style => "width: 90%;") %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("", "Documents:") %>
|
||||
<strong>Documents.zip</strong>
|
||||
<%= hidden_field_tag("email[zipped_content]", @email_params["zipped_content"]) %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("email[body]", "Body:") %>
|
||||
<%= text_area_tag("email[body]", @email_params["body"], :rows=> "20", :style => "width: 90%;") %>
|
||||
</p>
|
||||
<p><%= submit_tag("Send") %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
305
app/views/dmsf/index.html.erb
Normal file
@ -0,0 +1,305 @@
|
||||
<% html_title("DMSF") %>
|
||||
|
||||
<div class="contextual">
|
||||
<% 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 %>
|
||||
</div>
|
||||
|
||||
<% path = @folder.nil? ? [] : @folder.dmsf_path %>
|
||||
<h2>
|
||||
<%= render(:partial => 'path', :locals => {:path => path}) %>
|
||||
</h2>
|
||||
|
||||
<div class="wiki">
|
||||
<%= textilizable(@folder.description) unless @folder.nil? %>
|
||||
</div>
|
||||
|
||||
<%
|
||||
form_tag({:action => "entries_operation", :id => @project, :folder_id => @folder}, :method => :post,
|
||||
:class => "dmfs_entries", :id => "Entries") do
|
||||
%>
|
||||
<%= hidden_field_tag("action") %>
|
||||
<table class="list entries" id="browser">
|
||||
<thead>
|
||||
<tr id="root">
|
||||
<th style="width: 17px; padding: 2px; text-align: left;">
|
||||
<input id="check_all_entries" title="Check/Uncheck all for zip download or email" type="checkbox" />
|
||||
</th>
|
||||
<%= 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") %>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @subfolders.each do |subfolder| %>
|
||||
<tr class="dir">
|
||||
<td class="size"><%= check_box_tag("subfolders[]", subfolder.id, false, :title => "Check for multi download or email") %></td>
|
||||
<td class="filename">
|
||||
<%= link_to(h(subfolder.name),
|
||||
{:action => "index", :folder_id => subfolder},
|
||||
:class => "icon icon-folder") %>
|
||||
</td>
|
||||
<td class="size">-</td>
|
||||
<td class="modified">-</td>
|
||||
<td class="version">-</td>
|
||||
<td class="author">-</td>
|
||||
<td class="actions">
|
||||
<div class="right_icon_box">
|
||||
<% 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 %>
|
||||
</div><br class="clear" />
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% @files.each do |file| %>
|
||||
<tr class="file">
|
||||
<td class="size"><%= check_box_tag("files[]", file.id, false, :title => "Check for zip download or email") %></td>
|
||||
<td class="filename">
|
||||
<%= 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") %>
|
||||
<div class="filename">
|
||||
(<%= h(file.display_name) %>)</div><br class="clear" />
|
||||
</td>
|
||||
<td class="size"><%= number_to_human_size(file.last_revision.size) unless file.last_revision.nil? %></td>
|
||||
<td class="modified">
|
||||
<%= 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 %>
|
||||
</td>
|
||||
<td class="version">
|
||||
<%= 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 %>
|
||||
</td>
|
||||
<td class="author"><%= h(file.last_revision.user) unless file.last_revision.nil? %></td>
|
||||
<td class="actions">
|
||||
<div class="right_icon_box">
|
||||
<%= 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 %>
|
||||
</div><br class="clear" />
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="controls">
|
||||
<%= submit_tag("Download", :title => "Download checked in zip archive", :name => "download_entries") %>
|
||||
<!-- <button type="button" id="EntriesDeleteButton" title="Delete checked">Delete</button> -->
|
||||
<%= submit_tag("Email", :title => "Send checked by email", :name => "email_entries") %>
|
||||
</div>
|
||||
<br style="clear: both"/>
|
||||
<% end %>
|
||||
|
||||
<div class="box">
|
||||
<h3>File Upload</h3>
|
||||
<% form_tag({:controller => "dmsf_detail", :action => "upload_files", :id => @project, :folder_id => @folder},
|
||||
:id => "uploadform", :method=>:post, :multipart => true) do %>
|
||||
<div style="padding-bottom: 6px; line-height: 1.4em; font-size: 0.9em;">
|
||||
<div class="upload_select">
|
||||
File size: <select id="uploader_select"><option value="1">< 100 MB</option><option value="2">> 100 MB</option></select>
|
||||
</div>
|
||||
<span>There can be uploaded maximum of 20 files at once. To upload files greater than 2GB you must have 64b browser.</span>
|
||||
<br style="clear: both" />
|
||||
</div>
|
||||
<div id="uploader">
|
||||
<p>
|
||||
<span id="uploaded_files_fields">
|
||||
<%= file_field_tag("uploaded_files[1]", :size => 30, :id => nil) %>
|
||||
</span>
|
||||
<br />
|
||||
<small><%= 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) %>)
|
||||
</small>
|
||||
</p>
|
||||
<%= submit_tag("Upload") %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= 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" %>
|
||||
<script type="text/javascript">
|
||||
jQuery.noConflict();
|
||||
|
||||
var originalUploaderContent;
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
jQuery("a.delete-link").click(function(event) {
|
||||
if(!window.confirm("<%= l(:question_do_you_really_want_to_delete_this_entry) %>")) event.preventDefault();
|
||||
})
|
||||
|
||||
jQuery("#check_all_entries").click(function(event) {
|
||||
var $this = jQuery(this);
|
||||
if($this.attr("checked")) {
|
||||
jQuery("input[type=checkbox]", jQuery("#browser > tbody")).attr("checked", true);
|
||||
} else {
|
||||
jQuery("input[type=checkbox]", jQuery("#browser > tbody")).attr("checked", false);
|
||||
}
|
||||
})
|
||||
|
||||
jQuery("#EntriesDeleteButton").click(function(event) {
|
||||
if(!window.confirm("Really?")) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
var operation = jQuery("input[name=EntriesOperation]");
|
||||
operation.val("delete");
|
||||
var entries = jQuery("#Entries");
|
||||
entries.submit();
|
||||
})
|
||||
|
||||
var uploader = jQuery('#uploader');
|
||||
|
||||
originalUploaderContent = uploader.html();
|
||||
|
||||
jQuery('#uploader_select').change(function() {
|
||||
if(jQuery(this).val() == 2) {
|
||||
uploader.html(originalUploaderContent);
|
||||
} else {
|
||||
initPlUploader(uploader);
|
||||
}
|
||||
});
|
||||
|
||||
initPlUploader(uploader);
|
||||
|
||||
});
|
||||
|
||||
function initPlUploader(uploader) {
|
||||
uploader.html("<div></div>");
|
||||
uploader = jQuery("div",uploader);
|
||||
uploader.plupload({
|
||||
runtimes : 'gears,html5,flash,html4',
|
||||
url : '<%= url_for({:controller => "dmsf_detail", :action => "upload_file", :id => @project, :folder_id => @folder}) %>',
|
||||
max_file_size : '100mb',
|
||||
max_file_count: 20, // user can add no more then 20 files at a time
|
||||
multipart: true,
|
||||
multipart_params : {authenticity_token : jQuery("input[name=authenticity_token]").val()},
|
||||
|
||||
// Rename files by clicking on their titles
|
||||
rename: true,
|
||||
|
||||
// Flash settings
|
||||
flash_swf_url : '<%= Engines::RailsExtensions::AssetHelpers.plugin_asset_path("redmine_dmsf", "javascripts", "plupload/plupload.flash.swf") %>'
|
||||
});
|
||||
jQuery(".plupload_scroll",uploader).resizable({
|
||||
handles: "s"
|
||||
});
|
||||
var pluploader = uploader.plupload('getUploader');
|
||||
|
||||
pluploader.bind('FileUploaded', function(pluploader, file, response) {
|
||||
var responseObject = jQuery.parseJSON(response.response);
|
||||
var disk_filename_input = jQuery("<input/>").attr("type","hidden")
|
||||
.attr("name","uploaded[" + dmsfFileFieldCount + "][disk_filename]")
|
||||
.val(responseObject.disk_filename);
|
||||
uploader.append(disk_filename_input);
|
||||
var content_type_input = jQuery("<input/>").attr("type","hidden")
|
||||
.attr("name","uploaded[" + dmsfFileFieldCount + "][content_type]")
|
||||
.val(responseObject.content_type);
|
||||
uploader.append(content_type_input);
|
||||
var original_filename_input = jQuery("<input/>").attr("type","hidden")
|
||||
.attr("name","uploaded[" + dmsfFileFieldCount + "][original_filename]")
|
||||
.val(responseObject.original_filename);
|
||||
uploader.append(original_filename_input);
|
||||
|
||||
if(pluploader.total.uploaded == pluploader.files.length) jQuery('#uploadform').submit();
|
||||
else dmsfFileFieldCount++;
|
||||
});
|
||||
}
|
||||
|
||||
var dmsfFileFieldCount = 1;
|
||||
|
||||
function dmsfAddFileField() {
|
||||
if (dmsfFileFieldCount >= 10) return false
|
||||
dmsfFileFieldCount++;
|
||||
|
||||
var f = document.createElement("input");
|
||||
f.type = "file";
|
||||
f.name = "uploaded_files[" + dmsfFileFieldCount + "]";
|
||||
f.size = 30;
|
||||
|
||||
var p = document.getElementById("uploaded_files_fields");
|
||||
p.appendChild(document.createElement("br"));
|
||||
p.appendChild(f);
|
||||
}
|
||||
|
||||
</script>
|
||||
<% end %>
|
||||
1
app/views/dmsf_detail/_file_locked.html.erb
Normal file
@ -0,0 +1 @@
|
||||
<p class="warning">File locked!</p>
|
||||
68
app/views/dmsf_detail/_file_new_revision.html.erb
Normal file
@ -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
|
||||
%>
|
||||
<div class="box dmsf_detail">
|
||||
<fieldset class="tabular">
|
||||
<legend>Create New Revision <a href="#" id="newRevisionFormContentToggle">[-]</a></legend>
|
||||
<div id="newRevisionFormContent">
|
||||
<div class="splitcontentleft">
|
||||
<p>
|
||||
<%= label_tag("file[title]", "Title:") %>
|
||||
<%= text_field_tag("file[title]", @file.last_revision.title, :size => "32") %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("file[description]", "Description:") %>
|
||||
<%= text_area_tag("file[description]", @file.last_revision.description, :rows=> "6") %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%= label_tag("file[file]", "New content:") %>
|
||||
<%= file_field_tag("file[file]", :size => 30, :id => "fileFileUpload") %>
|
||||
<br />
|
||||
<small>
|
||||
(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)
|
||||
</small>
|
||||
</p>
|
||||
<p>
|
||||
<%= 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<br />
|
||||
<%= radio_button_tag("file[version]", "minor", false, :id => "fileMinorVersionRadio") %>
|
||||
<%= @file.last_revision.major_version %>.<%= @file.last_revision.minor_version + 1 %> Minor<br />
|
||||
<%= radio_button_tag("file[version]", "major") %>
|
||||
<%= @file.last_revision.major_version + 1 %>.0 Major<br />
|
||||
</p>
|
||||
<p>
|
||||
<%= 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)) %>
|
||||
</p>
|
||||
</div>
|
||||
<div class="splitcontentright">
|
||||
<p>
|
||||
<%= label_tag("file[comment]", "Comment:") %>
|
||||
<%= text_area_tag("file[comment]", "", :rows=> "2") %>
|
||||
</p>
|
||||
</div>
|
||||
<br style="clear: both;"/>
|
||||
<br />
|
||||
<%= submit_tag("Create") %>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<% end %>
|
||||
38
app/views/dmsf_detail/_upload_file.html.erb
Normal file
@ -0,0 +1,38 @@
|
||||
<div class="box dmsf_upload">
|
||||
<%= hidden_field_tag("commited_files[#{i}][disk_filename]", upload.disk_filename) %>
|
||||
<fieldset class="tabular">
|
||||
<legend><%= h(upload.name) %></legend>
|
||||
<%= hidden_field_tag("commited_files[#{i}][name]", upload.name) %>
|
||||
<div class="splitcontentleft">
|
||||
<p>
|
||||
<%= label_tag("commited_files[#{i}][title]", "Title:") %>
|
||||
<%= text_field_tag("commited_files[#{i}][title]", upload.title, :size => "32") %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("commited_files[#{i}][description]", "Description:") %>
|
||||
<%= text_area_tag("commited_files[#{i}][description]", upload.description, :rows=> "6") %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("commited_files[#{i}][version]_minor", "Version:") %>
|
||||
<%= radio_button_tag("commited_files[#{i}][version]", "minor", true) %>
|
||||
<%= upload.major_version %>.<%= upload.minor_version + 1 %><br />
|
||||
<%= radio_button_tag("commited_files[#{i}][version]", "major") %>
|
||||
<%= upload.major_version + 1 %>.0<br />
|
||||
</p>
|
||||
</div>
|
||||
<div class="splitcontentright">
|
||||
<p>
|
||||
<%= label_tag("commited_files[#{i}][comment]", "Comment:") %>
|
||||
<%= text_area_tag("commited_files[#{i}][comment]", upload.comment, :rows=> "2") %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("", "Mime:") %>
|
||||
<%= h(upload.mime_type) %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("", "Size:") %>
|
||||
<%= number_to_human_size(upload.size) %>
|
||||
</p>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
31
app/views/dmsf_detail/_upload_file_locked.html.erb
Normal file
@ -0,0 +1,31 @@
|
||||
<div class="box dmsf_upload">
|
||||
<%= hidden_field_tag("commited_files[#{i}][disk_filename]", upload.disk_filename) %>
|
||||
<fieldset class="tabular">
|
||||
<legend><%= h(upload.name) %></legend>
|
||||
<p class="warning">File locked!</p>
|
||||
<div class="splitcontentleft">
|
||||
<p>
|
||||
<%= label_tag("", "Title:") %>
|
||||
<%= h(upload.title) %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("", "Description:") %>
|
||||
<%= h(upload.description) %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("", "Version:") %>
|
||||
<%= upload.major_version %>.<%= upload.minor_version %>
|
||||
</p>
|
||||
</div>
|
||||
<div class="splitcontentright">
|
||||
<p>
|
||||
<%= label_tag("", "Mime:") %>
|
||||
<%= h(upload.mime_type) %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("", "Size:") %>
|
||||
<%= number_to_human_size(upload.size) %>
|
||||
</p>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
114
app/views/dmsf_detail/file_detail.html.erb
Normal file
@ -0,0 +1,114 @@
|
||||
<% html_title("DMSF") %>
|
||||
|
||||
<div class="contextual">
|
||||
</div>
|
||||
|
||||
<% path = @file.folder.nil? ? [] : @file.folder.dmsf_path %>
|
||||
<h2>
|
||||
<%= render(:partial => "/dmsf/path", :locals => {:path => path}) %>
|
||||
/
|
||||
<%= h(@file.last_revision.title) %> <div style="float: right"><%= h(@file.name) %></div>
|
||||
<% if @file.notification %>
|
||||
<%= image_tag("notify.png", :plugin => "redmine_dmsf", :alt => "Not. act.",
|
||||
:title => "Notifications active") %>
|
||||
<% end %>
|
||||
</h2>
|
||||
|
||||
<% 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 %>
|
||||
|
||||
<h3>Revisions</h3>
|
||||
<% @file.revisions.each do |revision| %>
|
||||
<div class="box dmsf_detail">
|
||||
<fieldset class="tabular">
|
||||
<legend>
|
||||
<%= revision.source_revision.nil? ? "Created:" : "Changed:" %>
|
||||
<%= revision.updated_at.strftime("%Y-%m-%d %H:%M:%S") %>
|
||||
by
|
||||
<%= h(revision.user) %>
|
||||
</legend>
|
||||
<div class="splitcontentleft">
|
||||
<p>
|
||||
<%= label_tag("", "Title:") %>
|
||||
<%= h(revision.title) %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("", "Description:") %>
|
||||
<%= h(revision.description) %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("", "Version:") %>
|
||||
<%= revision.major_version %>.<%= revision.minor_version %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("", "Workflow:") %>
|
||||
<%= case revision.workflow
|
||||
when 1 then "Waiting for approval"
|
||||
when 2 then "Approved"
|
||||
else "None"
|
||||
end %>
|
||||
</p>
|
||||
</div>
|
||||
<div class="splitcontentright">
|
||||
<p>
|
||||
<%= label_tag("", "Comment:") %>
|
||||
<%= h(revision.comment) %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("", "Mime:") %>
|
||||
<%= h(revision.mime_type) %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("", "Size:") %>
|
||||
<%= number_to_human_size(revision.size) %>
|
||||
</p>
|
||||
</div>
|
||||
<br style="clear: both;" />
|
||||
<div>
|
||||
<%= 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 %>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<% 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" %>
|
||||
<script type="text/javascript">
|
||||
jQuery.noConflict();
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
jQuery("a.delete-link").click(function(event) {
|
||||
if(!window.confirm("<%= l(:question_do_you_really_want_to_delete_this_revision) %>")) event.preventDefault();
|
||||
})
|
||||
|
||||
jQuery("#fileFileUpload").change(function() {
|
||||
if(jQuery("input[name=\"file[version]\"]:checked").val() == "same") {
|
||||
jQuery("#fileMinorVersionRadio").attr("checked", true);
|
||||
}
|
||||
jQuery("#fileSameVersionRadio").attr("disabled", "disabled");
|
||||
});
|
||||
|
||||
jQuery("#newRevisionFormContentToggle").toggle(function() {
|
||||
jQuery("#newRevisionFormContentToggle").text("[-]");
|
||||
jQuery("#newRevisionFormContent").show();
|
||||
}, function() {
|
||||
jQuery("#newRevisionFormContentToggle").text("[+]");
|
||||
jQuery("#newRevisionFormContent").hide();
|
||||
});
|
||||
jQuery("#newRevisionFormContentToggle").text("[+]");
|
||||
jQuery("#newRevisionFormContent").hide();
|
||||
});
|
||||
</script>
|
||||
<% end %>
|
||||
29
app/views/dmsf_detail/folder_detail.html.erb
Normal file
@ -0,0 +1,29 @@
|
||||
<% html_title("DMSF") %>
|
||||
|
||||
<div class="contextual">
|
||||
</div>
|
||||
|
||||
<h2>
|
||||
<%= render(:partial => "/dmsf/path", :locals => {:path => @folder.dmsf_path}) %>
|
||||
</h2>
|
||||
|
||||
<% form_tag({:action => "save_folder", :id => @project, :folder_id => @folder},
|
||||
:method=>:post, :multipart => true, :class => "tabular") do %>
|
||||
<div class="box">
|
||||
<p>
|
||||
<%= label_tag("title", "Title:") %>
|
||||
<%= text_field_tag("title", @folder.name, :size => "32") %>
|
||||
</p>
|
||||
<p>
|
||||
<%= label_tag("description", "Description:") %>
|
||||
<%= text_area_tag("description", @folder.description, :rows => 15, :class => "wiki-edit") %>
|
||||
</p>
|
||||
</div>
|
||||
<%= submit_tag("Save") %>
|
||||
<% end %>
|
||||
|
||||
<%= wikitoolbar_for "description" %>
|
||||
|
||||
<% content_for :header_tags do %>
|
||||
<%= stylesheet_link_tag "dmsf", :plugin => "redmine_dmsf" %>
|
||||
<% end %>
|
||||
2
app/views/dmsf_detail/upload_file.html.erb
Normal file
@ -0,0 +1,2 @@
|
||||
{"original_filename":"<%= h(@tempfile.original_filename) %>", "content_type":"<%= h(@tempfile.content_type) %>",
|
||||
"disk_filename":"<%= h(@disk_filename) %>"}
|
||||
41
app/views/dmsf_detail/upload_files.html.erb
Normal file
@ -0,0 +1,41 @@
|
||||
<% html_title("DMSF") %>
|
||||
|
||||
<div class="contextual">
|
||||
</div>
|
||||
|
||||
<% path = @folder.nil? ? [] : @folder.dmsf_path %>
|
||||
<h2>
|
||||
<%= render(:partial => '/dmsf/path', :locals => {:path => path}) %>
|
||||
</h2>
|
||||
|
||||
<div class="wiki">
|
||||
<%= textilizable(@folder.description) unless @folder.nil? %>
|
||||
</div>
|
||||
|
||||
<h3>Uploaded Files</h3>
|
||||
<%
|
||||
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" %>
|
||||
<script type="text/javascript">
|
||||
jQuery.noConflict();
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
});
|
||||
</script>
|
||||
<% end %>
|
||||
16
app/views/dmsf_mailer/files_deleted.text.html.rhtml
Normal file
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
|
||||
</head>
|
||||
<body>
|
||||
User <%= @user %> deleted following files in project <%= @project.name %>:
|
||||
<% @files.each do |file| %>
|
||||
<p>
|
||||
<%= h(file.dmsf_path_str) %>
|
||||
(<%= number_to_human_size(file.last_revision.size) %>),
|
||||
version: <%= file.last_revision.major_version %>.<%= file.last_revision.minor_version %>
|
||||
</p>
|
||||
<% end %>
|
||||
</body>
|
||||
</html>
|
||||
5
app/views/dmsf_mailer/files_deleted.text.plain.rhtml
Normal file
@ -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 %>
|
||||
21
app/views/dmsf_mailer/files_updated.text.html.rhtml
Normal file
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
|
||||
</head>
|
||||
<body>
|
||||
User <%= @user %> actualized following files in project <%= @project.name %>:
|
||||
<% @files.each do |file| %>
|
||||
<p>
|
||||
<%= 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 }) %>
|
||||
</p>
|
||||
<% end %>
|
||||
</body>
|
||||
</html>
|
||||
7
app/views/dmsf_mailer/files_updated.text.plain.rhtml
Normal file
@ -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 %>
|
||||
42
app/views/settings/_dmsf_settings.erb
Normal file
@ -0,0 +1,42 @@
|
||||
<p>
|
||||
<%=content_tag(:label, "Maximum files download:") %>
|
||||
<%=text_field_tag "settings[dmsf_max_file_download]", @settings["dmsf_max_file_download"], :size=>10 %><br/>
|
||||
(<%=l(:label_default)%>: 0)
|
||||
<br/>
|
||||
Limits maximum number of files downloaded in zip or sent via email. "0" means unlimited.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%=content_tag(:label, "File storage directory:") %>
|
||||
<%=text_field_tag "settings[dmsf_storage_directory]", @settings["dmsf_storage_directory"], :size=>50 %><br/>
|
||||
(<%=l(:label_default)%>: <%="#{RAILS_ROOT}/files/dmsf"%>)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%=content_tag(:label, "Index database:") %>
|
||||
<%=text_field_tag 'settings[dmsf_index_database]', @settings['dmsf_index_database'], :size=>50 %><br/>
|
||||
(<%=l(:label_default)%>: <%="#{RAILS_ROOT}/files/dmsf_index"%>)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%=content_tag(:label, "Stemming Language:") %>
|
||||
<%=text_field_tag 'settings[dmsf_stemming_lang]', @settings['dmsf_stemming_lang'] %><br/>
|
||||
(<%=l(:label_default)%>: english )<br/>
|
||||
<br/>
|
||||
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)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%=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)<br>
|
||||
<%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_SOME', @settings['dmsf_stemming_strategy'] == 'STEM_SOME' %> Stem some<br>
|
||||
<%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_ALL', @settings['dmsf_stemming_strategy'] == 'STEM_ALL' %> Stem all <br>
|
||||
<br/>
|
||||
This controls how the query parser will apply the stemming algorithm. The default value is STEM_NONE. The possible values are:
|
||||
<br>
|
||||
* STEM_NONE: Don't perform any stemming.<br>
|
||||
* 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'.<br>
|
||||
* STEM_ALL: Search for stemmed forms of all words (note: no 'Z' prefix is added).<br>
|
||||
<br/>
|
||||
Note that the stemming algorithm is only applied to words in probabilistic fields - boolean filter terms are never stemmed.<br>
|
||||
</p>
|
||||
BIN
assets/images/approve.png
Normal file
|
After Width: | Height: | Size: 535 B |
BIN
assets/images/approved.png
Normal file
|
After Width: | Height: | Size: 262 B |
BIN
assets/images/askforapproval.png
Normal file
|
After Width: | Height: | Size: 641 B |
BIN
assets/images/delete.png
Normal file
|
After Width: | Height: | Size: 459 B |
BIN
assets/images/dmsf.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/images/filedetails.png
Normal file
|
After Width: | Height: | Size: 803 B |
BIN
assets/images/lock.png
Normal file
|
After Width: | Height: | Size: 797 B |
BIN
assets/images/locked.png
Normal file
|
After Width: | Height: | Size: 530 B |
BIN
assets/images/lockedbycurrent.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/images/notify.png
Normal file
|
After Width: | Height: | Size: 569 B |
BIN
assets/images/notifynot.png
Normal file
|
After Width: | Height: | Size: 578 B |
BIN
assets/images/operations.png
Normal file
|
After Width: | Height: | Size: 459 B |
BIN
assets/images/plupload/backgrounds.gif
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
assets/images/plupload/buttons-disabled.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/images/plupload/buttons.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/images/plupload/delete.gif
Normal file
|
After Width: | Height: | Size: 180 B |
BIN
assets/images/plupload/done.gif
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/images/plupload/error.gif
Normal file
|
After Width: | Height: | Size: 994 B |
BIN
assets/images/plupload/plupload-bw.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/images/plupload/plupload.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/images/plupload/throbber.gif
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/images/plupload/transp50.png
Normal file
|
After Width: | Height: | Size: 399 B |
BIN
assets/images/unlock.png
Normal file
|
After Width: | Height: | Size: 748 B |
BIN
assets/images/unlockdisabled.png
Normal file
|
After Width: | Height: | Size: 662 B |
BIN
assets/images/waitingforapproval.png
Normal file
|
After Width: | Height: | Size: 412 B |
BIN
assets/images/workflowdisabled.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
86
assets/javascripts/plupload/gears_init.js
Normal file
@ -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};
|
||||
}
|
||||
})();
|
||||
14
assets/javascripts/plupload/i18n/cs.js
Normal file
@ -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ů'
|
||||
});
|
||||
12
assets/javascripts/plupload/i18n/da.js
Normal file
@ -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.'
|
||||
});
|
||||
25
assets/javascripts/plupload/i18n/de.js
Normal file
@ -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.'
|
||||
});
|
||||
25
assets/javascripts/plupload/i18n/es.js
Normal file
@ -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.'
|
||||
});
|
||||
12
assets/javascripts/plupload/i18n/fr.js
Normal file
@ -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.'
|
||||
});
|
||||
21
assets/javascripts/plupload/i18n/it.js
Normal file
@ -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.'
|
||||
});
|
||||
21
assets/javascripts/plupload/i18n/nl.js
Normal file
@ -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.'
|
||||
});
|
||||
35
assets/javascripts/plupload/i18n/pt-br.js
Normal file
@ -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: '
|
||||
});
|
||||
21
assets/javascripts/plupload/i18n/ru.js
Normal file
@ -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.': 'Ошибка ввода-вывода.'
|
||||
});
|
||||
12
assets/javascripts/plupload/i18n/sv.js
Normal file
@ -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'
|
||||
});
|
||||
1
assets/javascripts/plupload/jquery.plupload.queue.min.js
vendored
Normal file
737
assets/javascripts/plupload/jquery.ui.plupload.js
vendored
Normal file
@ -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(
|
||||
'<div class="plupload_wrapper">' +
|
||||
'<div class="ui-widget-content plupload_container">' +
|
||||
'<div class="plupload">' +
|
||||
|
||||
'<div class="plupload_content">' +
|
||||
'<table class="plupload_filelist">' +
|
||||
'<tr class="ui-widget-header plupload_filelist_header">' +
|
||||
'<td class="plupload_cell plupload_file_name">' + _('Filename') + '</td>' +
|
||||
'<td class="plupload_cell plupload_file_status">' + _('Status') + '</td>' +
|
||||
'<td class="plupload_cell plupload_file_size">' + _('Size') + '</td>' +
|
||||
'<td class="plupload_cell plupload_file_action"> </td>' +
|
||||
'</tr>' +
|
||||
'</table>' +
|
||||
|
||||
'<div class="plupload_scroll">' +
|
||||
'<table class="plupload_filelist_content"></table>' +
|
||||
'</div>' +
|
||||
|
||||
'<table class="plupload_filelist">' +
|
||||
'<tr class="ui-widget-header ui-widget-content plupload_filelist_footer">' +
|
||||
'<td class="plupload_cell plupload_file_name">' +
|
||||
|
||||
'<div class="plupload_buttons"><!-- Visible -->' +
|
||||
'<a class="plupload_button plupload_add">' + _('Add Files') + '</a> ' +
|
||||
'<a class="plupload_button plupload_start">' + _('Start Upload') + '</a> ' +
|
||||
'<a class="plupload_button plupload_stop plupload_hidden">'+_('Stop Upload') + '</a> ' +
|
||||
'</div>' +
|
||||
|
||||
'<div class="plupload_started plupload_hidden"><!-- Hidden -->' +
|
||||
|
||||
'<div class="plupload_progress plupload_right">' +
|
||||
'<div class="plupload_progress_container"></div>' +
|
||||
'</div>' +
|
||||
|
||||
'<div class="plupload_cell plupload_upload_status"></div>' +
|
||||
|
||||
'<div class="plupload_clearer"> </div>' +
|
||||
|
||||
'</div>' +
|
||||
'</td>' +
|
||||
'<td class="plupload_file_status"><span class="plupload_total_status">0%</span></td>' +
|
||||
'<td class="plupload_file_size"><span class="plupload_total_file_size">0 kb</span></td>' +
|
||||
'<td class="plupload_file_action"></td>' +
|
||||
'</tr>' +
|
||||
'</table>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<input class="plupload_count" value="0" type="hidden">' +
|
||||
'</div>'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
$.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 = '<strong>' + err.message + '</strong>';
|
||||
details = err.details;
|
||||
|
||||
if (details) {
|
||||
message += " <br /><i>" + err.details + "</i>";
|
||||
} 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! <b>%s</b> 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 += " <br /><i>" + details + "</i>";
|
||||
}
|
||||
|
||||
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 += '<input type="hidden" name="' + id + '_tmpname" value="'+plupload.xmlEncode(file.target_name)+'" />';
|
||||
}
|
||||
fields += '<input type="hidden" name="' + id + '_name" value="'+plupload.xmlEncode(file.name)+'" />';
|
||||
fields += '<input type="hidden" name="' + id + '_status" value="' + (file.status === plupload.DONE ? 'done' : 'failed') + '" />';
|
||||
|
||||
count++;
|
||||
self.counter.val(count);
|
||||
}
|
||||
|
||||
filelist.append(
|
||||
'<tr class="ui-state-default plupload_file" id="' + file.id + '">' +
|
||||
'<td class="plupload_cell plupload_file_name"><span>' + file.name + '</span></td>' +
|
||||
'<td class="plupload_cell plupload_file_status">' + file.percent + '%</td>' +
|
||||
'<td class="plupload_cell plupload_file_size">' + plupload.formatSize(file.size) + '</td>' +
|
||||
'<td class="plupload_cell plupload_file_action"><div class="ui-icon"></div>' + fields + '</td>' +
|
||||
'</tr>'
|
||||
);
|
||||
|
||||
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('<tr><td class="plupload_droptext">' + _("Drag files here.") + '</td></tr>');
|
||||
} 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('<input class="plupload_file_rename" type="text" />');
|
||||
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('<tr><td class="plupload_droptext">' + _("Drag files here.") + '</td></tr>');
|
||||
|
||||
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 = $(
|
||||
'<div class="plupload_message">' +
|
||||
'<span class="plupload_message_close ui-icon ui-icon-circle-close" title="'+_('Close')+'"></span>' +
|
||||
'<p><span class="ui-icon"></span>' + message + '</p>' +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
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));
|
||||
1
assets/javascripts/plupload/plupload.browserplus.min.js
vendored
Normal file
@ -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;l<n.length;l++){k=n[l];o=a.guid();h[o]=k;j.push(new a.File(o,k.name,k.size))}if(l){g.trigger("FilesAdded",j)}}function b(){g.bind("PostInit",function(){var m,k=d.drop_element,o=g.id+"_droptarget",j=document.getElementById(k),l;function p(r,q){e.DragAndDrop.AddDropTarget({id:r},function(s){e.DragAndDrop.AttachCallbacks({id:r,hover:function(t){if(!t&&q){q()}},drop:function(t){if(q){q()}f(t)}},function(){})})}function n(){document.getElementById(o).style.top="-1000px"}if(j){if(document.attachEvent&&(/MSIE/gi).test(navigator.userAgent)){m=document.createElement("div");m.setAttribute("id",o);a.extend(m.style,{position:"absolute",top:"-1000px",background:"red",filter:"alpha(opacity=0)",opacity:0});document.body.appendChild(m);a.addEvent(j,"dragenter",function(r){var q,s;q=document.getElementById(k);s=a.getPos(q);a.extend(document.getElementById(o).style,{top:s.y+"px",left:s.x+"px",width:q.offsetWidth+"px",height:q.offsetHeight+"px"})});p(o,n)}else{p(k)}}a.addEvent(document.getElementById(d.browse_button),"click",function(v){var t=[],r,q,u=d.filters,s;v.preventDefault();for(r=0;r<u.length;r++){s=u[r].extensions.split(",");for(q=0;q<s.length;q++){t.push(a.mimeTypes[s[q]])}}e.FileBrowse.OpenBrowseDialog({mimeTypes:t},function(w){if(w.success){f(w.value)}})});j=m=null});g.bind("UploadFile",function(m,j){var l=h[j.id],r={},k=m.settings.chunk_size,n,o=[];function q(s,u){var t;if(j.status==a.FAILED){return}r.name=j.target_name||j.name;if(k){r.chunk=""+s;r.chunks=""+u}t=o.shift();e.Uploader.upload({url:m.settings.url,files:{file:t},cookies:document.cookies,postvars:a.extend(r,m.settings.multipart_params),progressCallback:function(x){var w,v=0;n[s]=parseInt(x.filePercent*t.size/100,10);for(w=0;w<n.length;w++){v+=n[w]}j.loaded=v;m.trigger("UploadProgress",j)}},function(w){var v,x;if(w.success){v=w.value.statusCode;if(k){m.trigger("ChunkUploaded",j,{chunk:s,chunks:u,response:w.value.body,status:v})}if(o.length>0){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<t;u++){n[u]=0;o.push(w[u])}q(0,t)}})}else{n=Array(1);o.push(s);q(0,1)}}if(c&&/\.(png|jpg|jpeg)$/i.test(j.name)){BrowserPlus.ImageAlter.transform({file:l,quality:c.quality||90,actions:[{scale:{maxwidth:c.width,maxheight:c.height}}]},function(s){if(s.success){p(s.value.file)}})}else{p(l)}});i({success:true})}if(e){e.init(function(k){var j=[{service:"Uploader",version:"3"},{service:"DragAndDrop",version:"1"},{service:"FileBrowse",version:"1"},{service:"FileAccess",version:"2"}];if(c){j.push({service:"ImageAlter",version:"4"})}if(k.success){e.require({services:j},function(l){if(l.success){b()}else{i()}})}else{i()}})}else{i()}}})})(plupload);
|
||||
1
assets/javascripts/plupload/plupload.flash.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
(function(f,b,d,e){var a={},g={};function c(){var h;try{h=navigator.plugins["Shockwave Flash"];h=h.description}catch(j){try{h=new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version")}catch(i){h="0.0"}}h=h.match(/\d+/g);return parseFloat(h[0]+"."+h[1])}d.flash={trigger:function(j,h,i){setTimeout(function(){var m=a[j],l,k;if(m){m.trigger("Flash:"+h,i)}},0)}};d.runtimes.Flash=d.addRuntime("flash",{getFeatures:function(){return{jpgresize:true,pngresize:true,maxWidth:8091,maxHeight:8091,chunks:true,progress:true,multipart:true}},init:function(j,o){var n,i,k,p=0,h=b.body;if(c()<10){o({success:false});return}g[j.id]=false;a[j.id]=j;n=b.getElementById(j.settings.browse_button);i=b.createElement("div");i.id=j.id+"_flash_container";d.extend(i.style,{position:"absolute",top:"0px",background:j.settings.shim_bgcolor||"transparent",zIndex:99999,width:"100%",height:"100%"});i.className="plupload flash";if(j.settings.container){h=b.getElementById(j.settings.container);h.style.position="relative"}h.appendChild(i);k="id="+escape(j.id);i.innerHTML='<object id="'+j.id+'_flash" width="100%" height="100%" style="outline:0" type="application/x-shockwave-flash" data="'+j.settings.flash_swf_url+'"><param name="movie" value="'+j.settings.flash_swf_url+'" /><param name="flashvars" value="'+k+'" /><param name="wmode" value="transparent" /><param name="allowscriptaccess" value="always" /></object>';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;u<w.length;u++){v=w[u];y=d.guid();s[y]=v.id;s[v.id]=y;x.push(new d.File(y,v.name,v.size))}if(x.length){j.trigger("FilesAdded",x)}});j.bind("Flash:SecurityError",function(t,u){j.trigger("Error",{code:d.SECURITY_ERROR,message:d.translate("Security error."),details:u.message,file:j.getFile(s[u.id])})});j.bind("Flash:GenericError",function(t,u){j.trigger("Error",{code:d.GENERIC_ERROR,message:d.translate("Generic error."),details:u.message,file:j.getFile(s[u.id])})});j.bind("Flash:IOError",function(t,u){j.trigger("Error",{code:d.IO_ERROR,message:d.translate("IO error."),details:u.message,file:j.getFile(s[u.id])})});j.bind("Flash:ImageError",function(t,u){j.trigger("Error",{code:parseInt(u.code),message:d.translate("Image error."),file:j.getFile(s[u.id])})});j.bind("Flash:StageEvent:rollOver",function(t){var u,v;u=b.getElementById(j.settings.browse_button);v=t.settings.browse_button_hover;if(u&&v){d.addClass(u,v)}});j.bind("Flash:StageEvent:rollOut",function(t){var u,v;u=b.getElementById(j.settings.browse_button);v=t.settings.browse_button_hover;if(u&&v){d.removeClass(u,v)}});j.bind("Flash:StageEvent:mouseDown",function(t){var u,v;u=b.getElementById(j.settings.browse_button);v=t.settings.browse_button_active;if(u&&v){d.addClass(u,v);d.addEvent(b.body,"mouseup",function(){d.removeClass(u,v)},t.id)}});j.bind("Flash:StageEvent:mouseUp",function(t){var u,v;u=b.getElementById(j.settings.browse_button);v=t.settings.browse_button_active;if(u&&v){d.removeClass(u,v)}});j.bind("QueueChanged",function(t){j.refresh()});j.bind("FilesRemoved",function(t,v){var u;for(u=0;u<v.length;u++){m().removeFile(s[v[u].id])}});j.bind("StateChanged",function(t){j.refresh()});j.bind("Refresh",function(t){var u,v,w;m().setFileFilters(j.settings.filters,j.settings.multi_selection);u=b.getElementById(t.settings.browse_button);if(u){v=d.getPos(u,b.getElementById(t.settings.container));w=d.getSize(u);d.extend(b.getElementById(t.id+"_flash_container").style,{top:v.y+"px",left:v.x+"px",width:w.w+"px",height:w.h+"px"})}});j.bind("Destroy",function(t){var u;d.removeAllEvents(b.body,t.id);delete g[t.id];delete a[t.id];u=b.getElementById(t.id+"_flash_container");if(u){h.removeChild(u)}});o({success:true})})}})})(window,document,plupload);
|
||||
BIN
assets/javascripts/plupload/plupload.flash.swf
Normal file
2
assets/javascripts/plupload/plupload.full.min.js
vendored
Normal file
1
assets/javascripts/plupload/plupload.gears.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
(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;l<n.length;l++){m=n[l];p=c.guid();f[p]=m.blob;o.push(new c.File(p,m.name,m.blob.length))}i.trigger("FilesAdded",o)}i.bind("PostInit",function(){var m=i.settings,l=b.getElementById(m.drop_element);if(l){c.addEvent(l,"dragover",function(n){j.setDropEffect(n,"copy");n.preventDefault()},i.id);c.addEvent(l,"drop",function(o){var n=j.getDragData(o,"application/x-gears-files");if(n){g(n.files)}o.preventDefault()},i.id);l=0}c.addEvent(b.getElementById(m.browse_button),"click",function(r){var q=[],o,n,p;r.preventDefault();for(o=0;o<m.filters.length;o++){p=m.filters[o].extensions.split(",");for(n=0;n<p.length;n++){q.push("."+p[n])}}j.openFiles(g,{singleFile:!m.multi_selection,filter:q})},i.id)});i.bind("UploadFile",function(r,o){var t=0,s,p,q=0,n=r.settings.resize,l;if(n&&/\.(png|jpg|jpeg)$/i.test(o.name)){f[o.id]=a(f[o.id],n.width,n.height,n.quality||90,/\.png$/i.test(o.name)?"image/png":"image/jpeg")}o.size=f[o.id].length;p=r.settings.chunk_size;l=p>0;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(t<s){x(f[o.id].slice(t*p,A))}}m()});i.bind("Destroy",function(l){var m,n,o={browseButton:l.settings.browse_button,dropElm:l.settings.drop_element};for(m in o){n=b.getElementById(o[m]);if(n){c.removeAllEvents(n,l.id)}}});k({success:true})}})})(window,document,plupload);
|
||||
1
assets/javascripts/plupload/plupload.html4.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
(function(d,a,b,c){function e(f){return a.getElementById(f)}b.runtimes.Html4=b.addRuntime("html4",{getFeatures:function(){return{multipart:true,canOpenDialog:navigator.userAgent.indexOf("WebKit")!==-1}},init:function(f,g){f.bind("Init",function(p){var j=a.body,n,h="javascript",k,x,q,z=[],r=/MSIE/.test(navigator.userAgent),t=[],m=p.settings.filters,o,l,s,w;for(o=0;o<m.length;o++){l=m[o].extensions.split(/,/);for(w=0;w<l.length;w++){s=b.mimeTypes[l[w]];if(s){t.push(s)}}}t=t.join(",");function v(){var B,y,i,A;q=b.guid();z.push(q);B=a.createElement("form");B.setAttribute("id","form_"+q);B.setAttribute("method","post");B.setAttribute("enctype","multipart/form-data");B.setAttribute("encoding","multipart/form-data");B.setAttribute("target",p.id+"_iframe");B.style.position="absolute";y=a.createElement("input");y.setAttribute("id","input_"+q);y.setAttribute("type","file");y.setAttribute("accept",t);y.setAttribute("size",1);A=e(p.settings.browse_button);if(p.features.canOpenDialog&&A){b.addEvent(e(p.settings.browse_button),"click",function(C){y.click();C.preventDefault()},p.id)}b.extend(y.style,{width:"100%",height:"100%",opacity:0,fontSize:"2em"});b.extend(B.style,{overflow:"hidden"});i=p.settings.shim_bgcolor;if(i){B.style.background=i}if(r){b.extend(y.style,{filter:"alpha(opacity=0)"})}b.addEvent(y,"change",function(F){var D=F.target,C,E=[],G;if(D.value){e("form_"+q).style.top=-1048575+"px";C=D.value.replace(/\\/g,"/");C=C.substring(C.length,C.lastIndexOf("/")+1);E.push(new b.File(q,C));if(!p.features.canOpenDialog){b.removeAllEvents(B,p.id)}else{b.removeEvent(A,"click",p.id)}b.removeEvent(y,"change",p.id);v();if(E.length){f.trigger("FilesAdded",E)}}},p.id);B.appendChild(y);j.appendChild(B);p.refresh()}function u(){var i=a.createElement("div");i.innerHTML='<iframe id="'+p.id+'_iframe" name="'+p.id+'_iframe" src="'+h+':""" style="display:none"></iframe>';n=i.firstChild;j.appendChild(n);b.addEvent(n,"load",function(C){var D=C.target,B,y;if(!k){return}try{B=D.contentWindow.document||D.contentDocument||d.frames[D.id].document}catch(A){p.trigger("Error",{code:b.SECURITY_ERROR,message:b.translate("Security error."),file:k});return}y=B.documentElement.innerText||B.documentElement.textContent;if(y){k.status=b.DONE;k.loaded=1025;k.percent=100;p.trigger("UploadProgress",k);p.trigger("FileUploaded",k,{response:y})}},p.id)}if(p.settings.container){j=e(p.settings.container);j.style.position="relative"}p.bind("UploadFile",function(i,A){var B,y;if(A.status==b.DONE||A.status==b.FAILED||i.state==b.STOPPED){return}B=e("form_"+A.id);y=e("input_"+A.id);y.setAttribute("name",i.settings.file_data_name);B.setAttribute("action",i.settings.url);b.each(b.extend({name:A.target_name||A.name},i.settings.multipart_params),function(E,C){var D=a.createElement("input");b.extend(D,{type:"hidden",name:C,value:E});B.insertBefore(D,B.firstChild)});k=A;e("form_"+q).style.top=-1048575+"px";B.submit();B.parentNode.removeChild(B)});p.bind("FileUploaded",function(i){i.refresh()});p.bind("StateChanged",function(i){if(i.state==b.STARTED){u()}if(i.state==b.STOPPED){d.setTimeout(function(){b.removeEvent(n,"load",i.id);n.parentNode.removeChild(n)},0)}});p.bind("Refresh",function(A){var F,B,C,D,i,G,H,E,y;F=e(A.settings.browse_button);if(F){i=b.getPos(F,e(A.settings.container));G=b.getSize(F);H=e("form_"+q);E=e("input_"+q);b.extend(H.style,{top:i.y+"px",left:i.x+"px",width:G.w+"px",height:G.h+"px"});if(A.features.canOpenDialog){y=parseInt(F.parentNode.style.zIndex,10);if(isNaN(y)){y=0}b.extend(F.style,{position:"relative",zIndex:y});b.extend(H.style,{zIndex:y-1})}C=A.settings.browse_button_hover;D=A.settings.browse_button_active;B=A.features.canOpenDialog?F:H;if(C){b.addEvent(B,"mouseover",function(){b.addClass(F,C)},A.id);b.addEvent(B,"mouseout",function(){b.removeClass(F,C)},A.id)}if(D){b.addEvent(B,"mousedown",function(){b.addClass(F,D)},A.id);b.addEvent(a.body,"mouseup",function(){b.removeClass(F,D)},A.id)}}});f.bind("FilesRemoved",function(y,B){var A,C;for(A=0;A<B.length;A++){C=e("form_"+B[A].id);if(C){C.parentNode.removeChild(C)}}});f.bind("Destroy",function(i){var y,A,B,C={inputContainer:"form_"+q,inputFile:"input_"+q,browseButton:i.settings.browse_button};for(y in C){A=e(C[y]);if(A){b.removeAllEvents(A,i.id)}}b.removeAllEvents(a.body,i.id);b.each(z,function(E,D){B=e("form_"+E);if(B){j.removeChild(B)}})});v()});g({success:true})}})})(window,document,plupload);
|
||||
1
assets/javascripts/plupload/plupload.html5.min.js
vendored
Normal file
2
assets/javascripts/plupload/plupload.min.js
vendored
Normal file
1
assets/javascripts/plupload/plupload.silverlight.min.js
vendored
Normal file
BIN
assets/javascripts/plupload/plupload.silverlight.xap
Normal file
119
assets/stylesheets/dmsf.css
Normal file
@ -0,0 +1,119 @@
|
||||
table.list tbody td, table.list tbody tr:hover td {
|
||||
border: solid 1px #D7D7D7;
|
||||
}
|
||||
|
||||
#sidebar h3 {
|
||||
border: none;
|
||||
}
|
||||
|
||||
button {
|
||||
vertical-align: middle;
|
||||
margin-top: 1px;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
form.dmfs_entries {
|
||||
margin-bottom: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
form.dmfs_entries div.controls {
|
||||
float: right;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
form.dmfs_entries div.controls input, form.dmfs_entries div.controls button {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
div.filename {
|
||||
padding: 0 10px 0 10px; float: right; font-size: 0.8em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
td.size {
|
||||
font-size: 0.8em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
td.modified {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
td.modified img {
|
||||
vertical-align:text-top;
|
||||
}
|
||||
|
||||
td.author {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
td.version {
|
||||
font-size: 0.8em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
td.version img {
|
||||
vertical-align:text-top;
|
||||
}
|
||||
|
||||
div.right_icon_box {
|
||||
float: right;
|
||||
white-space: nowrap;
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
.dmsf_upload textarea {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.dmsf_upload legend {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dmsf_upload .warning {
|
||||
padding: 4px 4px 4px 30px;
|
||||
}
|
||||
|
||||
.dmsf_detail textarea {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.dmsf_detail legend {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dmsf_detail .warning {
|
||||
padding: 4px 4px 4px 30px;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
padding-top: 4px;
|
||||
padding-right: 8px;
|
||||
padding-bottom: 8px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
#uploader div.flash {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.upload_select {
|
||||
float: right;
|
||||
white-space: nowrap;
|
||||
line-height: 1.4em;
|
||||
padding-left: 10px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
div.upload_select input, div.upload_select select {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
margin: 1px;
|
||||
}
|
||||
138
assets/stylesheets/plupload/jquery.ui.plupload.css
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
Plupload
|
||||
------------------------------------------------------------------- */
|
||||
|
||||
.plupload_button {cursor: pointer;}
|
||||
|
||||
.plupload_wrapper {
|
||||
font: normal 11px Verdana,sans-serif;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.plupload .plupload_container input {width: 98%;}
|
||||
.plupload .plupload_filelist_footer {border-width: 1px 0 0 0}
|
||||
.plupload .plupload_filelist_header {border-width: 0 0 1px 0}
|
||||
div.plupload .plupload_file {border-width: 0 0 1px 0}
|
||||
div.plupload div.plupload_header {border-width: 0 0 1px 0; position: relative;}
|
||||
|
||||
.plupload_file .ui-icon {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.plupload_header_content {
|
||||
background-image: url('../../images/plupload/plupload-bw.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: 8px center;
|
||||
min-height: 56px;
|
||||
padding-left: 60px;
|
||||
position:relative;
|
||||
}
|
||||
.plupload_header_content_bw {background-image: url('../../images/plupload/plupload-bw.png');}
|
||||
.plupload_header_title {
|
||||
font: normal 18px sans-serif;
|
||||
padding: 6px 0 3px;
|
||||
}
|
||||
.plupload_header_text {font: normal 12px sans-serif;}
|
||||
|
||||
.plupload_filelist,
|
||||
.plupload_filelist_content {
|
||||
border-collapse: collapse;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.plupload_cell {padding: 8px 6px;}
|
||||
|
||||
.plupload_file {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.plupload_scroll {
|
||||
min-height: 168px;
|
||||
_height: 168px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.plupload_file_size, .plupload_file_status {text-align: right;}
|
||||
.plupload_file_size, .plupload_file_status {width: 52px;}
|
||||
.plupload_file_action {width: 16px;}
|
||||
.plupload_file_name {
|
||||
overflow: hidden;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.plupload_file_rename {
|
||||
width:95%;
|
||||
}
|
||||
|
||||
.plupload_progress {width: 60px;}
|
||||
.plupload_progress_container {padding: 1px;}
|
||||
|
||||
|
||||
/* Floats */
|
||||
|
||||
.plupload_right {float: right;}
|
||||
.plupload_left {float: left;}
|
||||
.plupload_clear,.plupload_clearer {clear: both;}
|
||||
.plupload_clearer, .plupload_progress_bar {
|
||||
display: block;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
}
|
||||
.plupload_clearer {height: 0;}
|
||||
|
||||
/* Misc */
|
||||
.plupload_hidden {display: none;}
|
||||
.plupload_droptext {
|
||||
background: transparent;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
border: 0;
|
||||
line-height: 165px;
|
||||
}
|
||||
|
||||
.plupload_buttons, .plupload_upload_status {float: left}
|
||||
|
||||
.plupload_message {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.plupload_message p {
|
||||
padding:0.7em;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
.plupload_message strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
plupload_message i {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.plupload_message p span.ui-icon {
|
||||
float: left;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
.plupload_header_content .ui-state-error,
|
||||
.plupload_header_content .ui-state-highlight {
|
||||
border:none;
|
||||
}
|
||||
|
||||
.plupload_message_close {
|
||||
position:absolute;
|
||||
top:5px;
|
||||
right:5px;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.plupload .ui-sortable-placeholder {
|
||||
height:35px;
|
||||
}
|
||||
177
assets/stylesheets/plupload/plupload.queue.css
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
Plupload
|
||||
------------------------------------------------------------------- */
|
||||
|
||||
.plupload_button {
|
||||
display: -moz-inline-box; /* FF < 3*/
|
||||
display: inline-block;
|
||||
font: normal 12px sans-serif;
|
||||
text-decoration: none;
|
||||
color: #42454a;
|
||||
border: 1px solid #bababa;
|
||||
padding: 2px 8px 3px 20px;
|
||||
margin-right: 4px;
|
||||
background: #f3f3f3 url('../img/buttons.png') no-repeat 0 center;
|
||||
outline: 0;
|
||||
|
||||
/* Optional rounded corners for browsers that support it */
|
||||
-moz-border-radius: 3px;
|
||||
-khtml-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.plupload_button:hover {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.plupload_disabled, a.plupload_disabled:hover {
|
||||
color: #737373;
|
||||
border-color: #c5c5c5;
|
||||
background: #ededed url('../img/buttons-disabled.png') no-repeat 0 center;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.plupload_add {
|
||||
background-position: -181px center;
|
||||
}
|
||||
|
||||
.plupload_wrapper {
|
||||
font: normal 11px Verdana,sans-serif;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.plupload_container {
|
||||
padding: 8px;
|
||||
background: url('../img/transp50.png');
|
||||
/*-moz-border-radius: 5px;*/
|
||||
}
|
||||
|
||||
.plupload_container input {
|
||||
border: 1px solid #DDD;
|
||||
font: normal 11px Verdana,sans-serif;
|
||||
width: 98%;
|
||||
}
|
||||
|
||||
.plupload_header {background: #2A2C2E url('../img/backgrounds.gif') repeat-x;}
|
||||
.plupload_header_content {
|
||||
background: url('../img/backgrounds.gif') no-repeat 0 -317px;
|
||||
min-height: 56px;
|
||||
padding-left: 60px;
|
||||
color: #FFF;
|
||||
}
|
||||
.plupload_header_title {
|
||||
font: normal 18px sans-serif;
|
||||
padding: 6px 0 3px;
|
||||
}
|
||||
.plupload_header_text {
|
||||
font: normal 12px sans-serif;
|
||||
}
|
||||
|
||||
.plupload_filelist {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.plupload_scroll .plupload_filelist {
|
||||
height: 185px;
|
||||
background: #F5F5F5;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.plupload_filelist li {
|
||||
padding: 10px 8px;
|
||||
background: #F5F5F5 url('../img/backgrounds.gif') repeat-x 0 -156px;
|
||||
border-bottom: 1px solid #DDD;
|
||||
}
|
||||
|
||||
.plupload_filelist_header, .plupload_filelist_footer {
|
||||
background: #DFDFDF;
|
||||
padding: 8px 8px;
|
||||
color: #42454A;
|
||||
}
|
||||
.plupload_filelist_header {
|
||||
border-top: 1px solid #EEE;
|
||||
border-bottom: 1px solid #CDCDCD;
|
||||
}
|
||||
|
||||
.plupload_filelist_footer {border-top: 1px solid #FFF; height: 22px; line-height: 20px; vertical-align: middle;}
|
||||
.plupload_file_name {float: left; overflow: hidden}
|
||||
.plupload_file_status {color: #777;}
|
||||
.plupload_file_status span {color: #42454A;}
|
||||
.plupload_file_size, .plupload_file_status, .plupload_progress {
|
||||
float: right;
|
||||
width: 80px;
|
||||
}
|
||||
.plupload_file_size, .plupload_file_status, .plupload_file_action {text-align: right;}
|
||||
|
||||
.plupload_filelist .plupload_file_name {width: 205px}
|
||||
|
||||
.plupload_file_action {
|
||||
float: right;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.plupload_file_action * {
|
||||
display: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
li.plupload_uploading {background: #ECF3DC url('../img/backgrounds.gif') repeat-x 0 -238px;}
|
||||
li.plupload_done {color:#AAA}
|
||||
|
||||
li.plupload_delete a {
|
||||
background: url('../img/delete.gif');
|
||||
}
|
||||
|
||||
li.plupload_failed a {
|
||||
background: url('../img/error.gif');
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
li.plupload_done a {
|
||||
background: url('../img/done.gif');
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.plupload_progress, .plupload_upload_status {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.plupload_progress_container {
|
||||
margin-top: 3px;
|
||||
border: 1px solid #CCC;
|
||||
background: #FFF;
|
||||
padding: 1px;
|
||||
}
|
||||
.plupload_progress_bar {
|
||||
width: 0px;
|
||||
height: 7px;
|
||||
background: #CDEB8B;
|
||||
}
|
||||
|
||||
.plupload_scroll .plupload_filelist_header .plupload_file_action, .plupload_scroll .plupload_filelist_footer .plupload_file_action {
|
||||
margin-right: 17px;
|
||||
}
|
||||
|
||||
/* Floats */
|
||||
|
||||
.plupload_clear,.plupload_clearer {clear: both;}
|
||||
.plupload_clearer, .plupload_progress_bar {
|
||||
display: block;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
li.plupload_droptext {
|
||||
background: transparent;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
border: 0;
|
||||
line-height: 165px;
|
||||
}
|
||||
15
config/locales/cs.yml
Normal file
@ -0,0 +1,15 @@
|
||||
cs:
|
||||
:dmsf: "DMSF"
|
||||
:label_dmsf_file_plural: "Dmsf soubory"
|
||||
:warning_no_entries_selected: "No entries selected"
|
||||
:error_email_to_must_be_entered: "Email To must be entered"
|
||||
:notice_email_sent: "Email sent"
|
||||
:warning_file_already_locked: "File already locked"
|
||||
:notice_file_locked: "File locked"
|
||||
:warning_file_not_locked: "File not locked"
|
||||
:notice_file_unlocked: "File unlocked"
|
||||
:error_only_user_that_locked_file_can_unlock_it: "Only user that locked file can unlock it"
|
||||
:question_do_you_really_want_to_delete_this_entry: "Do you really want to delete this entry?"
|
||||
:error_max_files_exceeded: "Limit for number of simultaneously downloadable files exceeded: "
|
||||
:question_do_you_really_want_to_delete_this_revision: "Do you really want to delete this revision?"
|
||||
|
||||
16
config/locales/de.yml
Normal file
@ -0,0 +1,16 @@
|
||||
# English strings go here for Rails i18n
|
||||
en:
|
||||
:dmsf: "DMSF"
|
||||
:label_dmsf_file_plural: "Dmsf files"
|
||||
:warning_no_entries_selected: "No entries selected"
|
||||
:error_email_to_must_be_entered: "Email To must be entered"
|
||||
:notice_email_sent: "Email sent"
|
||||
:warning_file_already_locked: "File already locked"
|
||||
:notice_file_locked: "File locked"
|
||||
:warning_file_not_locked: "File not locked"
|
||||
:notice_file_unlocked: "File unlocked"
|
||||
:error_only_user_that_locked_file_can_unlock_it: "Only user that locked file can unlock it"
|
||||
:question_do_you_really_want_to_delete_this_entry: "Do you really want to delete this entry?"
|
||||
:error_max_files_exceeded: "Limit for number of simultaneously downloadable files exceeded: "
|
||||
:question_do_you_really_want_to_delete_this_revision: "Do you really want to delete this revision?"
|
||||
|
||||
16
config/locales/en-GB.yml
Normal file
@ -0,0 +1,16 @@
|
||||
# English strings go here for Rails i18n
|
||||
en:
|
||||
:dmsf: "DMSF"
|
||||
:label_dmsf_file_plural: "Dmsf files"
|
||||
:warning_no_entries_selected: "No entries selected"
|
||||
:error_email_to_must_be_entered: "Email To must be entered"
|
||||
:notice_email_sent: "Email sent"
|
||||
:warning_file_already_locked: "File already locked"
|
||||
:notice_file_locked: "File locked"
|
||||
:warning_file_not_locked: "File not locked"
|
||||
:notice_file_unlocked: "File unlocked"
|
||||
:error_only_user_that_locked_file_can_unlock_it: "Only user that locked file can unlock it"
|
||||
:question_do_you_really_want_to_delete_this_entry: "Do you really want to delete this entry?"
|
||||
:error_max_files_exceeded: "Limit for number of simultaneously downloadable files exceeded: "
|
||||
:question_do_you_really_want_to_delete_this_revision: "Do you really want to delete this revision?"
|
||||
|
||||
16
config/locales/en.yml
Normal file
@ -0,0 +1,16 @@
|
||||
# English strings go here for Rails i18n
|
||||
en:
|
||||
:dmsf: "DMSF"
|
||||
:label_dmsf_file_plural: "Dmsf files"
|
||||
:warning_no_entries_selected: "No entries selected"
|
||||
:error_email_to_must_be_entered: "Email To must be entered"
|
||||
:notice_email_sent: "Email sent"
|
||||
:warning_file_already_locked: "File already locked"
|
||||
:notice_file_locked: "File locked"
|
||||
:warning_file_not_locked: "File not locked"
|
||||
:notice_file_unlocked: "File unlocked"
|
||||
:error_only_user_that_locked_file_can_unlock_it: "Only user that locked file can unlock it"
|
||||
:question_do_you_really_want_to_delete_this_entry: "Do you really want to delete this entry?"
|
||||
:error_max_files_exceeded: "Limit for number of simultaneously downloadable files exceeded: "
|
||||
:question_do_you_really_want_to_delete_this_revision: "Do you really want to delete this revision?"
|
||||
|
||||
16
config/locales/es.yml
Normal file
@ -0,0 +1,16 @@
|
||||
# English strings go here for Rails i18n
|
||||
en:
|
||||
:dmsf: "DMSF"
|
||||
:label_dmsf_file_plural: "Dmsf files"
|
||||
:warning_no_entries_selected: "No entries selected"
|
||||
:error_email_to_must_be_entered: "Email To must be entered"
|
||||
:notice_email_sent: "Email sent"
|
||||
:warning_file_already_locked: "File already locked"
|
||||
:notice_file_locked: "File locked"
|
||||
:warning_file_not_locked: "File not locked"
|
||||
:notice_file_unlocked: "File unlocked"
|
||||
:error_only_user_that_locked_file_can_unlock_it: "Only user that locked file can unlock it"
|
||||
:question_do_you_really_want_to_delete_this_entry: "Do you really want to delete this entry?"
|
||||
:error_max_files_exceeded: "Limit for number of simultaneously downloadable files exceeded: "
|
||||
:question_do_you_really_want_to_delete_this_revision: "Do you really want to delete this revision?"
|
||||
|
||||
16
config/locales/fr.yml
Normal file
@ -0,0 +1,16 @@
|
||||
# English strings go here for Rails i18n
|
||||
en:
|
||||
:dmsf: "DMSF"
|
||||
:label_dmsf_file_plural: "Dmsf files"
|
||||
:warning_no_entries_selected: "No entries selected"
|
||||
:error_email_to_must_be_entered: "Email To must be entered"
|
||||
:notice_email_sent: "Email sent"
|
||||
:warning_file_already_locked: "File already locked"
|
||||
:notice_file_locked: "File locked"
|
||||
:warning_file_not_locked: "File not locked"
|
||||
:notice_file_unlocked: "File unlocked"
|
||||
:error_only_user_that_locked_file_can_unlock_it: "Only user that locked file can unlock it"
|
||||
:question_do_you_really_want_to_delete_this_entry: "Do you really want to delete this entry?"
|
||||
:error_max_files_exceeded: "Limit for number of simultaneously downloadable files exceeded: "
|
||||
:question_do_you_really_want_to_delete_this_revision: "Do you really want to delete this revision?"
|
||||
|
||||
16
config/locales/ru.yml
Normal file
@ -0,0 +1,16 @@
|
||||
# English strings go here for Rails i18n
|
||||
en:
|
||||
:dmsf: "DMSF"
|
||||
:label_dmsf_file_plural: "Dmsf files"
|
||||
:warning_no_entries_selected: "No entries selected"
|
||||
:error_email_to_must_be_entered: "Email To must be entered"
|
||||
:notice_email_sent: "Email sent"
|
||||
:warning_file_already_locked: "File already locked"
|
||||
:notice_file_locked: "File locked"
|
||||
:warning_file_not_locked: "File not locked"
|
||||
:notice_file_unlocked: "File unlocked"
|
||||
:error_only_user_that_locked_file_can_unlock_it: "Only user that locked file can unlock it"
|
||||
:question_do_you_really_want_to_delete_this_entry: "Do you really want to delete this entry?"
|
||||
:error_max_files_exceeded: "Limit for number of simultaneously downloadable files exceeded: "
|
||||
:question_do_you_really_want_to_delete_this_revision: "Do you really want to delete this revision?"
|
||||
|
||||
78
db/migrate/01_create_hierarchy.rb
Normal file
@ -0,0 +1,78 @@
|
||||
class CreateHierarchy < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :dmsf_folders do |t|
|
||||
t.string :name, :null => false
|
||||
t.text :description
|
||||
t.references :project, :null => false
|
||||
t.references :dmsf_folder
|
||||
|
||||
t.boolean :notification, :default => false, :null => false
|
||||
|
||||
t.references :user, :null => false
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
create_table :dmsf_files do |t|
|
||||
t.string :name, :null => false
|
||||
t.references :project, :null => false
|
||||
t.references :dmsf_folder
|
||||
|
||||
t.boolean :notification, :default => false, :null => false
|
||||
|
||||
t.boolean :deleted, :default => false, :null => false
|
||||
t.integer :deleted_by_user_id
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
create_table :dmsf_file_revisions do |t|
|
||||
t.references :dmsf_file, :null => false
|
||||
t.string :disk_filename, :null => false
|
||||
|
||||
t.string :name, :null => false
|
||||
t.references :dmsf_folder
|
||||
|
||||
t.integer :size
|
||||
t.string :mime_type
|
||||
t.string :title
|
||||
t.text :description
|
||||
t.references :user, :null => false
|
||||
|
||||
t.integer :workflow
|
||||
|
||||
t.text :comment
|
||||
t.integer :major_version, :null => false
|
||||
t.integer :minor_version, :null => false
|
||||
|
||||
t.integer :source_dmsf_file_revision_id
|
||||
|
||||
t.boolean :deleted, :default => false, :null => false
|
||||
t.integer :deleted_by_user_id
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
create_table :dmsf_file_locks do |t|
|
||||
t.references :dmsf_file, :null => false
|
||||
t.boolean :locked, :default => false, :null => false
|
||||
t.references :user, :null => false
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
create_table :dmsf_user_prefs do |t|
|
||||
t.references :project, :null => false
|
||||
t.references :user, :null => false
|
||||
|
||||
t.boolean :email_notify
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :dmsf_file_revisions
|
||||
drop_table :dmsf_files
|
||||
drop_table :dmsf_folders
|
||||
end
|
||||
end
|
||||
60
init.rb
Normal file
@ -0,0 +1,60 @@
|
||||
require 'redmine'
|
||||
|
||||
Redmine::Plugin.register :redmine_dmsf do
|
||||
name "DMSF"
|
||||
author "Vít Jonáš"
|
||||
description "Document Management System Features for Redmine"
|
||||
version "0.5.0"
|
||||
url "https://code.google.com/p/redmine-dmsf/"
|
||||
author_url "mailto:vit.jonas@kontron-czech.com"
|
||||
|
||||
settings :partial => 'settings/dmsf_settings',
|
||||
:default => {
|
||||
"dmsf_max_file_download" => "0",
|
||||
"dmsf_storage_directory" => "#{RAILS_ROOT}/files/dmsf",
|
||||
"dmsf_index_database" => "#{RAILS_ROOT}/files/dmsf_index",
|
||||
"dmsf_stemming_lang" => "english",
|
||||
"dmsf_stemming_strategy" => "STEM_NONE"
|
||||
}
|
||||
|
||||
menu :project_menu, :dmsf, { :controller => 'dmsf', :action => 'index' }, :caption => 'DMSF', :after => :activity, :param => :id
|
||||
#delete_menu_item :project_menu, :documents
|
||||
|
||||
activity_provider :dmsf_files, :class_name => "DmsfFileRevision", :default => true
|
||||
|
||||
project_module :dmsf do
|
||||
permission :browse_documents, {:dmsf => [:index]}
|
||||
permission :user_preferences, {:dmsf_state => [:user_pref]}
|
||||
permission :view_dmsf_files, {:dmsf => [:download_file, :download_revision, :entries_operation, :email_entries_send],
|
||||
:dmsf_detail => [:file_detail]}
|
||||
permission :folder_manipulation, {:dmsf_detail => [:create_folder, :delete_folder, :folder_detail, :save_folder]}
|
||||
permission :file_manipulation, {:dmsf_detail => [:upload_files, :upload_file, :commit_files, :save_file, :delete_file],
|
||||
:dmsf_state => [:lock_file, :unlock_file]}
|
||||
permission :file_approval, {:dmsf_detail => [:approve_file, :delete_revision],
|
||||
:dmsf_state => [:file_notify_activate, :file_notify_deactivate, :folder_notify_activate, :folder_notify_deactivate]}
|
||||
permission :force_file_unlock, {:dmsf_state => [:force_file_unlock]}
|
||||
end
|
||||
|
||||
Redmine::WikiFormatting::Macros.register do
|
||||
desc "Wiki link to DMSF content:\n\n" +
|
||||
" File: !{{dmsf(file_id)}}\n\n" +
|
||||
"_file_id_ can be found in link for file download"
|
||||
|
||||
macro :dmsf do |obj, args|
|
||||
return nil if args.length < 1 # require file id
|
||||
return nil if @project == nil
|
||||
entry_id = args[0].strip
|
||||
file = DmsfFile.find(entry_id)
|
||||
unless file.nil? || file.deleted
|
||||
return nil if file.project != @project
|
||||
return link_to "#{file.last_revision.title}", :controller => "dmsf", :action => "download_file", :id => @project, :file_id => file
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Redmine::Search.map do |search|
|
||||
search.register :dmsf_files
|
||||
end
|
||||
127
lib/tasks/dmsf_convert_documents.rake
Normal file
@ -0,0 +1,127 @@
|
||||
desc <<-END_DESC
|
||||
Convert project documents to DMSF folder/file structure.
|
||||
|
||||
Converted project must have documents and may not have DMSF active!
|
||||
|
||||
Available options:
|
||||
* project => id or identifier of project (defaults to all projects)
|
||||
|
||||
Example:
|
||||
rake redmine:dmsf_convert_documents project=test RAILS_ENV="production"
|
||||
END_DESC
|
||||
require File.expand_path(File.dirname(__FILE__) + "/../../../../../config/environment")
|
||||
|
||||
class DmsfConvertDocuments
|
||||
|
||||
def self.convert(options={})
|
||||
projects = options[:project] ? [Project.find(options[:project])] : Project.find(:all)
|
||||
|
||||
projects.reject! {|project| !project.enabled_modules.index{|mod| mod.name == "dmsf"}.nil? }
|
||||
projects.reject! {|project| project.enabled_modules.index{|mod| mod.name == "documents"}.nil? }
|
||||
|
||||
unless projects.nil? || projects.empty?
|
||||
projects.each do |project|
|
||||
puts "Processing project: " + project.identifier
|
||||
|
||||
project.enabled_module_names = (project.enabled_module_names << "dmsf").uniq
|
||||
project.save!
|
||||
|
||||
folders = []
|
||||
project.documents.each do |document|
|
||||
folder = DmsfFolder.new
|
||||
|
||||
folder.project = project
|
||||
folder.user = (a = document.attachments.find(:first, :order => "created_on ASC")) ? a.author : User.find(:first)
|
||||
|
||||
folder.name = document.title
|
||||
|
||||
i = 1
|
||||
suffix = ""
|
||||
while folders.index{|f| f.name == (folder.name + suffix)}
|
||||
i+=1
|
||||
suffix = "_#{i}"
|
||||
end
|
||||
|
||||
folder.name = folder.name + suffix
|
||||
|
||||
folder.description = document.description
|
||||
|
||||
folder.save!
|
||||
folders << folder;
|
||||
|
||||
puts "Created folder: " + folder.name
|
||||
|
||||
files = []
|
||||
document.attachments.each do |attachment|
|
||||
begin
|
||||
file = DmsfFile.new
|
||||
file.project = project
|
||||
file.folder = folder
|
||||
|
||||
file.name = attachment.filename
|
||||
i = 1
|
||||
suffix = ""
|
||||
while files.index{|f| f.name == (DmsfFileRevision.remove_extension(file.name) + suffix + File.extname(file.name))}
|
||||
i+=1
|
||||
suffix = "_#{i}"
|
||||
end
|
||||
|
||||
file.name = DmsfFileRevision.remove_extension(file.name) + suffix + File.extname(file.name)
|
||||
|
||||
revision = DmsfFileRevision.new
|
||||
revision.file = file
|
||||
revision.name = file.name
|
||||
revision.folder = file.folder
|
||||
revision.title = DmsfFileRevision.filename_to_title(attachment.filename)
|
||||
revision.description = attachment.description
|
||||
revision.user = attachment.author
|
||||
revision.created_at = attachment.created_on
|
||||
revision.updated_at = attachment.created_on
|
||||
revision.major_version = 0
|
||||
revision.minor_version = 1
|
||||
revision.comment = "Converted from documents"
|
||||
revision.mime_type = attachment.content_type
|
||||
|
||||
revision.disk_filename = file.new_storage_filename
|
||||
attachment_file = File.open(attachment.diskfile, "rb")
|
||||
File.open(revision.disk_file, "wb") do |f|
|
||||
while (buffer = attachment_file.read(8192))
|
||||
f.write(buffer)
|
||||
end
|
||||
end
|
||||
attachment_file.close
|
||||
|
||||
revision.size = File.size(revision.disk_file)
|
||||
|
||||
file.save!
|
||||
revision.save!
|
||||
|
||||
files << file
|
||||
|
||||
attachment.destroy
|
||||
|
||||
puts "Created file: " + file.name
|
||||
rescue Exception => e
|
||||
puts "Creating file: " + attachment.filename + " failed"
|
||||
end
|
||||
end
|
||||
|
||||
document.destroy
|
||||
|
||||
end
|
||||
project.enabled_module_names = project.enabled_module_names.reject {|mod| mod == "documents"}
|
||||
project.save!
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
namespace :redmine do
|
||||
task :dmsf_convert_documents => :environment do
|
||||
options = {}
|
||||
options[:project] = ENV['project'] if ENV['project']
|
||||
|
||||
DmsfConvertDocuments.convert(options)
|
||||
end
|
||||
end
|
||||
24
test/fixtures/dmsf_folders.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
|
||||
# It is possible to use ERb template markup
|
||||
one:
|
||||
id: 1
|
||||
|
||||
name: folder1
|
||||
|
||||
project_id: 1
|
||||
|
||||
dmsf_folder_id:
|
||||
|
||||
user_id: 1
|
||||
|
||||
two:
|
||||
id: 2
|
||||
|
||||
name: folder2
|
||||
|
||||
project_id: 1
|
||||
|
||||
dmsf_folder_id: 1
|
||||
|
||||
user_id: 1
|
||||
|
||||
8
test/functional/dmsf_controller_test.rb
Normal file
@ -0,0 +1,8 @@
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class DmsfControllerTest < ActionController::TestCase
|
||||
# Replace this with your real tests.
|
||||
def test_truth
|
||||
assert true
|
||||
end
|
||||
end
|
||||
5
test/test_helper.rb
Normal file
@ -0,0 +1,5 @@
|
||||
# Load the normal Rails helper
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../../../../test/test_helper')
|
||||
|
||||
# Ensure that we are using the temporary fixture path
|
||||
Engines::Testing.set_fixture_path
|
||||
11
test/unit/dmsf_folder_test.rb
Normal file
@ -0,0 +1,11 @@
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class DmsfFolderTest < ActiveSupport::TestCase
|
||||
fixtures :projects, :users, :dmsf_folders, :dmsf_files, :dmsf_file_revisions,
|
||||
:roles, :members, :member_roles, :enabled_modules, :enumerations
|
||||
|
||||
def test_folder_creating
|
||||
assert_not_nil(dmsf_folders(:one))
|
||||
end
|
||||
|
||||
end
|
||||