* worinkg on Issue #19: DMSF document and folder can have custom fields

git-svn-id: http://redmine-dmsf.googlecode.com/svn/trunk/redmine_dmsf@280 5e329b0b-a2ee-ea63-e329-299493fc886d
This commit is contained in:
mavimo@gmail.com 2012-03-31 17:56:49 +00:00
parent 4cd8f7f958
commit d2cb53a027
22 changed files with 275 additions and 27 deletions

View File

@ -27,6 +27,8 @@ class DmsfController < ApplicationController
verify :method => :post, :only => [:delete_entries, :create, :save, :delete, :save_root, :notify_activate, :notify_deactivate],
:render => { :nothing => true, :status => :method_not_allowed }
helper :all
def show
if @folder.nil?
@subfolders = DmsfFolder.project_root_folders(@project)

View File

@ -28,6 +28,8 @@ class DmsfFilesController < ApplicationController
verify :method => :post, :only => [:create_revision, :delete_revision, :delete, :lock, :unlock, :notify_activate, :notify_deactivate],
:render => { :nothing => true, :status => :method_not_allowed }
helper :all
def show
# download is put here to provide more clear and usable links
if params.has_key?(:download)

View File

@ -26,6 +26,8 @@ class DmsfFilesCopyController < ApplicationController
verify :method => :post, :only => [:create, :move], :render => { :nothing => true, :status => :method_not_allowed }
helper :all
def new
@target_project = DmsfFile.allowed_target_projects_on_copy.detect {|p| p.id.to_s == params[:target_project_id]} if params[:target_project_id]
@target_project ||= @project if User.current.allowed_to?(:file_manipulation, @project)

View File

@ -26,6 +26,8 @@ class DmsfStateController < ApplicationController
verify :method => :post, :only => [:user_pref_save], :render => { :nothing => true, :status => :method_not_allowed }
helper :all
def user_pref_save
member = @project.members.find(:first, :conditions => {:user_id => User.current.id})
if member

View File

@ -28,6 +28,8 @@ class DmsfUploadController < ApplicationController
verify :method => :post, :only => [:upload_files, :upload_file, :commit_files],
:render => { :nothing => true, :status => :method_not_allowed }
helper :all
def upload_files
uploaded_files = params[:uploaded_files]
@uploads = []
@ -141,6 +143,14 @@ class DmsfUploadController < ApplicationController
File.delete(commited_disk_filepath)
files.push(file)
unless commited_file["dmsf_file_revision"].blank?
commited_file["dmsf_file_revision"]["custom_field_values"].each do |v|
cv = CustomValue.find(:first, :conditions => ["customized_id = " + new_revision.id.to_s + " AND custom_field_id = " + v[0]])
cv.value = v[1]
cv.save
end
end
else
failed_uploads.push(commited_file)
end

View File

@ -90,6 +90,9 @@ class DmsfFile < ActiveRecord::Base
return false
end
if Setting.plugin_redmine_dmsf["dmsf_really_delete_files"]
CustomValue.find(:all, :conditions => "customized_id = " + self.id.to_s).each do |v|
v.destroy
end
self.revisions.each {|r| r.delete(true)}
self.destroy
else
@ -187,6 +190,21 @@ class DmsfFile < ActiveRecord::Base
projects
end
# Overrides Redmine::Acts::Customizable::InstanceMethods#available_custom_fields
def available_custom_fields
search_project = nil
if self.project.present?
search_project = self.project
elsif self.project_id.present?
search_project = Project.find(self.project_id)
end
if search_project
search_project.all_dmsf_custom_fields
else
DmsfFileRevisionCustomField.all
end
end
def move_to(project, folder)
if self.locked_for_user?
errors.add_to_base(l(:error_file_is_locked))
@ -199,6 +217,18 @@ class DmsfFile < ActiveRecord::Base
new_revision.project = folder ? folder.project : project
new_revision.comment = l(:comment_moved_from, :source => "#{self.project.identifier}:#{self.dmsf_path_str}")
new_revision.custom_values = []
temp_custom_values = self.last_revision.custom_values.select{|cv| new_revision.project.all_dmsf_custom_fields.include?(cv.custom_field)}.map(&:clone)
new_revision.custom_values = temp_custom_values
#add default value for CFs not existing
present_custom_fields = new_revision.custom_values.collect(&:custom_field).uniq
Project.find(new_revision.project_id).all_dmsf_custom_fields.each do |cf|
unless present_custom_fields.include?(cf)
new_revision.custom_values << CustomValue.new({:custom_field => cf, :value => cf.default_value})
end
end
self.folder = new_revision.folder
self.project = new_revision.project
@ -212,14 +242,30 @@ class DmsfFile < ActiveRecord::Base
file.name = self.name
file.notification = !Setting.plugin_redmine_dmsf["dmsf_default_notifications"].blank?
new_revision = self.last_revision.clone
if file.save
new_revision = self.last_revision.clone
new_revision.file = file
new_revision.folder = folder ? folder : nil
new_revision.project = folder ? folder.project : project
new_revision.comment = l(:comment_copied_from, :source => "#{self.project.identifier}:#{self.dmsf_path_str}")
new_revision.file = file
new_revision.folder = folder ? folder : nil
new_revision.project = folder ? folder.project : project
new_revision.comment = l(:comment_copied_from, :source => "#{self.project.identifier}: #{self.dmsf_path_str}")
new_revision.save if file.save
new_revision.custom_values = []
temp_custom_values = self.last_revision.custom_values.select{|cv| new_revision.project.all_dmsf_custom_fields.include?(cv.custom_field)}.map(&:clone)
new_revision.custom_values = temp_custom_values
#add default value for CFs not existing
present_custom_fields = new_revision.custom_values.collect(&:custom_field).uniq
new_revision.project.all_dmsf_custom_fields.each do |cf|
unless present_custom_fields.include?(cf)
new_revision.custom_values << CustomValue.new({:custom_field => cf, :value => cf.default_value})
end
end
unless new_revision.save
file.delete
end
end
return file
end

View File

@ -25,6 +25,8 @@ class DmsfFileRevision < ActiveRecord::Base
belongs_to :deleted_by_user, :class_name => "User", :foreign_key => "deleted_by_user_id"
belongs_to :project
acts_as_customizable
acts_as_event :title => Proc.new {|o| "#{l(:label_dmsf_updated)}: #{o.file.dmsf_path_str}"},
:url => Proc.new {|o| {:controller => 'dmsf_files', :action => 'show', :id => o.file}},
:datetime => Proc.new {|o| o.updated_at },
@ -77,6 +79,9 @@ class DmsfFileRevision < ActiveRecord::Base
["disk_filename = :filename", {:filename => self.disk_filename}])
File.delete(self.disk_file) if dependent.length <= 1 && File.exist?(self.disk_file)
DmsfFileRevisionAccess.find(:all, :conditions => ["dmsf_file_revision_id = ?", self.id]).each {|a| a.destroy}
CustomValue.find(:all, :conditions => "customized_id = " + self.id.to_s).each do |v|
v.destroy
end
self.destroy
else
self.deleted = true
@ -129,6 +134,8 @@ class DmsfFileRevision < ActiveRecord::Base
new_revision.name = self.name
new_revision.folder = self.folder
new_revision.custom_values = self.custom_values.map(&:clone)
return new_revision
end
@ -203,4 +210,19 @@ class DmsfFileRevision < ActiveRecord::Base
end
end
# Overrides Redmine::Acts::Customizable::InstanceMethods#available_custom_fields
def available_custom_fields
search_project = nil
if self.project.present?
search_project = self.project
elsif self.project_id.present?
search_project = Project.find(self.project_id)
end
if search_project
search_project.all_dmsf_custom_fields
else
DmsfFileRevisionCustomField.all
end
end
end

View File

@ -0,0 +1,13 @@
class DmsfFileRevisionCustomField < CustomField
unloadable
has_and_belongs_to_many :projects, :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}", :foreign_key => "custom_field_id"
def initialize(attributes = nil)
super
self.searchable = true
end
def type_name
:DMSF_custom_field
end
end

View File

@ -29,6 +29,8 @@ class DmsfFolder < ActiveRecord::Base
:conditions => { :deleted => false }
belongs_to :user
acts_as_customizable
validates_presence_of :title
validates_uniqueness_of :title, :scope => [:dmsf_folder_id, :project_id]
@ -153,6 +155,19 @@ class DmsfFolder < ActiveRecord::Base
new_folder.description = self.description
new_folder.user = User.current
#copy only cfs present in destination project
temp_custom_values = self.custom_values.select{|cv| new_folder.project.all_dmsf_custom_fields.include?(cv.custom_field)}.map(&:clone)
new_folder.custom_values = temp_custom_values
#add default value for CFs not existing
present_custom_fields = new_folder.custom_values.collect(&:custom_field).uniq
new_folder.project.all_dmsf_custom_fields.each do |cf|
unless present_custom_fields.include?(cf)
new_folder.custom_values << CustomValue.new({:custom_field => cf, :value => cf.default_value})
end
end
return new_folder unless new_folder.save
self.files.each do |f|
@ -166,6 +181,21 @@ class DmsfFolder < ActiveRecord::Base
return new_folder
end
# Overrides Redmine::Acts::Customizable::InstanceMethods#available_custom_fields
def available_custom_fields
search_project = nil
if self.project.present?
search_project = self.project
elsif self.project_id.present?
search_project = Project.find(self.project_id)
end
if search_project
search_project.all_dmsf_custom_fields
else
DmsfFileRevisionCustomField.all
end
end
# To fullfill searchable module expectations
def self.search(tokens, projects=nil, options={})
tokens = [] << tokens unless tokens.is_a?(Array)

View File

@ -0,0 +1,9 @@
<% unless object.nil? %>
<div class="dmsf-customfields">
<% object.show_custom_field_values.each do |custom_value| %>
<div class="dmsf-customfield-<%= custom_value.custom_field.id %> dmsf-customfield customfield">
<%= label_tag("", h(custom_value.custom_field.name) + ":") %> <%= show_value(custom_value) %>
</div>
<% end %>
</div>
<% end %>

View File

@ -36,6 +36,10 @@
<div class="wiki data clear">
<%= f.text_area(:description, :rows => 15, :class => "wiki-edit") %>
</div>
<% values = @folder ? @folder.custom_field_values : @parent ? @parent.custom_field_values : DmsfFolder.new(:project => @project).custom_field_values %>
<% values.each do |value| %>
<p><%= custom_field_tag_with_label(:dmsf_folder, value) %></p>
<% end %>
</div>
<%= submit_tag(create ? l(:submit_create) : l(:submit_save)) %>
<% end %>

View File

@ -19,10 +19,12 @@
<%= render(:partial => 'path', :locals => {:path => path}) %>
</h2>
<div class="wiki">
<%= textilizable(@folder.nil? ? @project.dmsf_description : @folder.description) %>
<div class="dmsf-header">
<div class="wiki">
<%= textilizable(@folder.nil? ? @project.dmsf_description : @folder.description) %>
</div>
<%= render "custom_fields", :object => @folder %>
</div>
<%
form_tag({:action => "entries_operation", :id => @project, :folder_id => @folder}, :method => :post,
:class => "dmfs_entries", :id => "entries_form") do
@ -60,6 +62,7 @@ form_tag({:action => "entries_operation", :id => @project, :folder_id => @folder
{:action => "show", :id => @project, :folder_id => subfolder},
:class => "icon icon-folder") %>
<div class="filename" title="<%= l(:title_number_of_files_in_directory)%>">[<%= subfolder.deep_file_count %>]</div>
<%= render "custom_fields", :object => subfolder %>
</td>
<td class="size" title="<%= l(:title_total_size_of_all_files)%>"><%= number_to_human_size(subfolder.deep_size) %></td>
<td class="modified"><%= subfolder.updated_at.strftime("%Y-%m-%d %H:%M") %></td>
@ -110,6 +113,7 @@ form_tag({:action => "entries_operation", :id => @project, :folder_id => @folder
:title => l(:title_title_version_version_download, :title => h(file.title), :version => file.version),
"data-downloadurl" => "#{file.last_revision.detect_content_type}:#{h(file.name)}:#{file_download_url}") %>
<div class="filename" title="<%= l(:title_filename_for_download)%>"><%= h(file.display_name) %></div>
<%= render "custom_fields", :object => file.last_revision %>
</td>
<td class="size"><%= number_to_human_size(file.last_revision.size) %></td>
<td class="modified">

View File

@ -81,12 +81,19 @@ form_for(:dmsf_file_revision, @revision, :url => {:action => "create_revision",
</p>
</div>
<br style="clear: both"/>
<p>
<%= label_tag("dmsf_file_revision_comment", l(:label_comment) + ":") %>
<div class="data">
<%= f.text_area(:comment, :rows=> "2", :style => "width: 99%;") %>
</div>
</p>
<div class="custom_fields">
<% @revision.custom_field_values.each do |value| %>
<p><%= custom_field_tag_with_label(:dmsf_file_revision, value) %></p>
<% end %>
</div>
<div class="comment">
<p>
<%= label_tag("dmsf_file_revision_comment", l(:label_comment) + ":") %>
<div class="data">
<%= f.text_area(:comment, :rows=> "2", :style => "width: 99%;") %>
</div>
</p>
</div>
<br />
<%= submit_tag(l(:submit_create)) %>
<% end %>

View File

@ -117,6 +117,9 @@
</p>
</div>
<br style="clear: both"/>
<div class="no-ident clear">
<%= render "dmsf/custom_fields", :object => revision %>
</div>
<p class="no-ident clear">
<%= label_tag("", l(:label_comment) + ":") %>
<%= h(revision.comment) %>

View File

@ -15,6 +15,24 @@
<%= submit_tag(l(:submit_save), :title => l(:title_save_preferences)) %>
</div>
<% end %>
<% form_for :project, @project, :url => project_path(@project), :html => {:method=>:post} do %>
<% custom_fields = DmsfFileRevisionCustomField.find(:all) %>
<% unless custom_fields.empty? %>
<fieldset id="project_issue_custom_fields"><legend><%=l(:label_custom_field_plural)%></legend>
<% custom_fields.each do |custom_field| %>
<label class="floating">
<%= check_box_tag 'project[dmsf_file_revision_custom_field_ids][]', custom_field.id, (@project.all_dmsf_custom_fields.include? custom_field), (custom_field.is_for_all? ? {:disabled => "disabled"} : {}) %>
<%= custom_field.name %>
</label>
<% end %>
<%= hidden_field_tag 'project[dmsf_file_revision_custom_field_ids][]', '' %>
<%= hidden_field_tag '_method', 'put' %>
</fieldset>
<% end %>
<%= submit_tag(l(:submit_save), :title => l(:title_save_preferences)) %>
<% end %>
</div>
<% content_for :header_tags do %>

View File

@ -66,6 +66,11 @@ end
</p>
</div>
<br style="clear: both"/>
<% @folder ? folder_exists = true : folder_exists = false %>
<% values = folder_exists ? @folder.custom_field_values : DmsfFileRevision.new(:file => DmsfFile.new(:project => @project)).custom_field_values %>
<% values.each do |value| %>
<p><%= custom_field_tag_with_label("commited_files[#{i}][dmsf_file_revision]", value) %></p>
<% end %>
<p>
<%= label_tag("commited_files[#{i}][comment]", l(:label_comment) + ":") %>
<div class="data">

View File

@ -217,3 +217,19 @@ p.no-ident {
table.access-table tbody td, table.access-table tbody tr:hover td {
border: solid 1px #D7D7D7;
}
/* Custom field */
.dmsf-customfields {
margin: 5px 0 5px 25px;
}
.dmsf-customfields .customfield {
}
.dmsf-customfields .customfield label {
font-weight: bold;
width: 100px;
display: block;
float: left;
}

View File

@ -23,6 +23,12 @@ Dispatcher.to_prepare :redmine_dmsf do
unless ProjectsHelper.included_modules.include?(ProjectTabsExtended)
ProjectsHelper.send(:include, ProjectTabsExtended)
end
unless CustomFieldsHelper.included_modules.include?(CustomFieldsHelper)
CustomFieldsHelper.send(:include, RedmineDmsf::Patches::CustomFieldsHelper)
end
Project.send(:include, RedmineDmsf::Patches::ProjectPatch)
end
Redmine::Plugin.register :redmine_dmsf do

6
lib/redmine_dmsf.rb Normal file
View File

@ -0,0 +1,6 @@
require 'redmine_dmsf/patches/custom_fields_helper'
require 'redmine_dmsf/patches/acts_as_customizable'
require 'redmine_dmsf/patches/project_patch'
module RedmineDmsf
end

View File

@ -0,0 +1,16 @@
module Redmine
module Acts
module Customizable
module InstanceMethods
def available_custom_fields
cf_classname = self.class.name == 'DmsfFolder' ? 'DmsfFileRevision' : self.class.name
CustomField.find(:all, :conditions => "type = '#{cf_classname}CustomField'", :order => 'position')
end
def show_custom_field_values
custom_field_values.delete_if { |x| (!x.id && x.id.blank?) || x.value.blank? }
end
end
end
end
end

View File

@ -0,0 +1,16 @@
module RedmineDmsf
module Patches
module CustomFieldsHelper
def self.included(base)
base.class_eval do
alias_method_chain :custom_fields_tabs, :customer_tab
end
end
def custom_fields_tabs_with_customer_tab
tabs = custom_fields_tabs_without_customer_tab
tabs << {:name => 'DmsfFileRevisionCustomField', :partial => 'custom_fields/index', :label => :DMSF_custom_field}
end
end
end
end

View File

@ -0,0 +1,9 @@
module RedmineDmsf
module Patches
module ProjectPatch
def all_dmsf_custom_fields
@all_dmsf_custom_fields ||= (DmsfFileRevisionCustomField.for_all + dmsf_file_revision_custom_fields).uniq.sort
end
end
end
end